summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
authorKristian Monsen <kristianm@google.com>2011-06-28 21:49:31 +0100
committerKristian Monsen <kristianm@google.com>2011-07-08 17:55:00 +0100
commitddb351dbec246cf1fab5ec20d2d5520909041de1 (patch)
tree158e3fb57bdcac07c7f1e767fde3c70687c9fbb1 /chrome/browser/sync
parent6b92e04f5f151c896e3088e86f70db7081009308 (diff)
downloadexternal_chromium-ddb351dbec246cf1fab5ec20d2d5520909041de1.zip
external_chromium-ddb351dbec246cf1fab5ec20d2d5520909041de1.tar.gz
external_chromium-ddb351dbec246cf1fab5ec20d2d5520909041de1.tar.bz2
Merge Chromium at r12.0.742.93: Initial merge by git
Change-Id: Ic5ee2fec31358bbee305f7e915442377bfa6cda6
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r--chrome/browser/sync/abstract_profile_sync_service_test.h2
-rw-r--r--chrome/browser/sync/backend_migrator.cc176
-rw-r--r--chrome/browser/sync/backend_migrator.h71
-rw-r--r--chrome/browser/sync/backend_migrator_unittest.cc267
-rw-r--r--chrome/browser/sync/engine/all_status.cc13
-rw-r--r--chrome/browser/sync/engine/all_status.h4
-rw-r--r--chrome/browser/sync/engine/apply_updates_command.cc4
-rw-r--r--chrome/browser/sync/engine/apply_updates_command_unittest.cc58
-rw-r--r--chrome/browser/sync/engine/build_and_process_conflict_sets_command.cc15
-rw-r--r--chrome/browser/sync/engine/change_reorder_buffer.h4
-rw-r--r--chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc12
-rw-r--r--chrome/browser/sync/engine/download_updates_command.cc9
-rw-r--r--chrome/browser/sync/engine/http_post_provider_factory.h34
-rw-r--r--chrome/browser/sync/engine/http_post_provider_interface.h66
-rw-r--r--chrome/browser/sync/engine/idle_query_linux.cc29
-rw-r--r--chrome/browser/sync/engine/idle_query_linux.h4
-rw-r--r--chrome/browser/sync/engine/mock_model_safe_workers.cc15
-rw-r--r--chrome/browser/sync/engine/mock_model_safe_workers.h4
-rw-r--r--chrome/browser/sync/engine/model_safe_worker.cc2
-rw-r--r--chrome/browser/sync/engine/model_safe_worker.h4
-rw-r--r--chrome/browser/sync/engine/net/server_connection_manager.cc21
-rw-r--r--chrome/browser/sync/engine/net/server_connection_manager.h24
-rw-r--r--chrome/browser/sync/engine/net/syncapi_server_connection_manager.cc17
-rw-r--r--chrome/browser/sync/engine/net/syncapi_server_connection_manager.h4
-rw-r--r--chrome/browser/sync/engine/nudge_source.h5
-rw-r--r--chrome/browser/sync/engine/syncapi.cc880
-rw-r--r--chrome/browser/sync/engine/syncapi.h125
-rw-r--r--chrome/browser/sync/engine/syncapi_unittest.cc192
-rw-r--r--chrome/browser/sync/engine/syncer.cc11
-rw-r--r--chrome/browser/sync/engine/syncer.h4
-rw-r--r--chrome/browser/sync/engine/syncer_end_command.cc14
-rw-r--r--chrome/browser/sync/engine/syncer_proto_util.cc17
-rw-r--r--chrome/browser/sync/engine/syncer_proto_util.h6
-rw-r--r--chrome/browser/sync/engine/syncer_thread.cc1473
-rw-r--r--chrome/browser/sync/engine/syncer_thread.h578
-rw-r--r--chrome/browser/sync/engine/syncer_thread2.cc589
-rw-r--r--chrome/browser/sync/engine/syncer_thread2.h227
-rw-r--r--chrome/browser/sync/engine/syncer_thread2_unittest.cc363
-rw-r--r--chrome/browser/sync/engine/syncer_thread2_whitebox_unittest.cc231
-rw-r--r--chrome/browser/sync/engine/syncer_thread_adapter.cc142
-rw-r--r--chrome/browser/sync/engine/syncer_thread_adapter.h50
-rw-r--r--chrome/browser/sync/engine/syncer_thread_unittest.cc1244
-rw-r--r--chrome/browser/sync/engine/syncer_types.cc15
-rw-r--r--chrome/browser/sync/engine/syncer_types.h33
-rw-r--r--chrome/browser/sync/engine/syncer_unittest.cc8
-rw-r--r--chrome/browser/sync/engine/syncer_util.cc24
-rw-r--r--chrome/browser/sync/engine/syncer_util.h8
-rw-r--r--chrome/browser/sync/engine/update_applicator.cc15
-rw-r--r--chrome/browser/sync/glue/app_data_type_controller.cc105
-rw-r--r--chrome/browser/sync/glue/app_data_type_controller.h55
-rw-r--r--chrome/browser/sync/glue/autofill_change_processor.cc32
-rw-r--r--chrome/browser/sync/glue/autofill_change_processor.h6
-rw-r--r--chrome/browser/sync/glue/autofill_data_type_controller.cc50
-rw-r--r--chrome/browser/sync/glue/autofill_data_type_controller.h15
-rw-r--r--chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc30
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator.cc68
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator.h29
-rw-r--r--chrome/browser/sync/glue/autofill_profile_change_processor.cc69
-rw-r--r--chrome/browser/sync/glue/autofill_profile_change_processor.h14
-rw-r--r--chrome/browser/sync/glue/autofill_profile_data_type_controller.cc4
-rw-r--r--chrome/browser/sync/glue/autofill_profile_data_type_controller.h4
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator.cc77
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator.h35
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc35
-rw-r--r--chrome/browser/sync/glue/bookmark_change_processor.cc87
-rw-r--r--chrome/browser/sync/glue/bookmark_change_processor.h11
-rw-r--r--chrome/browser/sync/glue/bookmark_data_type_controller.cc148
-rw-r--r--chrome/browser/sync/glue/bookmark_data_type_controller.h64
-rw-r--r--chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc39
-rw-r--r--chrome/browser/sync/glue/bookmark_model_associator.cc98
-rw-r--r--chrome/browser/sync/glue/bookmark_model_associator.h23
-rw-r--r--chrome/browser/sync/glue/data_type_controller.h19
-rw-r--r--chrome/browser/sync/glue/data_type_controller_mock.h8
-rw-r--r--chrome/browser/sync/glue/data_type_manager.cc15
-rw-r--r--chrome/browser/sync/glue/data_type_manager.h37
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.cc394
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl.h49
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl2.cc337
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl2.h70
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl2_unittest.cc114
-rw-r--r--chrome/browser/sync/glue/data_type_manager_impl_unittest.cc667
-rw-r--r--chrome/browser/sync/glue/data_type_manager_mock.cc4
-rw-r--r--chrome/browser/sync/glue/data_type_manager_mock.h11
-rw-r--r--chrome/browser/sync/glue/database_model_worker_unittest.cc4
-rw-r--r--chrome/browser/sync/glue/do_optimistic_refresh_task.h4
-rw-r--r--chrome/browser/sync/glue/extension_change_processor.cc68
-rw-r--r--chrome/browser/sync/glue/extension_change_processor.h18
-rw-r--r--chrome/browser/sync/glue/extension_data_type_controller.cc107
-rw-r--r--chrome/browser/sync/glue/extension_data_type_controller.h55
-rw-r--r--chrome/browser/sync/glue/extension_data_type_controller_unittest.cc25
-rw-r--r--chrome/browser/sync/glue/extension_model_associator.cc32
-rw-r--r--chrome/browser/sync/glue/extension_model_associator.h17
-rw-r--r--chrome/browser/sync/glue/extension_sync.cc200
-rw-r--r--chrome/browser/sync/glue/extension_sync.h33
-rw-r--r--chrome/browser/sync/glue/extension_util.cc94
-rw-r--r--chrome/browser/sync/glue/extension_util.h32
-rw-r--r--chrome/browser/sync/glue/extension_util_unittest.cc52
-rw-r--r--chrome/browser/sync/glue/foreign_session_tracker.h4
-rw-r--r--chrome/browser/sync/glue/frontend_data_type_controller.cc172
-rw-r--r--chrome/browser/sync/glue/frontend_data_type_controller.h113
-rw-r--r--chrome/browser/sync/glue/frontend_data_type_controller_mock.cc13
-rw-r--r--chrome/browser/sync/glue/frontend_data_type_controller_mock.h47
-rw-r--r--chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc242
-rw-r--r--chrome/browser/sync/glue/history_model_worker.cc4
-rw-r--r--chrome/browser/sync/glue/history_model_worker.h4
-rw-r--r--chrome/browser/sync/glue/http_bridge.cc132
-rw-r--r--chrome/browser/sync/glue/http_bridge.h86
-rw-r--r--chrome/browser/sync/glue/http_bridge_unittest.cc57
-rw-r--r--chrome/browser/sync/glue/model_associator.h8
-rw-r--r--chrome/browser/sync/glue/model_associator_mock.h3
-rw-r--r--chrome/browser/sync/glue/password_change_processor.cc37
-rw-r--r--chrome/browser/sync/glue/password_change_processor.h31
-rw-r--r--chrome/browser/sync/glue/password_data_type_controller.cc109
-rw-r--r--chrome/browser/sync/glue/password_data_type_controller.h21
-rw-r--r--chrome/browser/sync/glue/password_model_associator.cc147
-rw-r--r--chrome/browser/sync/glue/password_model_associator.h5
-rw-r--r--chrome/browser/sync/glue/password_model_worker.cc4
-rw-r--r--chrome/browser/sync/glue/password_model_worker.h4
-rw-r--r--chrome/browser/sync/glue/preference_change_processor.cc10
-rw-r--r--chrome/browser/sync/glue/preference_change_processor.h4
-rw-r--r--chrome/browser/sync/glue/preference_data_type_controller.cc117
-rw-r--r--chrome/browser/sync/glue/preference_data_type_controller.h55
-rw-r--r--chrome/browser/sync/glue/preference_data_type_controller_unittest.cc28
-rw-r--r--chrome/browser/sync/glue/preference_model_associator.cc21
-rw-r--r--chrome/browser/sync/glue/preference_model_associator.h5
-rw-r--r--chrome/browser/sync/glue/preference_model_associator_unittest.cc136
-rw-r--r--chrome/browser/sync/glue/session_change_processor.cc24
-rw-r--r--chrome/browser/sync/glue/session_change_processor.h8
-rw-r--r--chrome/browser/sync/glue/session_data_type_controller.cc116
-rw-r--r--chrome/browser/sync/glue/session_data_type_controller.h62
-rw-r--r--chrome/browser/sync/glue/session_model_associator.cc37
-rw-r--r--chrome/browser/sync/glue/session_model_associator.h11
-rw-r--r--chrome/browser/sync/glue/session_model_associator_unittest.cc6
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.cc346
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h146
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_mock.cc9
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_mock.h7
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_unittest.cc194
-rw-r--r--chrome/browser/sync/glue/synchronized_preferences.h25
-rw-r--r--chrome/browser/sync/glue/theme_change_processor.cc104
-rw-r--r--chrome/browser/sync/glue/theme_change_processor.h16
-rw-r--r--chrome/browser/sync/glue/theme_data_type_controller.cc110
-rw-r--r--chrome/browser/sync/glue/theme_data_type_controller.h56
-rw-r--r--chrome/browser/sync/glue/theme_data_type_controller_unittest.cc25
-rw-r--r--chrome/browser/sync/glue/theme_model_associator.cc12
-rw-r--r--chrome/browser/sync/glue/theme_model_associator.h3
-rw-r--r--chrome/browser/sync/glue/theme_util.cc49
-rw-r--r--chrome/browser/sync/glue/theme_util_unittest.cc44
-rw-r--r--chrome/browser/sync/glue/typed_url_change_processor.cc6
-rw-r--r--chrome/browser/sync/glue/typed_url_change_processor.h10
-rw-r--r--chrome/browser/sync/glue/typed_url_data_type_controller.cc106
-rw-r--r--chrome/browser/sync/glue/typed_url_data_type_controller.h29
-rw-r--r--chrome/browser/sync/glue/typed_url_model_associator.cc13
-rw-r--r--chrome/browser/sync/glue/typed_url_model_associator.h4
-rw-r--r--chrome/browser/sync/glue/ui_model_worker.cc17
-rw-r--r--chrome/browser/sync/glue/ui_model_worker.h7
-rw-r--r--chrome/browser/sync/glue/ui_model_worker_unittest.cc28
-rw-r--r--chrome/browser/sync/js_arg_list.cc2
-rw-r--r--chrome/browser/sync/js_arg_list.h2
-rw-r--r--chrome/browser/sync/js_arg_list_unittest.cc2
-rw-r--r--chrome/browser/sync/js_sync_manager_observer.cc17
-rw-r--r--chrome/browser/sync/js_sync_manager_observer.h3
-rw-r--r--chrome/browser/sync/js_sync_manager_observer_unittest.cc53
-rw-r--r--chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc7
-rw-r--r--chrome/browser/sync/notifier/cache_invalidation_packet_handler.h6
-rw-r--r--chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc4
-rw-r--r--chrome/browser/sync/notifier/chrome_invalidation_client.cc93
-rw-r--r--chrome/browser/sync/notifier/chrome_invalidation_client.h52
-rw-r--r--chrome/browser/sync/notifier/chrome_invalidation_client_unittest.cc191
-rw-r--r--chrome/browser/sync/notifier/chrome_system_resources.h4
-rw-r--r--chrome/browser/sync/notifier/invalidation_notifier.cc133
-rw-r--r--chrome/browser/sync/notifier/invalidation_notifier.h104
-rw-r--r--chrome/browser/sync/notifier/invalidation_notifier_unittest.cc88
-rw-r--r--chrome/browser/sync/notifier/mock_sync_notifier_observer.cc12
-rw-r--r--chrome/browser/sync/notifier/mock_sync_notifier_observer.h29
-rw-r--r--chrome/browser/sync/notifier/non_blocking_invalidation_notifier.cc216
-rw-r--r--chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h56
-rw-r--r--chrome/browser/sync/notifier/non_blocking_invalidation_notifier_unittest.cc75
-rw-r--r--chrome/browser/sync/notifier/p2p_notifier.cc150
-rw-r--r--chrome/browser/sync/notifier/p2p_notifier.h76
-rw-r--r--chrome/browser/sync/notifier/registration_manager.cc243
-rw-r--r--chrome/browser/sync/notifier/registration_manager.h136
-rw-r--r--chrome/browser/sync/notifier/registration_manager_unittest.cc444
-rw-r--r--chrome/browser/sync/notifier/server_notifier_thread.cc174
-rw-r--r--chrome/browser/sync/notifier/server_notifier_thread.h106
-rw-r--r--chrome/browser/sync/notifier/server_notifier_thread_unittest.cc160
-rw-r--r--chrome/browser/sync/notifier/sync_notifier.h61
-rw-r--r--chrome/browser/sync/notifier/sync_notifier_factory.cc111
-rw-r--r--chrome/browser/sync/notifier/sync_notifier_factory.h43
-rw-r--r--chrome/browser/sync/notifier/sync_notifier_observer.h31
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl.cc34
-rw-r--r--chrome/browser/sync/profile_sync_factory_impl_unittest.cc12
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.h2
-rw-r--r--chrome/browser/sync/profile_sync_service.cc273
-rw-r--r--chrome/browser/sync/profile_sync_service.h99
-rw-r--r--chrome/browser/sync/profile_sync_service_autofill_unittest.cc242
-rw-r--r--chrome/browser/sync/profile_sync_service_bookmark_unittest.cc1362
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.cc152
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.h31
-rw-r--r--chrome/browser/sync/profile_sync_service_mock.cc4
-rw-r--r--chrome/browser/sync/profile_sync_service_mock.h2
-rw-r--r--chrome/browser/sync/profile_sync_service_password_unittest.cc149
-rw-r--r--chrome/browser/sync/profile_sync_service_preference_unittest.cc51
-rw-r--r--chrome/browser/sync/profile_sync_service_session_unittest.cc31
-rw-r--r--chrome/browser/sync/profile_sync_service_startup_unittest.cc59
-rw-r--r--chrome/browser/sync/profile_sync_service_typed_url_unittest.cc13
-rw-r--r--chrome/browser/sync/profile_sync_service_unittest.cc1477
-rw-r--r--chrome/browser/sync/profile_sync_test_util.h12
-rw-r--r--chrome/browser/sync/protocol/autofill_specifics.proto4
-rw-r--r--chrome/browser/sync/protocol/proto_value_conversions_unittest.cc2
-rw-r--r--chrome/browser/sync/protocol/sync.proto64
-rw-r--r--chrome/browser/sync/protocol/sync_proto.gyp40
-rw-r--r--chrome/browser/sync/resources/configure.html349
-rw-r--r--chrome/browser/sync/resources/firstpassphrase.html49
-rw-r--r--chrome/browser/sync/resources/gaia_login.css2
-rw-r--r--chrome/browser/sync/resources/gaia_login.html25
-rw-r--r--chrome/browser/sync/resources/gaia_login.js4
-rw-r--r--chrome/browser/sync/resources/passphrase.html10
-rw-r--r--chrome/browser/sync/sessions/session_state.cc60
-rw-r--r--chrome/browser/sync/sessions/session_state.h38
-rw-r--r--chrome/browser/sync/sessions/session_state_unittest.cc18
-rw-r--r--chrome/browser/sync/sessions/status_controller.cc5
-rw-r--r--chrome/browser/sync/sessions/status_controller.h1
-rw-r--r--chrome/browser/sync/sessions/sync_session.cc17
-rw-r--r--chrome/browser/sync/sessions/sync_session.h6
-rw-r--r--chrome/browser/sync/sessions/sync_session_context.cc7
-rw-r--r--chrome/browser/sync/sessions/sync_session_context.h11
-rw-r--r--chrome/browser/sync/sessions/sync_session_unittest.cc33
-rw-r--r--chrome/browser/sync/signin_manager.cc4
-rw-r--r--chrome/browser/sync/signin_manager.h4
-rw-r--r--chrome/browser/sync/sync_constants.cc17
-rw-r--r--chrome/browser/sync/sync_constants.h21
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc782
-rw-r--r--chrome/browser/sync/sync_setup_flow.h110
-rw-r--r--chrome/browser/sync/sync_setup_flow_handler.h35
-rw-r--r--chrome/browser/sync/sync_setup_wizard.cc293
-rw-r--r--chrome/browser/sync/sync_setup_wizard.h11
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc68
-rw-r--r--chrome/browser/sync/sync_ui_util.cc64
-rw-r--r--chrome/browser/sync/sync_ui_util.h2
-rw-r--r--chrome/browser/sync/sync_ui_util_mac_unittest.mm4
-rw-r--r--chrome/browser/sync/syncable/autofill_migration.h7
-rw-r--r--chrome/browser/sync/syncable/directory_backing_store.cc22
-rw-r--r--chrome/browser/sync/syncable/directory_backing_store_unittest.cc7
-rw-r--r--chrome/browser/sync/syncable/directory_change_listener.h42
-rw-r--r--chrome/browser/sync/syncable/directory_manager.cc4
-rw-r--r--chrome/browser/sync/syncable/directory_manager.h14
-rw-r--r--chrome/browser/sync/syncable/model_type.cc14
-rw-r--r--chrome/browser/sync/syncable/model_type.h8
-rw-r--r--chrome/browser/sync/syncable/model_type_payload_map.cc61
-rw-r--r--chrome/browser/sync/syncable/model_type_payload_map.h50
-rw-r--r--chrome/browser/sync/syncable/model_type_payload_map_unittest.cc38
-rw-r--r--chrome/browser/sync/syncable/model_type_unittest.cc26
-rw-r--r--chrome/browser/sync/syncable/syncable.cc248
-rw-r--r--chrome/browser/sync/syncable/syncable.h99
-rw-r--r--chrome/browser/sync/syncable/syncable_enum_conversions.cc153
-rw-r--r--chrome/browser/sync/syncable/syncable_enum_conversions.h43
-rw-r--r--chrome/browser/sync/syncable/syncable_enum_conversions_unittest.cc80
-rw-r--r--chrome/browser/sync/syncable/syncable_id.cc7
-rw-r--r--chrome/browser/sync/syncable/syncable_id.h13
-rw-r--r--chrome/browser/sync/syncable/syncable_id_unittest.cc27
-rw-r--r--chrome/browser/sync/syncable/syncable_unittest.cc62
-rw-r--r--chrome/browser/sync/test_profile_sync_service.cc54
-rw-r--r--chrome/browser/sync/test_profile_sync_service.h20
-rw-r--r--chrome/browser/sync/token_migrator.cc53
-rw-r--r--chrome/browser/sync/token_migrator.h56
-rw-r--r--chrome/browser/sync/tools/sync_listen_notifications.cc344
-rw-r--r--chrome/browser/sync/tools/sync_tools.gyp10
-rw-r--r--chrome/browser/sync/util/channel.h145
-rw-r--r--chrome/browser/sync/util/channel_unittest.cc32
-rw-r--r--chrome/browser/sync/util/cryptographer.cc7
-rw-r--r--chrome/browser/sync/util/cryptographer.h12
-rw-r--r--chrome/browser/sync/util/cryptographer_unittest.cc4
-rw-r--r--chrome/browser/sync/util/extensions_activity_monitor.cc4
-rw-r--r--chrome/browser/sync/util/extensions_activity_monitor.h6
-rw-r--r--chrome/browser/sync/util/extensions_activity_monitor_unittest.cc12
-rw-r--r--chrome/browser/sync/util/nigori.cc12
-rw-r--r--chrome/browser/sync/util/nigori.h12
-rw-r--r--chrome/browser/sync/util/nigori_unittest.cc4
-rw-r--r--chrome/browser/sync/util/user_settings_unittest.cc198
280 files changed, 11980 insertions, 13005 deletions
diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.h b/chrome/browser/sync/abstract_profile_sync_service_test.h
index 8f8cbbe..2e681f1 100644
--- a/chrome/browser/sync/abstract_profile_sync_service_test.h
+++ b/chrome/browser/sync/abstract_profile_sync_service_test.h
@@ -8,8 +8,8 @@
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
diff --git a/chrome/browser/sync/backend_migrator.cc b/chrome/browser/sync/backend_migrator.cc
new file mode 100644
index 0000000..481d455
--- /dev/null
+++ b/chrome/browser/sync/backend_migrator.cc
@@ -0,0 +1,176 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/backend_migrator.h"
+
+#include <algorithm>
+
+#include "base/string_number_conversions.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/glue/data_type_manager.h"
+#include "chrome/browser/sync/sessions/session_state.h"
+#include "content/browser/browser_thread.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
+
+using syncable::ModelTypeSet;
+
+namespace browser_sync {
+
+using sessions::SyncSessionSnapshot;
+
+BackendMigrator::BackendMigrator(ProfileSyncService* service,
+ DataTypeManager* manager)
+ : state_(IDLE), service_(service), manager_(manager),
+ restart_migration_(false),
+ method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
+ registrar_.Add(this, NotificationType::SYNC_CONFIGURE_DONE,
+ Source<DataTypeManager>(manager_));
+ service_->AddObserver(this);
+}
+
+BackendMigrator::~BackendMigrator() {
+ service_->RemoveObserver(this);
+}
+
+bool BackendMigrator::HasStartedMigrating() const {
+ return state_ >= DISABLING_TYPES;
+}
+
+void BackendMigrator::MigrateTypes(const syncable::ModelTypeSet& types) {
+ {
+ ModelTypeSet temp;
+ std::set_union(to_migrate_.begin(), to_migrate_.end(),
+ types.begin(), types.end(),
+ std::inserter(temp, temp.end()));
+ to_migrate_ = temp;
+ }
+
+ if (HasStartedMigrating()) {
+ VLOG(1) << "BackendMigrator::MigrateTypes: STARTED_MIGRATING early-out.";
+ restart_migration_ = true;
+ return;
+ }
+
+ if (manager_->state() != DataTypeManager::CONFIGURED) {
+ VLOG(1) << "BackendMigrator::MigrateTypes: manager CONFIGURED early-out.";
+ state_ = WAITING_TO_START;
+ return;
+ }
+
+ // We'll now disable any running types that need to be migrated.
+ state_ = DISABLING_TYPES;
+ ModelTypeSet full_set;
+ service_->GetPreferredDataTypes(&full_set);
+ ModelTypeSet difference;
+ std::set_difference(full_set.begin(), full_set.end(),
+ to_migrate_.begin(), to_migrate_.end(),
+ std::inserter(difference, difference.end()));
+ VLOG(1) << "BackendMigrator disabling types; calling Configure.";
+ manager_->Configure(difference);
+}
+
+void BackendMigrator::OnStateChanged() {
+ if (restart_migration_ == true) {
+ VLOG(1) << "BackendMigrator restarting migration in OnStateChanged.";
+ state_ = WAITING_TO_START;
+ restart_migration_ = false;
+ MigrateTypes(to_migrate_);
+ return;
+ }
+
+ if (state_ != WAITING_FOR_PURGE)
+ return;
+
+ size_t num_empty_migrated_markers = 0;
+ const SyncSessionSnapshot* snap = service_->GetLastSessionSnapshot();
+ for (ModelTypeSet::const_iterator it = to_migrate_.begin();
+ it != to_migrate_.end(); ++it) {
+ if (snap->download_progress_markers[*it].empty())
+ num_empty_migrated_markers++;
+ }
+
+ if (num_empty_migrated_markers < to_migrate_.size())
+ return;
+
+ state_ = REENABLING_TYPES;
+ ModelTypeSet full_set;
+ service_->GetPreferredDataTypes(&full_set);
+ VLOG(1) << "BackendMigrator re-enabling types.";
+ // Don't use |to_migrate_| for the re-enabling because the user may have
+ // chosen to disable types during the migration.
+ manager_->Configure(full_set);
+}
+
+void BackendMigrator::Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details) {
+ DCHECK_EQ(NotificationType::SYNC_CONFIGURE_DONE, type.value);
+ if (state_ == IDLE)
+ return;
+
+ DataTypeManager::ConfigureResultWithErrorLocation* result =
+ Details<DataTypeManager::ConfigureResultWithErrorLocation>(
+ details).ptr();
+
+ ModelTypeSet intersection;
+ std::set_intersection(result->requested_types.begin(),
+ result->requested_types.end(), to_migrate_.begin(), to_migrate_.end(),
+ std::inserter(intersection, intersection.end()));
+
+ // The intersection check is to determine if our disable request was
+ // interrupted by a user changing preferred types. May still need to purge.
+ // It's pretty wild if we're in WAITING_FOR_PURGE here, because it would mean
+ // that after our disable-config finished but before the purge, another config
+ // was posted externally _and completed_, which means somehow the nudge to
+ // purge was dropped, yet nudges are reliable.
+ if (state_ == WAITING_TO_START || state_ == WAITING_FOR_PURGE ||
+ (state_ == DISABLING_TYPES && !intersection.empty())) {
+ state_ = WAITING_TO_START;
+ restart_migration_ = false;
+ VLOG(1) << "BackendMigrator::Observe posting MigrateTypes.";
+ if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ method_factory_.NewRunnableMethod(&BackendMigrator::MigrateTypes,
+ to_migrate_))) {
+ // Unittests need this.
+ // TODO(tim): Clean this up.
+ MigrateTypes(to_migrate_);
+ }
+ return;
+ }
+
+ if (result->result != DataTypeManager::OK) {
+ // If this fails, and we're disabling types, a type may or may not be
+ // disabled until the user restarts the browser. If this wasn't an abort,
+ // any failure will be reported as an unrecoverable error to the UI. If it
+ // was an abort, then typically things are shutting down anyway. There isn't
+ // much we can do in any case besides wait until a restart to try again.
+ // The server will send down MIGRATION_DONE again for types needing
+ // migration as the type will still be enabled on restart.
+ LOG(WARNING) << "Unable to migrate, configuration failed!";
+ state_ = IDLE;
+ to_migrate_.clear();
+ return;
+ }
+
+ if (state_ == DISABLING_TYPES) {
+ state_ = WAITING_FOR_PURGE;
+ VLOG(1) << "BackendMigrator waiting for purge.";
+ } else if (state_ == REENABLING_TYPES) {
+ // We're done!
+ state_ = IDLE;
+
+ std::stringstream ss;
+ std::copy(to_migrate_.begin(), to_migrate_.end(),
+ std::ostream_iterator<syncable::ModelType>(ss, ","));
+ VLOG(1) << "BackendMigrator: Migration complete for: " << ss.str();
+ to_migrate_.clear();
+ }
+}
+
+BackendMigrator::State BackendMigrator::state() const {
+ return state_;
+}
+
+}; // namespace browser_sync
diff --git a/chrome/browser/sync/backend_migrator.h b/chrome/browser/sync/backend_migrator.h
new file mode 100644
index 0000000..2f2a175
--- /dev/null
+++ b/chrome/browser/sync/backend_migrator.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_BACKEND_MIGRATOR_H_
+#define CHROME_BROWSER_SYNC_BACKEND_MIGRATOR_H_
+
+#include "base/task.h"
+#include "chrome/browser/sync/profile_sync_service_observer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+
+class ProfileSyncService;
+
+namespace browser_sync {
+
+class DataTypeManager;
+
+// A class to perform migration of a datatype pursuant to the 'MIGRATION_DONE'
+// code in the sync protocol definition (protocol/sync.proto).
+class BackendMigrator : public NotificationObserver,
+ public ProfileSyncServiceObserver {
+ public:
+ enum State {
+ IDLE,
+ WAITING_TO_START, // Waiting for previous configuration to finish.
+ DISABLING_TYPES, // Exit criteria: SYNC_CONFIGURE_DONE for enabled
+ // types _excluding_ |to_migrate_|.
+ WAITING_FOR_PURGE, // Exit criteria: SyncCycleEnded for enabled types
+ // excluding |to_migrate|
+ REENABLING_TYPES, // Exit criteria: SYNC_CONFIGURE_DONE for enabled
+ // types.
+ };
+
+ BackendMigrator(ProfileSyncService* service, DataTypeManager* manager);
+ virtual ~BackendMigrator();
+
+ // Starts a sequence of events that will disable and reenable |types|.
+ void MigrateTypes(const syncable::ModelTypeSet& types);
+
+ // ProfileSyncServiceObserver implementation.
+ virtual void OnStateChanged();
+
+ // NotificationObserver implementation.
+ virtual void Observe(NotificationType type,
+ const NotificationSource& source,
+ const NotificationDetails& details);
+
+ State state() const;
+
+ private:
+ bool HasStartedMigrating() const;
+
+ State state_;
+ ProfileSyncService* service_;
+ DataTypeManager* manager_;
+ NotificationRegistrar registrar_;
+
+ syncable::ModelTypeSet to_migrate_;
+ bool restart_migration_;
+
+ // We use this to gracefully re-start migrations.
+ ScopedRunnableMethodFactory<BackendMigrator> method_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BackendMigrator);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_BACKEND_MIGRATOR_H_
diff --git a/chrome/browser/sync/backend_migrator_unittest.cc b/chrome/browser/sync/backend_migrator_unittest.cc
new file mode 100644
index 0000000..39b2ef0
--- /dev/null
+++ b/chrome/browser/sync/backend_migrator_unittest.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/backend_migrator.h"
+
+#include "chrome/browser/sync/glue/data_type_manager_mock.h"
+#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/browser/sync/sessions/session_state.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Mock;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+
+namespace browser_sync {
+
+using sessions::ErrorCounters;
+using sessions::SyncerStatus;
+using sessions::SyncSessionSnapshot;
+
+class BackendMigratorTest : public testing::Test {
+ public:
+ BackendMigratorTest() { }
+ virtual ~BackendMigratorTest() { }
+
+ virtual void SetUp() {
+ Mock::VerifyAndClear(manager());
+ Mock::VerifyAndClear(&service_);
+ preferred_types_.insert(syncable::BOOKMARKS);
+ preferred_types_.insert(syncable::PREFERENCES);
+ preferred_types_.insert(syncable::AUTOFILL);
+
+ ON_CALL(service_, GetPreferredDataTypes(_)).
+ WillByDefault(SetArgumentPointee<0>(preferred_types_));
+ }
+
+ void ReturnEmptyProgressMarkersInSnapshot() {
+ ReturnNonEmptyProgressMarkersInSnapshot(syncable::ModelTypeSet());
+ }
+
+ void ReturnNonEmptyProgressMarkersInSnapshot(
+ const syncable::ModelTypeSet& for_types) {
+ std::string download_progress_markers[syncable::MODEL_TYPE_COUNT];
+ for (syncable::ModelTypeSet::const_iterator it = for_types.begin();
+ it != for_types.end(); ++it) {
+ download_progress_markers[*it] = "foobar";
+ }
+
+ snap_.reset(new SyncSessionSnapshot(SyncerStatus(), ErrorCounters(),
+ 0, false, syncable::ModelTypeBitSet(), download_progress_markers,
+ false, false, 0, 0, false, sessions::SyncSourceInfo()));
+ EXPECT_CALL(service_, GetLastSessionSnapshot())
+ .WillOnce(Return(snap_.get()));
+ }
+
+ void SendConfigureDone(DataTypeManager::ConfigureResult result,
+ const syncable::ModelTypeSet& types) {
+ DataTypeManager::ConfigureResultWithErrorLocation result_with_location(
+ result, FROM_HERE, types);
+ NotificationService::current()->Notify(
+ NotificationType::SYNC_CONFIGURE_DONE,
+ Source<DataTypeManager>(&manager_),
+ Details<DataTypeManager::ConfigureResultWithErrorLocation>(
+ &result_with_location));
+ }
+
+ ProfileSyncService* service() { return &service_; }
+ DataTypeManagerMock* manager() { return &manager_; }
+ const syncable::ModelTypeSet& preferred_types() { return preferred_types_; }
+ void RemovePreferredType(syncable::ModelType type) {
+ preferred_types_.erase(type);
+ Mock::VerifyAndClear(&service_);
+ ON_CALL(service_, GetPreferredDataTypes(_)).
+ WillByDefault(SetArgumentPointee<0>(preferred_types_));
+ }
+ private:
+ scoped_ptr<SyncSessionSnapshot> snap_;
+ syncable::ModelTypeSet preferred_types_;
+ NiceMock<ProfileSyncServiceMock> service_;
+ NiceMock<DataTypeManagerMock> manager_;
+};
+
+// Test that in the normal case a migration does transition through each state
+// and wind up back in IDLE.
+TEST_F(BackendMigratorTest, Sanity) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate, difference;
+ to_migrate.insert(syncable::PREFERENCES);
+ difference.insert(syncable::AUTOFILL);
+ difference.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+
+ migrator.MigrateTypes(to_migrate);
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+
+ SendConfigureDone(DataTypeManager::OK, difference);
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ ReturnEmptyProgressMarkersInSnapshot();
+ EXPECT_CALL(*manager(), Configure(preferred_types()));
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
+
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+ EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
+}
+
+// Test that the migrator waits for the data type manager to be idle before
+// starting a migration.
+TEST_F(BackendMigratorTest, WaitToStart) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ to_migrate.insert(syncable::PREFERENCES);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURING));
+ EXPECT_CALL(*manager(), Configure(_)).Times(0);
+ migrator.MigrateTypes(to_migrate);
+ EXPECT_EQ(BackendMigrator::WAITING_TO_START, migrator.state());
+
+ Mock::VerifyAndClearExpectations(manager());
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+ SendConfigureDone(DataTypeManager::OK, syncable::ModelTypeSet());
+
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+}
+
+// Test that the migrator can cope with a migration request while a migration
+// is in progress.
+TEST_F(BackendMigratorTest, RestartMigration) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate1, to_migrate2, bookmarks;
+ to_migrate1.insert(syncable::PREFERENCES);
+ to_migrate2.insert(syncable::AUTOFILL);
+ bookmarks.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_)).Times(1);
+ migrator.MigrateTypes(to_migrate1);
+
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+ migrator.MigrateTypes(to_migrate2);
+
+ syncable::ModelTypeSet difference1;
+ std::set_difference(preferred_types().begin(), preferred_types().end(),
+ to_migrate1.begin(), to_migrate1.end(),
+ std::inserter(difference1, difference1.end()));
+
+ Mock::VerifyAndClearExpectations(manager());
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(bookmarks));
+ SendConfigureDone(DataTypeManager::OK, difference1);
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+
+ SendConfigureDone(DataTypeManager::OK, bookmarks);
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+}
+
+// Test that an external invocation of Configure(...) during a migration results
+// in a migration reattempt.
+TEST_F(BackendMigratorTest, InterruptedWhileDisablingTypes) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ syncable::ModelTypeSet difference;
+ to_migrate.insert(syncable::PREFERENCES);
+ difference.insert(syncable::AUTOFILL);
+ difference.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(difference));
+ migrator.MigrateTypes(to_migrate);
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+
+ Mock::VerifyAndClearExpectations(manager());
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(difference));
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+
+ EXPECT_EQ(BackendMigrator::DISABLING_TYPES, migrator.state());
+}
+
+// Test that spurious OnStateChanged events don't confuse the migrator while
+// it's waiting for disabled types to have been purged from the sync db.
+TEST_F(BackendMigratorTest, WaitingForPurge) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate, difference;
+ to_migrate.insert(syncable::PREFERENCES);
+ to_migrate.insert(syncable::AUTOFILL);
+ difference.insert(syncable::BOOKMARKS);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+ migrator.MigrateTypes(to_migrate);
+ SendConfigureDone(DataTypeManager::OK, difference);
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ ReturnNonEmptyProgressMarkersInSnapshot(to_migrate);
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ syncable::ModelTypeSet prefs;
+ prefs.insert(syncable::PREFERENCES);
+ ReturnNonEmptyProgressMarkersInSnapshot(prefs);
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ syncable::ModelTypeSet bookmarks;
+ bookmarks.insert(syncable::BOOKMARKS);
+ ReturnNonEmptyProgressMarkersInSnapshot(bookmarks);
+ EXPECT_CALL(*manager(), Configure(preferred_types()));
+ migrator.OnStateChanged();
+ EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
+}
+
+TEST_F(BackendMigratorTest, MigratedTypeDisabledByUserDuringMigration) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ to_migrate.insert(syncable::PREFERENCES);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_));
+ migrator.MigrateTypes(to_migrate);
+
+ RemovePreferredType(syncable::PREFERENCES);
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+ EXPECT_EQ(BackendMigrator::WAITING_FOR_PURGE, migrator.state());
+
+ Mock::VerifyAndClearExpectations(manager());
+ ReturnEmptyProgressMarkersInSnapshot();
+ EXPECT_CALL(*manager(), Configure(preferred_types()));
+ migrator.OnStateChanged();
+
+ EXPECT_EQ(BackendMigrator::REENABLING_TYPES, migrator.state());
+ SendConfigureDone(DataTypeManager::OK, preferred_types());
+ EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
+}
+
+TEST_F(BackendMigratorTest, ConfigureFailure) {
+ BackendMigrator migrator(service(), manager());
+ syncable::ModelTypeSet to_migrate;
+ to_migrate.insert(syncable::PREFERENCES);
+
+ EXPECT_CALL(*manager(), state())
+ .WillOnce(Return(DataTypeManager::CONFIGURED));
+ EXPECT_CALL(*manager(), Configure(_)).Times(1);
+ migrator.MigrateTypes(to_migrate);
+ SendConfigureDone(DataTypeManager::ABORTED, syncable::ModelTypeSet());
+ EXPECT_EQ(BackendMigrator::IDLE, migrator.state());
+}
+
+}; // namespace browser_sync
diff --git a/chrome/browser/sync/engine/all_status.cc b/chrome/browser/sync/engine/all_status.cc
index 1580dc4..dea0085 100644
--- a/chrome/browser/sync/engine/all_status.cc
+++ b/chrome/browser/sync/engine/all_status.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -106,12 +106,10 @@ void AllStatus::OnSyncEngineEvent(const SyncEngineEvent& event) {
case SyncEngineEvent::STATUS_CHANGED:
status_ = CalcSyncing(event);
break;
- case SyncEngineEvent::SYNCER_THREAD_PAUSED:
- case SyncEngineEvent::SYNCER_THREAD_RESUMED:
- case SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION:
- case SyncEngineEvent::SYNCER_THREAD_CONNECTED:
case SyncEngineEvent::STOP_SYNCING_PERMANENTLY:
- case SyncEngineEvent::SYNCER_THREAD_EXITING:
+ case SyncEngineEvent::UPDATED_TOKEN:
+ case SyncEngineEvent::CLEAR_SERVER_DATA_FAILED:
+ case SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED:
break;
default:
LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened;
@@ -127,9 +125,6 @@ void AllStatus::HandleServerConnectionEvent(
status_.server_reachable = event.server_reachable;
if (event.connection_code == HttpResponse::SERVER_CONNECTION_OK) {
- if (!status_.authenticated) {
- status_ = CreateBlankStatus();
- }
status_.authenticated = true;
} else {
status_.authenticated = false;
diff --git a/chrome/browser/sync/engine/all_status.h b/chrome/browser/sync/engine/all_status.h
index 015bfb7..9bfc1a2 100644
--- a/chrome/browser/sync/engine/all_status.h
+++ b/chrome/browser/sync/engine/all_status.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -11,7 +11,7 @@
#include <map>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/engine/syncer_types.h"
diff --git a/chrome/browser/sync/engine/apply_updates_command.cc b/chrome/browser/sync/engine/apply_updates_command.cc
index 835c71a..5f98a67 100644
--- a/chrome/browser/sync/engine/apply_updates_command.cc
+++ b/chrome/browser/sync/engine/apply_updates_command.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -29,7 +29,7 @@ void ApplyUpdatesCommand::ModelChangingExecuteImpl(SyncSession* session) {
UpdateApplicator applicator(
session->context()->resolver(),
- session->context()->directory_manager()->cryptographer(),
+ session->context()->directory_manager()->GetCryptographer(&trans),
handles.begin(), handles.end(), session->routing_info(),
session->status_controller()->group_restriction());
while (applicator.AttemptOneApplication(&trans)) {}
diff --git a/chrome/browser/sync/engine/apply_updates_command_unittest.cc b/chrome/browser/sync/engine/apply_updates_command_unittest.cc
index df1a1ae..f9c06bd 100644
--- a/chrome/browser/sync/engine/apply_updates_command_unittest.cc
+++ b/chrome/browser/sync/engine/apply_updates_command_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -215,8 +215,16 @@ TEST_F(ApplyUpdatesCommandTest, ItemsBothKnownAndUnknown) {
TEST_F(ApplyUpdatesCommandTest, DecryptablePassword) {
// Decryptable password updates should be applied.
- Cryptographer* cryptographer =
- session()->context()->directory_manager()->cryptographer();
+ Cryptographer* cryptographer;
+ {
+ // Storing the cryptographer separately is bad, but for this test we
+ // know it's safe.
+ ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
+ ASSERT_TRUE(dir.good());
+ ReadTransaction trans(dir, __FILE__, __LINE__);
+ cryptographer =
+ session()->context()->directory_manager()->GetCryptographer(&trans);
+ }
browser_sync::KeyParams params = {"localhost", "dummy", "foobar"};
cryptographer->AddKey(params);
@@ -262,18 +270,22 @@ TEST_F(ApplyUpdatesCommandTest, UndecryptablePassword) {
TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) {
// Only decryptable password updates should be applied.
{
- Cryptographer* cryptographer =
- session()->context()->directory_manager()->cryptographer();
-
- KeyParams params = {"localhost", "dummy", "foobar"};
- cryptographer->AddKey(params);
-
sync_pb::EntitySpecifics specifics;
sync_pb::PasswordSpecificsData data;
data.set_origin("http://example.com/1");
-
- cryptographer->Encrypt(data,
- specifics.MutableExtension(sync_pb::password)->mutable_encrypted());
+ {
+ ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
+ ASSERT_TRUE(dir.good());
+ ReadTransaction trans(dir, __FILE__, __LINE__);
+ Cryptographer* cryptographer =
+ session()->context()->directory_manager()->GetCryptographer(&trans);
+
+ KeyParams params = {"localhost", "dummy", "foobar"};
+ cryptographer->AddKey(params);
+
+ cryptographer->Encrypt(data,
+ specifics.MutableExtension(sync_pb::password)->mutable_encrypted());
+ }
CreateUnappliedNewItem("item1", specifics, false);
}
{
@@ -304,12 +316,17 @@ TEST_F(ApplyUpdatesCommandTest, SomeUndecryptablePassword) {
}
TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
+ // Storing the cryptographer separately is bad, but for this test we
+ // know it's safe.
+ Cryptographer* cryptographer;
syncable::ModelTypeSet encrypted_types;
{
ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
ASSERT_TRUE(dir.good());
ReadTransaction trans(dir, __FILE__, __LINE__);
EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
+ cryptographer =
+ session()->context()->directory_manager()->GetCryptographer(&trans);
}
// Nigori node updates should update the Cryptographer.
@@ -325,9 +342,6 @@ TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
encrypted_types.insert(syncable::BOOKMARKS);
CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
specifics, true);
-
- Cryptographer* cryptographer =
- session()->context()->directory_manager()->cryptographer();
EXPECT_FALSE(cryptographer->has_pending_keys());
apply_updates_command_.ExecuteImpl(session());
@@ -346,12 +360,17 @@ TEST_F(ApplyUpdatesCommandTest, NigoriUpdate) {
}
TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
+ // Storing the cryptographer separately is bad, but for this test we
+ // know it's safe.
+ Cryptographer* cryptographer;
syncable::ModelTypeSet encrypted_types;
{
ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
ASSERT_TRUE(dir.good());
ReadTransaction trans(dir, __FILE__, __LINE__);
EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
+ cryptographer =
+ session()->context()->directory_manager()->GetCryptographer(&trans);
// With empty encrypted_types, this should be true.
EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
@@ -381,8 +400,6 @@ TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
syncable::BOOKMARKS, NULL);
}
- Cryptographer* cryptographer =
- session()->context()->directory_manager()->cryptographer();
KeyParams params = {"localhost", "dummy", "foobar"};
cryptographer->AddKey(params);
sync_pb::EntitySpecifics specifics;
@@ -437,12 +454,17 @@ TEST_F(ApplyUpdatesCommandTest, EncryptUnsyncedChanges) {
}
TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
+ // Storing the cryptographer separately is bad, but for this test we
+ // know it's safe.
+ Cryptographer* cryptographer;
syncable::ModelTypeSet encrypted_types;
{
ScopedDirLookup dir(syncdb()->manager(), syncdb()->name());
ASSERT_TRUE(dir.good());
ReadTransaction trans(dir, __FILE__, __LINE__);
EXPECT_EQ(encrypted_types, GetEncryptedDataTypes(&trans));
+ cryptographer =
+ session()->context()->directory_manager()->GetCryptographer(&trans);
// With empty encrypted_types, this should be true.
EXPECT_TRUE(VerifyUnsyncedChangesAreEncrypted(&trans, encrypted_types));
@@ -485,8 +507,6 @@ TEST_F(ApplyUpdatesCommandTest, CannotEncryptUnsyncedChanges) {
encrypted_types.insert(syncable::BOOKMARKS);
CreateUnappliedNewItem(syncable::ModelTypeToRootTag(syncable::NIGORI),
specifics, true);
- Cryptographer* cryptographer =
- session()->context()->directory_manager()->cryptographer();
EXPECT_FALSE(cryptographer->has_pending_keys());
{
diff --git a/chrome/browser/sync/engine/build_and_process_conflict_sets_command.cc b/chrome/browser/sync/engine/build_and_process_conflict_sets_command.cc
index 0d14069..ee5873f 100644
--- a/chrome/browser/sync/engine/build_and_process_conflict_sets_command.cc
+++ b/chrome/browser/sync/engine/build_and_process_conflict_sets_command.cc
@@ -1,9 +1,10 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/engine/build_and_process_conflict_sets_command.h"
+#include <set>
#include <string>
#include <sstream>
#include <vector>
@@ -47,7 +48,7 @@ bool BuildAndProcessConflictSetsCommand::BuildAndProcessConflictSets(
session->status_controller()->mutable_conflict_progress());
had_single_direction_sets = ProcessSingleDirectionConflictSets(&trans,
session->context()->resolver(),
- session->context()->directory_manager()->cryptographer(),
+ session->context()->directory_manager()->GetCryptographer(&trans),
session->status_controller(), session->routing_info());
// We applied some updates transactionally, lets try syncing again.
if (had_single_direction_sets)
@@ -65,7 +66,7 @@ bool BuildAndProcessConflictSetsCommand::ProcessSingleDirectionConflictSets(
for (all_sets_iterator = status->conflict_progress().ConflictSetsBegin();
all_sets_iterator != status->conflict_progress().ConflictSetsEnd();) {
const ConflictSet* conflict_set = *all_sets_iterator;
- CHECK(conflict_set->size() >= 2);
+ CHECK_GE(conflict_set->size(), 2U);
// We scan the set to see if it consists of changes of only one type.
ConflictSet::const_iterator i;
size_t unsynced_count = 0, unapplied_count = 0;
@@ -312,8 +313,8 @@ class ServerDeletedPathChecker {
// returns 0 if we should stop investigating the path.
static syncable::Id GetAndExamineParent(syncable::BaseTransaction* trans,
- syncable::Id id,
- syncable::Id check_id,
+ const syncable::Id& id,
+ const syncable::Id& check_id,
const syncable::Entry& log_entry) {
syncable::Entry parent(trans, syncable::GET_BY_ID, id);
CHECK(parent.good()) << "Tree inconsitency, missing id" << id << " "
@@ -334,8 +335,8 @@ class LocallyDeletedPathChecker {
// returns 0 if we should stop investigating the path.
static syncable::Id GetAndExamineParent(syncable::BaseTransaction* trans,
- syncable::Id id,
- syncable::Id check_id,
+ const syncable::Id& id,
+ const syncable::Id& check_id,
const syncable::Entry& log_entry) {
syncable::Entry parent(trans, syncable::GET_BY_ID, id);
if (!parent.good())
diff --git a/chrome/browser/sync/engine/change_reorder_buffer.h b/chrome/browser/sync/engine/change_reorder_buffer.h
index 4e0fd82..f4062ef 100644
--- a/chrome/browser/sync/engine/change_reorder_buffer.h
+++ b/chrome/browser/sync/engine/change_reorder_buffer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -13,7 +13,7 @@
#include <map>
#include <vector>
-#include "base/linked_ptr.h"
+#include "base/memory/linked_ptr.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/protocol/sync.pb.h"
diff --git a/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc b/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc
index 9a3e6ab..02ec54d 100644
--- a/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc
+++ b/chrome/browser/sync/engine/cleanup_disabled_types_command_unittest.cc
@@ -82,17 +82,5 @@ TEST_F(CleanupDisabledTypesCommandTest, TypeDisabled) {
command.ExecuteImpl(session());
}
-TEST_F(CleanupDisabledTypesCommandTest,
- SyncerEndCommandSetsPreviousRoutingInfo) {
- SyncerEndCommand command;
-
- ModelSafeRoutingInfo info;
- EXPECT_TRUE(info == session()->context()->previous_session_routing_info());
- command.ExecuteImpl(session());
- ASSERT_FALSE(routing_info().empty());
- EXPECT_TRUE(routing_info() ==
- session()->context()->previous_session_routing_info());
-}
-
} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/download_updates_command.cc b/chrome/browser/sync/engine/download_updates_command.cc
index ba6cb75..e05da4e 100644
--- a/chrome/browser/sync/engine/download_updates_command.cc
+++ b/chrome/browser/sync/engine/download_updates_command.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,8 +9,8 @@
#include "chrome/browser/sync/engine/syncer.h"
#include "chrome/browser/sync/engine/syncer_proto_util.h"
#include "chrome/browser/sync/engine/syncproto.h"
-#include "chrome/browser/sync/sessions/sync_session.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
using syncable::ScopedDirLookup;
@@ -43,7 +43,8 @@ void DownloadUpdatesCommand::ExecuteImpl(SyncSession* session) {
// Request updates for all enabled types.
syncable::ModelTypeBitSet enabled_types;
- const sessions::TypePayloadMap& type_payload_map = session->source().types;
+ const syncable::ModelTypePayloadMap& type_payload_map =
+ session->source().types;
for (ModelSafeRoutingInfo::const_iterator i = session->routing_info().begin();
i != session->routing_info().end(); ++i) {
syncable::ModelType model_type = syncable::ModelTypeFromInt(i->first);
@@ -53,7 +54,7 @@ void DownloadUpdatesCommand::ExecuteImpl(SyncSession* session) {
dir->GetDownloadProgress(model_type, progress_marker);
// Set notification hint if present.
- sessions::TypePayloadMap::const_iterator type_payload =
+ syncable::ModelTypePayloadMap::const_iterator type_payload =
type_payload_map.find(i->first);
if (type_payload != type_payload_map.end()) {
progress_marker->set_notification_hint(type_payload->second);
diff --git a/chrome/browser/sync/engine/http_post_provider_factory.h b/chrome/browser/sync/engine/http_post_provider_factory.h
new file mode 100644
index 0000000..57a54c7
--- /dev/null
+++ b/chrome/browser/sync/engine/http_post_provider_factory.h
@@ -0,0 +1,34 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_ENGINE_HTTP_POST_PROVIDER_FACTORY_H_
+#define CHROME_BROWSER_SYNC_ENGINE_HTTP_POST_PROVIDER_FACTORY_H_
+#pragma once
+
+namespace sync_api {
+
+class HttpPostProviderInterface;
+
+// A factory to create HttpPostProviders to hide details about the
+// implementations and dependencies.
+// A factory instance itself should be owned by whomever uses it to create
+// HttpPostProviders.
+class HttpPostProviderFactory {
+ public:
+ virtual ~HttpPostProviderFactory() {}
+
+ // Obtain a new HttpPostProviderInterface instance, owned by caller.
+ virtual HttpPostProviderInterface* Create() = 0;
+
+ // When the interface is no longer needed (ready to be cleaned up), clients
+ // must call Destroy().
+ // This allows actual HttpPostProvider subclass implementations to be
+ // reference counted, which is useful if a particular implementation uses
+ // multiple threads to serve network requests.
+ virtual void Destroy(HttpPostProviderInterface* http) = 0;
+};
+
+} // namespace sync_api
+
+#endif // CHROME_BROWSER_SYNC_ENGINE_HTTP_POST_PROVIDER_FACTORY_H_
diff --git a/chrome/browser/sync/engine/http_post_provider_interface.h b/chrome/browser/sync/engine/http_post_provider_interface.h
new file mode 100644
index 0000000..4bb5a34
--- /dev/null
+++ b/chrome/browser/sync/engine/http_post_provider_interface.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_ENGINE_HTTP_POST_PROVIDER_INTERFACE_H_
+#define CHROME_BROWSER_SYNC_ENGINE_HTTP_POST_PROVIDER_INTERFACE_H_
+#pragma once
+
+#include <string>
+
+namespace sync_api {
+
+// An interface the embedding application (e.g. Chromium) implements to provide
+// required HTTP POST functionality to the syncer backend. This interface is
+// designed for one-time use. You create one, use it, and create another if you
+// want to make a subsequent POST.
+class HttpPostProviderInterface {
+ public:
+ virtual ~HttpPostProviderInterface() {}
+
+ // Use specified user agent string when POSTing. If not called a default UA
+ // may be used.
+ virtual void SetUserAgent(const char* user_agent) = 0;
+
+ // Add additional headers to the request.
+ virtual void SetExtraRequestHeaders(const char* headers) = 0;
+
+ // Set the URL to POST to.
+ virtual void SetURL(const char* url, int port) = 0;
+
+ // Set the type, length and content of the POST payload.
+ // |content_type| is a null-terminated MIME type specifier.
+ // |content| is a data buffer; Do not interpret as a null-terminated string.
+ // |content_length| is the total number of chars in |content|. It is used to
+ // assign/copy |content| data.
+ virtual void SetPostPayload(const char* content_type,
+ int content_length,
+ const char* content) = 0;
+
+ // Returns true if the URL request succeeded. If the request failed,
+ // os_error() may be non-zero and hence contain more information.
+ virtual bool MakeSynchronousPost(int* os_error_code, int* response_code) = 0;
+
+ // Get the length of the content returned in the HTTP response.
+ // This does not count the trailing null-terminating character returned
+ // by GetResponseContent, so it is analogous to calling string.length.
+ virtual int GetResponseContentLength() const = 0;
+
+ // Get the content returned in the HTTP response.
+ // This is a null terminated string of characters.
+ // Value should be copied.
+ virtual const char* GetResponseContent() const = 0;
+
+ // Get the value of a header returned in the HTTP response.
+ // If the header is not present, returns the empty string.
+ virtual const std::string GetResponseHeaderValue(
+ const std::string& name) const = 0;
+
+ // Abandon any pending POST and unblock caller in MakeSynchronousPost.
+ // This must be safe to call from any thread.
+ virtual void Abort() = 0;
+};
+
+} // namespace sync_api
+
+#endif // CHROME_BROWSER_SYNC_ENGINE_HTTP_POST_PROVIDER_INTERFACE_H_
diff --git a/chrome/browser/sync/engine/idle_query_linux.cc b/chrome/browser/sync/engine/idle_query_linux.cc
index 443db06..55674a1 100644
--- a/chrome/browser/sync/engine/idle_query_linux.cc
+++ b/chrome/browser/sync/engine/idle_query_linux.cc
@@ -1,10 +1,11 @@
// Copyright (c) 2009 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.
+
#include "chrome/browser/sync/engine/idle_query_linux.h"
-#include <X11/Xlib.h>
#include <X11/extensions/scrnsaver.h>
+#include "ui/base/x/x11_util.h"
namespace browser_sync {
@@ -13,8 +14,8 @@ class IdleData {
IdleData() {
int event_base;
int error_base;
- display = XOpenDisplay(NULL);
- if (XScreenSaverQueryExtension(display, &event_base, &error_base)) {
+ if (XScreenSaverQueryExtension(ui::GetXDisplay(), &event_base,
+ &error_base)) {
mit_info = XScreenSaverAllocInfo();
} else {
mit_info = NULL;
@@ -22,36 +23,28 @@ class IdleData {
}
~IdleData() {
- if (display) {
- XCloseDisplay(display);
- display = NULL;
- }
- if (mit_info) {
+ if (mit_info)
XFree(mit_info);
- }
}
XScreenSaverInfo *mit_info;
- Display *display;
};
-IdleQueryLinux::IdleQueryLinux() : idle_data_(new IdleData()) {
-}
+IdleQueryLinux::IdleQueryLinux() : idle_data_(new IdleData()) {}
-IdleQueryLinux::~IdleQueryLinux() {
-}
+IdleQueryLinux::~IdleQueryLinux() {}
int IdleQueryLinux::IdleTime() {
- if (!idle_data_->mit_info || !idle_data_->display) {
+ if (!idle_data_->mit_info)
return 0;
- }
- if (XScreenSaverQueryInfo(idle_data_->display,
- RootWindow(idle_data_->display, 0),
+ if (XScreenSaverQueryInfo(ui::GetXDisplay(),
+ RootWindow(ui::GetXDisplay(), 0),
idle_data_->mit_info)) {
return (idle_data_->mit_info->idle) / 1000;
} else {
return 0;
}
}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/idle_query_linux.h b/chrome/browser/sync/engine/idle_query_linux.h
index ef91bdd..cd61e74 100644
--- a/chrome/browser/sync/engine/idle_query_linux.h
+++ b/chrome/browser/sync/engine/idle_query_linux.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,7 +6,7 @@
#define CHROME_BROWSER_SYNC_ENGINE_IDLE_QUERY_LINUX_H_
#pragma once
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/mock_model_safe_workers.cc b/chrome/browser/sync/engine/mock_model_safe_workers.cc
index bd6a26b..77ed4e9 100644
--- a/chrome/browser/sync/engine/mock_model_safe_workers.cc
+++ b/chrome/browser/sync/engine/mock_model_safe_workers.cc
@@ -24,6 +24,21 @@ MockModelSafeWorkerRegistrar*
return m;
}
+MockModelSafeWorkerRegistrar* MockModelSafeWorkerRegistrar::PassiveForTypes(
+ const syncable::ModelTypeBitSet& set) {
+ ModelSafeRoutingInfo routes;
+ for (int i = syncable::UNSPECIFIED ; i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType type = syncable::ModelTypeFromInt(i);
+ if (set[type]) {
+ routes[type] = GROUP_PASSIVE;
+ }
+ }
+ MockModelSafeWorkerRegistrar* m = new MockModelSafeWorkerRegistrar(routes);
+ m->passive_worker_ = new ModelSafeWorker();
+ return m;
+}
+
+
void MockModelSafeWorkerRegistrar::GetWorkers(
std::vector<ModelSafeWorker*>* out) {
if (passive_worker_.get())
diff --git a/chrome/browser/sync/engine/mock_model_safe_workers.h b/chrome/browser/sync/engine/mock_model_safe_workers.h
index a76a51c..04f4adf 100644
--- a/chrome/browser/sync/engine/mock_model_safe_workers.h
+++ b/chrome/browser/sync/engine/mock_model_safe_workers.h
@@ -8,7 +8,7 @@
#include <vector>
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/syncable/model_type.h"
@@ -30,6 +30,8 @@ class MockModelSafeWorkerRegistrar : public ModelSafeWorkerRegistrar {
public:
virtual ~MockModelSafeWorkerRegistrar();
static MockModelSafeWorkerRegistrar* PassiveBookmarks();
+ static MockModelSafeWorkerRegistrar* PassiveForTypes(
+ const syncable::ModelTypeBitSet& set);
virtual void GetWorkers(std::vector<ModelSafeWorker*>* out);
virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out);
diff --git a/chrome/browser/sync/engine/model_safe_worker.cc b/chrome/browser/sync/engine/model_safe_worker.cc
index c488e3b..d9f44bf 100644
--- a/chrome/browser/sync/engine/model_safe_worker.cc
+++ b/chrome/browser/sync/engine/model_safe_worker.cc
@@ -14,7 +14,7 @@ ModelSafeGroup GetGroupForModelType(const syncable::ModelType type,
// with the server's PermanentItemPopulator is causing TLF updates in
// some cases. See bug 36735.
if (type != syncable::UNSPECIFIED && type != syncable::TOP_LEVEL_FOLDER)
- NOTREACHED() << "Entry does not belong to active ModelSafeGroup!";
+ LOG(WARNING) << "Entry does not belong to active ModelSafeGroup!";
return GROUP_PASSIVE;
}
return it->second;
diff --git a/chrome/browser/sync/engine/model_safe_worker.h b/chrome/browser/sync/engine/model_safe_worker.h
index 9f31c67..29e11eb 100644
--- a/chrome/browser/sync/engine/model_safe_worker.h
+++ b/chrome/browser/sync/engine/model_safe_worker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,7 +11,7 @@
#include <vector>
#include "base/callback.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "chrome/browser/sync/syncable/model_type.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/engine/net/server_connection_manager.cc b/chrome/browser/sync/engine/net/server_connection_manager.cc
index 7f53a29..f84b8b7 100644
--- a/chrome/browser/sync/engine/net/server_connection_manager.cc
+++ b/chrome/browser/sync/engine/net/server_connection_manager.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,6 +10,7 @@
#include <string>
#include <vector>
+#include "base/command_line.h"
#include "build/build_config.h"
#include "chrome/browser/sync/engine/net/url_translator.h"
#include "chrome/browser/sync/engine/syncapi.h"
@@ -17,6 +18,7 @@
#include "chrome/browser/sync/engine/syncproto.h"
#include "chrome/browser/sync/protocol/sync.pb.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/http_return.h"
#include "googleurl/src/gurl.h"
@@ -144,6 +146,7 @@ ServerConnectionManager::ServerConnectionManager(
get_time_path_(kSyncServerGetTimePath),
error_count_(0),
channel_(new Channel(shutdown_event)),
+ listeners_(new ObserverListThreadSafe<ServerConnectionEventListener>()),
server_status_(HttpResponse::NONE),
server_reachable_(false),
reset_count_(0),
@@ -155,10 +158,8 @@ ServerConnectionManager::~ServerConnectionManager() {
}
void ServerConnectionManager::NotifyStatusChanged() {
- ServerConnectionEvent event = { ServerConnectionEvent::STATUS_CHANGED,
- server_status_,
- server_reachable_ };
- channel_->NotifyListeners(event);
+ listeners_->Notify(&ServerConnectionEventListener::OnServerConnectionEvent,
+ ServerConnectionEvent2(server_status_, server_reachable_));
}
bool ServerConnectionManager::PostBufferWithCachedAuth(
@@ -329,6 +330,16 @@ std::string ServerConnectionManager::GetServerHost() const {
return gurl.host();
}
+void ServerConnectionManager::AddListener(
+ ServerConnectionEventListener* listener) {
+ listeners_->AddObserver(listener);
+}
+
+void ServerConnectionManager::RemoveListener(
+ ServerConnectionEventListener* listener) {
+ listeners_->RemoveObserver(listener);
+}
+
ServerConnectionManager::Post* ServerConnectionManager::MakePost() {
return NULL; // For testing.
}
diff --git a/chrome/browser/sync/engine/net/server_connection_manager.h b/chrome/browser/sync/engine/net/server_connection_manager.h
index 214c622..9f49d4a 100644
--- a/chrome/browser/sync/engine/net/server_connection_manager.h
+++ b/chrome/browser/sync/engine/net/server_connection_manager.h
@@ -10,6 +10,7 @@
#include <string>
#include "base/atomicops.h"
+#include "base/observer_list_threadsafe.h"
#include "base/string_util.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
@@ -107,6 +108,7 @@ inline bool IsGoodReplyFromServer(HttpResponse::ServerConnectionCode code) {
return code >= HttpResponse::SERVER_CONNECTION_OK;
}
+// TODO(tim): Deprecated.
struct ServerConnectionEvent {
// Traits.
typedef ServerConnectionEvent EventType;
@@ -124,6 +126,21 @@ struct ServerConnectionEvent {
bool server_reachable;
};
+struct ServerConnectionEvent2 {
+ HttpResponse::ServerConnectionCode connection_code;
+ bool server_reachable;
+ ServerConnectionEvent2(HttpResponse::ServerConnectionCode code,
+ bool server_reachable) :
+ connection_code(code), server_reachable(server_reachable) {}
+};
+
+class ServerConnectionEventListener {
+ public:
+ virtual void OnServerConnectionEvent(const ServerConnectionEvent2& event) = 0;
+ protected:
+ virtual ~ServerConnectionEventListener() {}
+};
+
class ServerConnectionManager;
// A helper class that automatically notifies when the status changes.
// TODO(tim): This class shouldn't be exposed outside of the implementation,
@@ -240,6 +257,9 @@ class ServerConnectionManager {
inline Channel* channel() const { return channel_; }
+ void AddListener(ServerConnectionEventListener* listener);
+ void RemoveListener(ServerConnectionEventListener* listener);
+
inline std::string user_agent() const { return user_agent_; }
inline HttpResponse::ServerConnectionCode server_status() const {
@@ -344,8 +364,12 @@ class ServerConnectionManager {
base::Lock error_count_mutex_; // Protects error_count_
int error_count_; // Tracks the number of connection errors.
+ // TODO(tim): Deprecated.
Channel* const channel_;
+ scoped_refptr<ObserverListThreadSafe<ServerConnectionEventListener> >
+ listeners_;
+
// Volatile so various threads can call server_status() without
// synchronization.
volatile HttpResponse::ServerConnectionCode server_status_;
diff --git a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.cc b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.cc
index 0d8f2a7..3ba02b4 100644
--- a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.cc
+++ b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.cc
@@ -1,14 +1,15 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h"
+#include "chrome/browser/sync/engine/http_post_provider_factory.h"
+#include "chrome/browser/sync/engine/http_post_provider_interface.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/common/net/http_return.h"
using browser_sync::HttpResponse;
-using std::string;
namespace sync_api {
@@ -20,10 +21,11 @@ SyncAPIBridgedPost::SyncAPIBridgedPost(
SyncAPIBridgedPost::~SyncAPIBridgedPost() {}
-
-bool SyncAPIBridgedPost::Init(const char* path, const string& auth_token,
- const string& payload, HttpResponse* response) {
- string sync_server;
+bool SyncAPIBridgedPost::Init(const char* path,
+ const std::string& auth_token,
+ const std::string& payload,
+ HttpResponse* response) {
+ std::string sync_server;
int sync_server_port = 0;
bool use_ssl = false;
GetServerParams(&sync_server, &sync_server_port, &use_ssl);
@@ -34,7 +36,7 @@ bool SyncAPIBridgedPost::Init(const char* path, const string& auth_token,
http->SetURL(connection_url.c_str(), sync_server_port);
if (!auth_token.empty()) {
- string headers = "Authorization: GoogleLogin auth=" + auth_token;
+ std::string headers = "Authorization: GoogleLogin auth=" + auth_token;
http->SetExtraRequestHeaders(headers.c_str());
}
@@ -94,5 +96,4 @@ SyncAPIServerConnectionManager::MakePost() {
return new SyncAPIBridgedPost(this, post_provider_factory_.get());
}
-
} // namespace sync_api
diff --git a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h
index cdf0711..2c46df8 100644
--- a/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h
+++ b/chrome/browser/sync/engine/net/syncapi_server_connection_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,7 +8,7 @@
#include <string>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/browser/sync/engine/net/server_connection_manager.h"
namespace sync_api {
diff --git a/chrome/browser/sync/engine/nudge_source.h b/chrome/browser/sync/engine/nudge_source.h
index 3e3b3ae..fc9b02a 100644
--- a/chrome/browser/sync/engine/nudge_source.h
+++ b/chrome/browser/sync/engine/nudge_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,8 +8,6 @@
namespace browser_sync {
-namespace s3 {
-
enum NudgeSource {
NUDGE_SOURCE_UNKNOWN = 0,
// We received an invalidation message and are nudging to check for updates.
@@ -20,7 +18,6 @@ enum NudgeSource {
NUDGE_SOURCE_CONTINUATION,
};
-} // namespace s3
} // namespace browser_sync
#endif // CHROME_BROWSER_SYNC_ENGINE_NUDGE_SOURCE_H_
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index 416df24..9ad111d 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -15,31 +15,31 @@
#include "base/base64.h"
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/observer_list.h"
-#include "base/scoped_ptr.h"
#include "base/sha1.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/synchronization/lock.h"
#include "base/task.h"
+#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
-#include "chrome/browser/sync/sync_constants.h"
#include "chrome/browser/sync/engine/all_status.h"
#include "chrome/browser/sync/engine/change_reorder_buffer.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
+#include "chrome/browser/sync/engine/nudge_source.h"
#include "chrome/browser/sync/engine/net/server_connection_manager.h"
#include "chrome/browser/sync/engine/net/syncapi_server_connection_manager.h"
#include "chrome/browser/sync/engine/syncer.h"
#include "chrome/browser/sync/engine/syncer_thread.h"
-#include "chrome/browser/sync/engine/syncer_thread2.h"
-#include "chrome/browser/sync/engine/syncer_thread_adapter.h"
+#include "chrome/browser/sync/engine/http_post_provider_factory.h"
#include "chrome/browser/sync/js_arg_list.h"
#include "chrome/browser/sync/js_backend.h"
#include "chrome/browser/sync/js_event_router.h"
-#include "chrome/browser/sync/notifier/server_notifier_thread.h"
-#include "chrome/browser/sync/notifier/state_writer.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
#include "chrome/browser/sync/protocol/app_specifics.pb.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
@@ -55,7 +55,10 @@
#include "chrome/browser/sync/sessions/sync_session.h"
#include "chrome/browser/sync/sessions/sync_session_context.h"
#include "chrome/browser/sync/syncable/autofill_migration.h"
+#include "chrome/browser/sync/syncable/directory_change_listener.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/nigori_util.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/util/crypto_helpers.h"
@@ -63,12 +66,9 @@
#include "chrome/common/deprecated/event_sys.h"
#include "chrome/common/net/gaia/gaia_authenticator.h"
#include "content/browser/browser_thread.h"
-#include "jingle/notifier/listener/mediator_thread_impl.h"
-#include "jingle/notifier/listener/notification_constants.h"
-#include "jingle/notifier/listener/talk_mediator.h"
-#include "jingle/notifier/listener/talk_mediator_impl.h"
#include "net/base/network_change_notifier.h"
+using base::TimeDelta;
using browser_sync::AllStatus;
using browser_sync::Cryptographer;
using browser_sync::KeyParams;
@@ -76,15 +76,14 @@ using browser_sync::ModelSafeRoutingInfo;
using browser_sync::ModelSafeWorker;
using browser_sync::ModelSafeWorkerRegistrar;
using browser_sync::ServerConnectionEvent;
+using browser_sync::ServerConnectionEvent2;
+using browser_sync::ServerConnectionEventListener;
using browser_sync::SyncEngineEvent;
using browser_sync::SyncEngineEventListener;
using browser_sync::Syncer;
using browser_sync::SyncerThread;
-using browser_sync::SyncerThreadAdapter;
using browser_sync::kNigoriTag;
using browser_sync::sessions::SyncSessionContext;
-using notifier::TalkMediator;
-using notifier::TalkMediatorImpl;
using std::list;
using std::hex;
using std::string;
@@ -92,6 +91,9 @@ using std::vector;
using syncable::Directory;
using syncable::DirectoryManager;
using syncable::Entry;
+using syncable::ModelTypeBitSet;
+using syncable::OriginalEntries;
+using syncable::WriterTag;
using syncable::SPECIFICS;
using sync_pb::AutofillProfileSpecifics;
@@ -319,21 +321,9 @@ DictionaryValue* BaseNode::ToValue() const {
node_info->SetBoolean("isFolder", GetIsFolder());
// TODO(akalin): Add a std::string accessor for the title.
node_info->SetString("title", WideToUTF8(GetTitle()));
- {
- syncable::ModelType model_type = GetModelType();
- if (model_type >= syncable::FIRST_REAL_MODEL_TYPE) {
- node_info->SetString("type", ModelTypeToString(model_type));
- } else if (model_type == syncable::TOP_LEVEL_FOLDER) {
- node_info->SetString("type", "Top-level folder");
- } else if (model_type == syncable::UNSPECIFIED) {
- node_info->SetString("type", "Unspecified");
- } else {
- node_info->SetString("type", base::IntToString(model_type));
- }
- }
- node_info->Set(
- "specifics",
- browser_sync::EntitySpecificsToValue(GetEntry()->Get(SPECIFICS)));
+ node_info->Set("type", ModelTypeToValue(GetModelType()));
+ // Specifics are already in the Entry value, so no need to duplicate
+ // it here.
node_info->SetString("externalId",
base::Int64ToString(GetExternalId()));
node_info->SetString("predecessorId",
@@ -342,6 +332,7 @@ DictionaryValue* BaseNode::ToValue() const {
base::Int64ToString(GetSuccessorId()));
node_info->SetString("firstChildId",
base::Int64ToString(GetFirstChildId()));
+ node_info->Set("entry", GetEntry()->ToValue());
return node_info;
}
@@ -566,10 +557,25 @@ void WriteNode::PutNigoriSpecificsAndMarkForSyncing(
void WriteNode::SetPasswordSpecifics(
const sync_pb::PasswordSpecificsData& data) {
DCHECK_EQ(syncable::PASSWORDS, GetModelType());
+
+ Cryptographer* cryptographer = GetTransaction()->GetCryptographer();
+
+ // Idempotency check to prevent unnecessary syncing: if the plaintexts match
+ // and the old ciphertext is encrypted with the most current key, there's
+ // nothing to do here. Because each encryption is seeded with a different
+ // random value, checking for equivalence post-encryption doesn't suffice.
+ const sync_pb::EncryptedData& old_ciphertext =
+ GetEntry()->Get(SPECIFICS).GetExtension(sync_pb::password).encrypted();
+ scoped_ptr<sync_pb::PasswordSpecificsData> old_plaintext(
+ DecryptPasswordSpecifics(GetEntry()->Get(SPECIFICS), cryptographer));
+ if (old_plaintext.get() &&
+ old_plaintext->SerializeAsString() == data.SerializeAsString() &&
+ cryptographer->CanDecryptUsingDefaultKey(old_ciphertext)) {
+ return;
+ }
+
sync_pb::PasswordSpecifics new_value;
- if (!GetTransaction()->GetCryptographer()->Encrypt(
- data,
- new_value.mutable_encrypted())) {
+ if (!cryptographer->Encrypt(data, new_value.mutable_encrypted())) {
NOTREACHED();
}
PutPasswordSpecificsAndMarkForSyncing(new_value);
@@ -1091,6 +1097,21 @@ DictionaryValue* SyncManager::ChangeRecord::ToValue(
return value;
}
+bool BaseNode::ContainsString(const std::string& lowercase_query) const {
+ DCHECK(GetEntry());
+ // TODO(lipalani) - figure out what to do if the node is encrypted.
+ const sync_pb::EntitySpecifics& specifics = GetEntry()->Get(SPECIFICS);
+ std::string temp;
+ // The protobuf serialized string contains the original strings. So
+ // we will just serialize it and search it.
+ specifics.SerializeToString(&temp);
+
+ // Now convert to lower case.
+ StringToLowerASCII(&temp);
+
+ return temp.find(lowercase_query) != std::string::npos;
+}
+
SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData() {}
SyncManager::ExtraPasswordChangeRecordData::ExtraPasswordChangeRecordData(
@@ -1109,15 +1130,53 @@ const sync_pb::PasswordSpecificsData&
return unencrypted_;
}
+namespace {
+
+struct NotificationInfo {
+ int total_count;
+ std::string payload;
+
+ NotificationInfo() : total_count(0) {}
+
+ ~NotificationInfo() {}
+
+ // Returned pointer owned by the caller.
+ DictionaryValue* ToValue() const {
+ DictionaryValue* value = new DictionaryValue();
+ value->SetInteger("totalCount", total_count);
+ value->SetString("payload", payload);
+ return value;
+ }
+};
+
+typedef std::map<syncable::ModelType, NotificationInfo> NotificationInfoMap;
+
+// returned pointer is owned by the caller.
+DictionaryValue* NotificationInfoToValue(
+ const NotificationInfoMap& notification_info) {
+ DictionaryValue* value = new DictionaryValue();
+
+ for (NotificationInfoMap::const_iterator it = notification_info.begin();
+ it != notification_info.end(); ++it) {
+ const std::string& model_type_str =
+ syncable::ModelTypeToString(it->first);
+ value->Set(model_type_str, it->second.ToValue());
+ }
+
+ return value;
+}
+
+} // namespace
+
//////////////////////////////////////////////////////////////////////////
// SyncManager's implementation: SyncManager::SyncInternal
class SyncManager::SyncInternal
: public net::NetworkChangeNotifier::IPAddressObserver,
- public TalkMediator::Delegate,
- public sync_notifier::StateWriter,
- public browser_sync::ChannelEventHandler<syncable::DirectoryChangeEvent>,
+ public sync_notifier::SyncNotifierObserver,
public browser_sync::JsBackend,
- public SyncEngineEventListener {
+ public SyncEngineEventListener,
+ public ServerConnectionEventListener,
+ public syncable::DirectoryChangeListener {
static const int kDefaultNudgeDelayMilliseconds;
static const int kPreferencesNudgeDelayMilliseconds;
public:
@@ -1126,15 +1185,13 @@ class SyncManager::SyncInternal
parent_router_(NULL),
sync_manager_(sync_manager),
registrar_(NULL),
- notification_pending_(false),
initialized_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)),
- server_notifier_thread_(NULL) {
+ ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
virtual ~SyncInternal() {
- DCHECK(!core_message_loop_);
+ CHECK(!core_message_loop_);
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
@@ -1146,7 +1203,7 @@ class SyncManager::SyncInternal
ModelSafeWorkerRegistrar* model_safe_worker_registrar,
const char* user_agent,
const SyncCredentials& credentials,
- const notifier::NotifierOptions& notifier_options,
+ sync_notifier::SyncNotifier* sync_notifier,
const std::string& restored_key_for_bootstrapping,
bool setup_for_test_mode);
@@ -1159,9 +1216,8 @@ class SyncManager::SyncInternal
// Update tokens that we're using in Sync. Email must stay the same.
void UpdateCredentials(const SyncCredentials& credentials);
- // Update the set of enabled sync types. Usually called when the user disables
- // or enables a sync type.
- void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
+ // Called when the user disables or enables a sync type.
+ void UpdateEnabledTypes();
// Tell the sync engine to start the syncing process.
void StartSyncing();
@@ -1181,18 +1237,22 @@ class SyncManager::SyncInternal
// to the syncapi model.
void SaveChanges();
+ // DirectoryChangeListener implementation.
// This listener is called upon completion of a syncable transaction, and
// builds the list of sync-engine initiated changes that will be forwarded to
// the SyncManager's Observers.
- virtual void HandleChannelEvent(const syncable::DirectoryChangeEvent& event);
- void HandleTransactionCompleteChangeEvent(
- const syncable::DirectoryChangeEvent& event);
- void HandleTransactionEndingChangeEvent(
- const syncable::DirectoryChangeEvent& event);
- void HandleCalculateChangesChangeEventFromSyncApi(
- const syncable::DirectoryChangeEvent& event);
- void HandleCalculateChangesChangeEventFromSyncer(
- const syncable::DirectoryChangeEvent& event);
+ virtual void HandleTransactionCompleteChangeEvent(
+ const ModelTypeBitSet& models_with_changes);
+ virtual ModelTypeBitSet HandleTransactionEndingChangeEvent(
+ syncable::BaseTransaction* trans);
+ virtual void HandleCalculateChangesChangeEventFromSyncApi(
+ const OriginalEntries& originals,
+ const WriterTag& writer,
+ syncable::BaseTransaction* trans);
+ virtual void HandleCalculateChangesChangeEventFromSyncer(
+ const OriginalEntries& originals,
+ const WriterTag& writer,
+ syncable::BaseTransaction* trans);
// Listens for notifications from the ServerConnectionManager
void HandleServerConnectionEvent(const ServerConnectionEvent& event);
@@ -1200,21 +1260,14 @@ class SyncManager::SyncInternal
// Open the directory named with username_for_share
bool OpenDirectory();
- // Login to the talk mediator with the given credentials.
- void TalkMediatorLogin(
- const std::string& email, const std::string& token);
-
- // TalkMediator::Delegate implementation.
+ // SyncNotifierObserver implementation.
virtual void OnNotificationStateChange(
bool notifications_enabled);
virtual void OnIncomingNotification(
- const IncomingNotificationData& notification_data);
-
- virtual void OnOutgoingNotification();
+ const syncable::ModelTypePayloadMap& type_payloads);
- // sync_notifier::StateWriter implementation.
- virtual void WriteState(const std::string& state);
+ virtual void StoreState(const std::string& cookie);
void AddObserver(SyncManager::Observer* observer);
@@ -1225,8 +1278,7 @@ class SyncManager::SyncInternal
SyncAPIServerConnectionManager* connection_manager() {
return connection_manager_.get();
}
- SyncerThreadAdapter* syncer_thread() { return syncer_thread_.get(); }
- TalkMediator* talk_mediator() { return talk_mediator_.get(); }
+ SyncerThread* syncer_thread() { return syncer_thread_.get(); }
UserShare* GetUserShare() { return &share_; }
// Return the currently active (validated) username for use with syncable
@@ -1237,6 +1289,12 @@ class SyncManager::SyncInternal
Status GetStatus();
+ void RequestNudge(const tracked_objects::Location& nudge_location);
+
+ void RequestNudgeWithDataTypes(const TimeDelta& delay,
+ browser_sync::NudgeSource source, const ModelTypeBitSet& types,
+ const tracked_objects::Location& nudge_location);
+
// See SyncManager::Shutdown for information.
void Shutdown();
@@ -1325,6 +1383,9 @@ class SyncManager::SyncInternal
// SyncEngineEventListener implementation.
virtual void OnSyncEngineEvent(const SyncEngineEvent& event);
+ // ServerConnectionEventListener implementation.
+ virtual void OnServerConnectionEvent(const ServerConnectionEvent2& event);
+
// browser_sync::JsBackend implementation.
virtual void SetParentJsEventRouter(browser_sync::JsEventRouter* router);
virtual void RemoveParentJsEventRouter();
@@ -1333,11 +1394,9 @@ class SyncManager::SyncInternal
const browser_sync::JsArgList& args,
const browser_sync::JsEventHandler* sender);
- private:
- // Helper to handle the details of initializing the TalkMediator.
- // Must be called only after OpenDirectory() is called.
- void InitializeTalkMediator();
+ ListValue* FindNodesContainingString(const std::string& query);
+ private:
// Helper to call OnAuthError when no authentication credentials are
// available.
void RaiseAuthNeededEvent();
@@ -1347,10 +1406,8 @@ class SyncManager::SyncInternal
// already initialized, this is a no-op.
void MarkAndNotifyInitializationComplete();
- // If there's a pending notification to be sent, either from the
- // new_pending_notification flag or a previous unsuccessfully sent
- // notification, tries to send a notification.
- void SendPendingXMPPNotification(bool new_pending_notification);
+ // Sends notifications to peers.
+ void SendNotification();
// Determine if the parents or predecessors differ between the old and new
// versions of an entry stored in |a| and |b|. Note that a node's index may
@@ -1436,11 +1493,17 @@ class SyncManager::SyncInternal
// decryption. Otherwise, the cryptographer is made ready (is_ready()).
void BootstrapEncryption(const std::string& restored_key_for_bootstrapping);
+ // Called for every notification. This updates the notification statistics
+ // to be displayed in about:sync.
+ void UpdateNotificationInfo(
+ const syncable::ModelTypePayloadMap& type_payloads);
+
// Helper for migration to new nigori proto to set
// 'using_explicit_passphrase' in the NigoriSpecifics.
// TODO(tim): Bug 62103. Remove this after it has been pushed out to dev
// channel users.
- void SetUsingExplicitPassphrasePrefForMigration();
+ void SetUsingExplicitPassphrasePrefForMigration(
+ WriteTransaction* const trans);
// Checks for server reachabilty and requests a nudge.
void OnIPAddressChangedImpl();
@@ -1449,6 +1512,9 @@ class SyncManager::SyncInternal
browser_sync::JsArgList ProcessGetNodeByIdMessage(
const browser_sync::JsArgList& args);
+ browser_sync::JsArgList ProcessFindNodesContainingString(
+ const browser_sync::JsArgList& args);
+
// We couple the DirectoryManager and username together in a UserShare member
// so we can return a handle to share_ to clients of the API for use when
// constructing any transaction type.
@@ -1465,10 +1531,10 @@ class SyncManager::SyncInternal
scoped_ptr<SyncAPIServerConnectionManager> connection_manager_;
// The thread that runs the Syncer. Needs to be explicitly Start()ed.
- scoped_ptr<SyncerThreadAdapter> syncer_thread_;
+ scoped_ptr<SyncerThread> syncer_thread_;
- // Notification (xmpp) handler.
- scoped_ptr<TalkMediator> talk_mediator_;
+ // The SyncNotifier which notifies us when updates need to be downloaded.
+ sync_notifier::SyncNotifier* sync_notifier_;
// A multi-purpose status watch object that aggregates stats from various
// sync components.
@@ -1482,17 +1548,6 @@ class SyncManager::SyncInternal
// TRANSACTION_COMPLETE step by HandleTransactionCompleteChangeEvent.
ChangeReorderBuffer change_buffers_[syncable::MODEL_TYPE_COUNT];
- // Bit vector keeping track of which models need to have their
- // OnChangesComplete observer set.
- //
- // Set by HandleTransactionEndingChangeEvent, cleared in
- // HandleTransactionCompleteChangeEvent.
- std::bitset<syncable::MODEL_TYPE_COUNT> model_has_change_;
-
- // The event listener hookup that is registered for HandleChangeEvent.
- scoped_ptr<browser_sync::ChannelHookup<syncable::DirectoryChangeEvent> >
- dir_change_hookup_;
-
// Event listener hookup for the ServerConnectionManager.
scoped_ptr<EventListenerHookup> connection_manager_hookup_;
@@ -1503,9 +1558,6 @@ class SyncManager::SyncInternal
// The instance is shared between the SyncManager and the Syncer.
ModelSafeWorkerRegistrar* registrar_;
- // True if the next SyncCycle should notify peers of an update.
- bool notification_pending_;
-
// Set to true once Init has been called, and we know of an authenticated
// valid) username either from a fresh authentication attempt (as in
// first-use case) or from a previous attempt stored in our UserSettings
@@ -1515,17 +1567,15 @@ class SyncManager::SyncInternal
bool initialized_;
mutable base::Lock initialized_mutex_;
- notifier::NotifierOptions notifier_options_;
-
// True if the SyncManager should be running in test mode (no syncer thread
// actually communicating with the server).
bool setup_for_test_mode_;
- syncable::ModelTypeSet enabled_types_;
-
ScopedRunnableMethodFactory<SyncManager::SyncInternal> method_factory_;
- sync_notifier::ServerNotifierThread* server_notifier_thread_;
+ // Map used to store the notification info to be displayed in about:sync page.
+ // TODO(lipalani) - prefill the map with enabled data types.
+ NotificationInfoMap notification_info_map_;
};
const int SyncManager::SyncInternal::kDefaultNudgeDelayMilliseconds = 200;
const int SyncManager::SyncInternal::kPreferencesNudgeDelayMilliseconds = 2000;
@@ -1544,7 +1594,7 @@ bool SyncManager::Init(const FilePath& database_location,
ModelSafeWorkerRegistrar* registrar,
const char* user_agent,
const SyncCredentials& credentials,
- const notifier::NotifierOptions& notifier_options,
+ sync_notifier::SyncNotifier* sync_notifier,
const std::string& restored_key_for_bootstrapping,
bool setup_for_test_mode) {
DCHECK(post_factory);
@@ -1558,7 +1608,7 @@ bool SyncManager::Init(const FilePath& database_location,
registrar,
user_agent,
credentials,
- notifier_options,
+ sync_notifier,
restored_key_for_bootstrapping,
setup_for_test_mode);
}
@@ -1567,8 +1617,8 @@ void SyncManager::UpdateCredentials(const SyncCredentials& credentials) {
data_->UpdateCredentials(credentials);
}
-void SyncManager::UpdateEnabledTypes(const syncable::ModelTypeSet& types) {
- data_->UpdateEnabledTypes(types);
+void SyncManager::UpdateEnabledTypes() {
+ data_->UpdateEnabledTypes();
}
@@ -1615,35 +1665,27 @@ bool SyncManager::IsUsingExplicitPassphrase() {
return data_ && data_->IsUsingExplicitPassphrase();
}
-bool SyncManager::RequestPause() {
- if (data_->syncer_thread())
- return data_->syncer_thread()->RequestPause();
- return false;
-}
-
-bool SyncManager::RequestResume() {
- if (data_->syncer_thread())
- return data_->syncer_thread()->RequestResume();
- return false;
-}
-
-void SyncManager::RequestNudge() {
- if (data_->syncer_thread())
- data_->syncer_thread()->NudgeSyncer(0, SyncerThread::kLocal);
+void SyncManager::RequestNudge(const tracked_objects::Location& location) {
+ data_->RequestNudge(location);
}
void SyncManager::RequestClearServerData() {
if (data_->syncer_thread())
- data_->syncer_thread()->NudgeSyncer(0, SyncerThread::kClearPrivateData);
+ data_->syncer_thread()->ScheduleClearUserData();
}
void SyncManager::RequestConfig(const syncable::ModelTypeBitSet& types) {
if (!data_->syncer_thread())
return;
- // It is an error for this to be called if new_impl is null.
- data_->syncer_thread()->new_impl()->Start(
- browser_sync::s3::SyncerThread::CONFIGURATION_MODE);
- data_->syncer_thread()->new_impl()->ScheduleConfig(types);
+ StartConfigurationMode(NULL);
+ data_->syncer_thread()->ScheduleConfig(types);
+}
+
+void SyncManager::StartConfigurationMode(ModeChangeCallback* callback) {
+ if (!data_->syncer_thread())
+ return;
+ data_->syncer_thread()->Start(
+ browser_sync::SyncerThread::CONFIGURATION_MODE, callback);
}
const std::string& SyncManager::GetAuthenticatedUsername() {
@@ -1660,7 +1702,7 @@ bool SyncManager::SyncInternal::Init(
ModelSafeWorkerRegistrar* model_safe_worker_registrar,
const char* user_agent,
const SyncCredentials& credentials,
- const notifier::NotifierOptions& notifier_options,
+ sync_notifier::SyncNotifier* sync_notifier,
const std::string& restored_key_for_bootstrapping,
bool setup_for_test_mode) {
@@ -1668,20 +1710,21 @@ bool SyncManager::SyncInternal::Init(
core_message_loop_ = MessageLoop::current();
DCHECK(core_message_loop_);
- notifier_options_ = notifier_options;
registrar_ = model_safe_worker_registrar;
setup_for_test_mode_ = setup_for_test_mode;
+ sync_notifier_ = sync_notifier;
+ sync_notifier_->AddObserver(this);
+
share_.dir_manager.reset(new DirectoryManager(database_location));
connection_manager_.reset(new SyncAPIServerConnectionManager(
sync_server_and_path, port, use_ssl, user_agent, post_factory));
- connection_manager_hookup_.reset(
- NewEventListenerHookup(connection_manager()->channel(), this,
- &SyncManager::SyncInternal::HandleServerConnectionEvent));
-
net::NetworkChangeNotifier::AddIPAddressObserver(this);
+
+ connection_manager()->AddListener(this);
+
// TODO(akalin): CheckServerReachable() can block, which may cause jank if we
// try to shut down sync. Fix this.
core_message_loop_->PostTask(FROM_HERE,
@@ -1701,15 +1744,19 @@ bool SyncManager::SyncInternal::Init(
listeners);
context->set_account_name(credentials.email);
// The SyncerThread takes ownership of |context|.
- syncer_thread_.reset(new SyncerThreadAdapter(context,
- CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kNewSyncerThread)));
+ syncer_thread_.reset(new SyncerThread(context, new Syncer()));
}
bool signed_in = SignIn(credentials);
+ if (signed_in && syncer_thread()) {
+ syncer_thread()->Start(
+ browser_sync::SyncerThread::CONFIGURATION_MODE, NULL);
+ }
+
// Do this once the directory is opened.
BootstrapEncryption(restored_key_for_bootstrapping);
+ MarkAndNotifyInitializationComplete();
return signed_in;
}
@@ -1724,12 +1771,13 @@ void SyncManager::SyncInternal::BootstrapEncryption(
if (!lookup->initial_sync_ended_for_type(syncable::NIGORI))
return;
- Cryptographer* cryptographer = share_.dir_manager->cryptographer();
- cryptographer->Bootstrap(restored_key_for_bootstrapping);
-
sync_pb::NigoriSpecifics nigori;
{
+ // Cryptographer should only be accessed while holding a transaction.
ReadTransaction trans(GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
+ cryptographer->Bootstrap(restored_key_for_bootstrapping);
+
ReadNode node(&trans);
if (!node.InitByTagLookup(kNigoriTag)) {
NOTREACHED();
@@ -1757,11 +1805,12 @@ void SyncManager::SyncInternal::BootstrapEncryption(
}
void SyncManager::SyncInternal::StartSyncing() {
+ // Start the syncer thread. This won't actually
+ // result in any syncing until at least the
+ // DirectoryManager broadcasts the OPENED event,
+ // and a valid server connection is detected.
if (syncer_thread()) // NULL during certain unittests.
- syncer_thread()->Start(); // Start the syncer thread. This won't actually
- // result in any syncing until at least the
- // DirectoryManager broadcasts the OPENED event,
- // and a valid server connection is detected.
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
}
void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() {
@@ -1782,38 +1831,14 @@ void SyncManager::SyncInternal::MarkAndNotifyInitializationComplete() {
OnInitializationComplete());
}
-void SyncManager::SyncInternal::SendPendingXMPPNotification(
- bool new_pending_notification) {
+void SyncManager::SyncInternal::SendNotification() {
DCHECK_EQ(MessageLoop::current(), core_message_loop_);
- DCHECK_NE(notifier_options_.notification_method,
- notifier::NOTIFICATION_SERVER);
- notification_pending_ = notification_pending_ || new_pending_notification;
- if (!notification_pending_) {
- VLOG(1) << "Not sending notification: no pending notification";
- return;
- }
- if (!talk_mediator()) {
- VLOG(1) << "Not sending notification: shutting down (talk_mediator_ is "
- "NULL)";
+ if (!sync_notifier_) {
+ VLOG(1) << "Not sending notification: sync_notifier_ is NULL";
return;
}
- VLOG(1) << "Sending XMPP notification...";
- OutgoingNotificationData notification_data;
- notification_data.service_id = browser_sync::kSyncServiceId;
- notification_data.service_url = browser_sync::kSyncServiceUrl;
- notification_data.send_content = true;
- notification_data.priority = browser_sync::kSyncPriority;
- notification_data.write_to_cache_only = true;
- notification_data.service_specific_data =
- browser_sync::kSyncServiceSpecificData;
- notification_data.require_subscription = true;
- bool success = talk_mediator()->SendNotification(notification_data);
- if (success) {
- notification_pending_ = false;
- VLOG(1) << "Sent XMPP notification";
- } else {
- VLOG(1) << "Could not send XMPP notification";
- }
+ allstatus_.IncrementNotificationsSent();
+ sync_notifier_->SendNotification();
}
bool SyncManager::SyncInternal::OpenDirectory() {
@@ -1838,11 +1863,7 @@ bool SyncManager::SyncInternal::OpenDirectory() {
connection_manager()->set_client_id(lookup->cache_guid());
- if (syncer_thread())
- syncer_thread()->CreateSyncer(username_for_share());
-
- MarkAndNotifyInitializationComplete();
- dir_change_hookup_.reset(lookup->AddChangeObserver(this));
+ lookup->SetChangeListener(this);
return true;
}
@@ -1855,9 +1876,24 @@ bool SyncManager::SyncInternal::SignIn(const SyncCredentials& credentials) {
if (!OpenDirectory())
return false;
- if (!setup_for_test_mode_) {
- UpdateCredentials(credentials);
+ // Retrieve and set the sync notifier state. This should be done
+ // only after OpenDirectory is called.
+ syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
+ std::string state;
+ if (lookup.good()) {
+ state = lookup->GetAndClearNotificationState();
+ } else {
+ LOG(ERROR) << "Could not read notification state";
+ }
+ if (VLOG_IS_ON(1)) {
+ std::string encoded_state;
+ base::Base64Encode(state, &encoded_state);
+ VLOG(1) << "Read notification state: " << encoded_state;
}
+ sync_notifier_->SetState(state);
+
+ UpdateCredentials(credentials);
+ UpdateEnabledTypes();
return true;
}
@@ -1865,64 +1901,26 @@ void SyncManager::SyncInternal::UpdateCredentials(
const SyncCredentials& credentials) {
DCHECK_EQ(MessageLoop::current(), core_message_loop_);
DCHECK_EQ(credentials.email, share_.name);
+ DCHECK(!credentials.email.empty());
+ DCHECK(!credentials.sync_token.empty());
connection_manager()->set_auth_token(credentials.sync_token);
- TalkMediatorLogin(credentials.email, credentials.sync_token);
- CheckServerReachable();
- // TODO(tim): Why is this nudge necessary? Possibly just to realize that
- // our credentials are invalid (may have been cached, etc), rather than
- // wait until a sync needs to happen. Not sure that justifies it...
- sync_manager_->RequestNudge();
-}
-
-void SyncManager::SyncInternal::UpdateEnabledTypes(
- const syncable::ModelTypeSet& types) {
- DCHECK_EQ(MessageLoop::current(), core_message_loop_);
-
- enabled_types_ = types;
- if (server_notifier_thread_ != NULL) {
- server_notifier_thread_->UpdateEnabledTypes(types);
+ sync_notifier_->UpdateCredentials(
+ credentials.email, credentials.sync_token);
+ if (!setup_for_test_mode_) {
+ CheckServerReachable();
}
}
-void SyncManager::SyncInternal::InitializeTalkMediator() {
- if (notifier_options_.notification_method ==
- notifier::NOTIFICATION_SERVER) {
- syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
- std::string state;
- if (lookup.good())
- state = lookup->GetAndClearNotificationState();
- else
- LOG(ERROR) << "Could not read notification state";
- if (VLOG_IS_ON(1)) {
- std::string encoded_state;
- base::Base64Encode(state, &encoded_state);
- VLOG(1) << "Read notification state: " << encoded_state;
- }
-
- // |talk_mediator_| takes ownership of |sync_notifier_thread_|
- // but it is. guaranteed that |sync_notifier_thread_| is destroyed only
- // when |talk_mediator_| is (see the comments in talk_mediator.h).
- server_notifier_thread_ = new sync_notifier::ServerNotifierThread(
- notifier_options_, state, this);
- talk_mediator_.reset(
- new TalkMediatorImpl(server_notifier_thread_,
- notifier_options_.invalidate_xmpp_login,
- notifier_options_.allow_insecure_connection));
-
- // Since we may be initialized more than once, make sure that any
- // newly created server notifier thread has the latest enabled types.
- server_notifier_thread_->UpdateEnabledTypes(enabled_types_);
- } else {
- notifier::MediatorThread* mediator_thread =
- new notifier::MediatorThreadImpl(notifier_options_);
- talk_mediator_.reset(
- new TalkMediatorImpl(mediator_thread,
- notifier_options_.invalidate_xmpp_login,
- notifier_options_.allow_insecure_connection));
- talk_mediator_->AddSubscribedServiceUrl(browser_sync::kSyncServiceUrl);
- server_notifier_thread_ = NULL;
+void SyncManager::SyncInternal::UpdateEnabledTypes() {
+ DCHECK_EQ(MessageLoop::current(), core_message_loop_);
+ ModelSafeRoutingInfo routes;
+ registrar_->GetModelSafeRoutingInfo(&routes);
+ syncable::ModelTypeSet enabled_types;
+ for (ModelSafeRoutingInfo::const_iterator it = routes.begin();
+ it != routes.end(); ++it) {
+ enabled_types.insert(it->first);
}
- talk_mediator()->SetDelegate(this);
+ sync_notifier_->UpdateEnabledTypes(enabled_types);
}
void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
@@ -1931,9 +1929,9 @@ void SyncManager::SyncInternal::RaiseAuthNeededEvent() {
OnAuthError(AuthError(AuthError::INVALID_GAIA_CREDENTIALS)));
}
-void SyncManager::SyncInternal::SetUsingExplicitPassphrasePrefForMigration() {
- WriteTransaction trans(&share_);
- WriteNode node(&trans);
+void SyncManager::SyncInternal::SetUsingExplicitPassphrasePrefForMigration(
+ WriteTransaction* const trans) {
+ WriteNode node(trans);
if (!node.InitByTagLookup(kNigoriTag)) {
// TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
NOTREACHED();
@@ -1946,8 +1944,11 @@ void SyncManager::SyncInternal::SetUsingExplicitPassphrasePrefForMigration() {
void SyncManager::SyncInternal::SetPassphrase(
const std::string& passphrase, bool is_explicit) {
- Cryptographer* cryptographer = dir_manager()->cryptographer();
+ // All accesses to the cryptographer are protected by a transaction.
+ WriteTransaction trans(GetUserShare());
+ Cryptographer* cryptographer = trans.GetCryptographer();
KeyParams params = {"localhost", "dummy", passphrase};
+
if (cryptographer->has_pending_keys()) {
if (!cryptographer->DecryptPendingKeys(params)) {
VLOG(1) << "Passphrase failed to decrypt pending keys.";
@@ -1960,14 +1961,13 @@ void SyncManager::SyncInternal::SetPassphrase(
// since the protocol changed to store passphrase preferences in the cloud,
// make sure we update this preference. See bug 62103.
if (is_explicit)
- SetUsingExplicitPassphrasePrefForMigration();
+ SetUsingExplicitPassphrasePrefForMigration(&trans);
// Nudge the syncer so that encrypted datatype updates that were waiting for
// this passphrase get applied as soon as possible.
- sync_manager_->RequestNudge();
+ RequestNudge(FROM_HERE);
} else {
VLOG(1) << "No pending keys, adding provided passphrase.";
- WriteTransaction trans(GetUserShare());
WriteNode node(&trans);
if (!node.InitByTagLookup(kNigoriTag)) {
// TODO(albertb): Plumb an UnrecoverableError all the way back to the PSS.
@@ -2048,6 +2048,52 @@ void SyncManager::SyncInternal::EncryptDataTypes(
return;
}
+namespace {
+
+void FindChildNodesContainingString(const std::string& lowercase_query,
+ const ReadNode& parent_node,
+ sync_api::ReadTransaction* trans,
+ ListValue* result) {
+ int64 child_id = parent_node.GetFirstChildId();
+ while (child_id != kInvalidId) {
+ ReadNode node(trans);
+ if (node.InitByIdLookup(child_id)) {
+ if (node.ContainsString(lowercase_query)) {
+ result->Append(new StringValue(base::Int64ToString(child_id)));
+ }
+ FindChildNodesContainingString(lowercase_query, node, trans, result);
+ child_id = node.GetSuccessorId();
+ } else {
+ LOG(WARNING) << "Lookup of node failed. Id: " << child_id;
+ return;
+ }
+ }
+}
+} // namespace
+
+// Returned pointer owned by the caller.
+ListValue* SyncManager::SyncInternal::FindNodesContainingString(
+ const std::string& query) {
+ // Convert the query string to lower case to perform case insensitive
+ // searches.
+ std::string lowercase_query = query;
+ StringToLowerASCII(&lowercase_query);
+ ReadTransaction trans(GetUserShare());
+ ReadNode root(&trans);
+ root.InitByRootLookup();
+
+ ListValue* result = new ListValue();
+
+ base::Time start_time = base::Time::Now();
+ FindChildNodesContainingString(lowercase_query, root, &trans, result);
+ base::Time end_time = base::Time::Now();
+
+ base::TimeDelta delta = end_time - start_time;
+ VLOG(1) << "Time taken in milliseconds to search " << delta.InMilliseconds();
+
+ return result;
+}
+
void SyncManager::SyncInternal::ReEncryptEverything(WriteTransaction* trans) {
syncable::ModelTypeSet encrypted_types =
GetEncryptedDataTypes(trans->GetWrappedTrans());
@@ -2140,36 +2186,24 @@ void SyncManager::Shutdown() {
void SyncManager::SyncInternal::Shutdown() {
method_factory_.RevokeAll();
- // We NULL out talk_mediator_ so that any tasks pumped below do not
- // trigger further XMPP actions.
- //
- // TODO(akalin): NULL the other member variables defensively, too.
- scoped_ptr<TalkMediator> talk_mediator(talk_mediator_.release());
-
if (syncer_thread()) {
- if (!syncer_thread()->Stop(kThreadExitTimeoutMsec)) {
- LOG(FATAL) << "Unable to stop the syncer, it won't be happy...";
- }
+ syncer_thread()->Stop();
syncer_thread_.reset();
}
- // Shutdown the xmpp buzz connection.
- if (talk_mediator.get()) {
- VLOG(1) << "P2P: Mediator logout started.";
- talk_mediator->Logout();
- VLOG(1) << "P2P: Mediator logout completed.";
- talk_mediator.reset();
-
- // |server_notifier_thread_| is owned by |talk_mediator|. We NULL
- // it out here so as to not have a dangling pointer.
- server_notifier_thread_= NULL;
- VLOG(1) << "P2P: Mediator destroyed.";
+ // We NULL out sync_notifer_ so that any pending tasks do not
+ // trigger further notifications.
+ // TODO(akalin): NULL the other member variables defensively, too.
+ if (sync_notifier_) {
+ sync_notifier_->RemoveObserver(this);
}
- // Pump any messages the auth watcher, syncer thread, or talk
- // mediator posted before they shut down. (See OnSyncEngineEvent(),
- // and HandleTalkMediatorEvent() for the
- // events that may be posted.)
+ // |this| is about to be destroyed, so we have to ensure any messages
+ // that were posted to core_thread_ before or during syncer thread shutdown
+ // are flushed out, else they refer to garbage memory. SendNotification
+ // is an example.
+ // TODO(tim): Remove this monstrosity, perhaps with ObserverListTS once core
+ // thread is removed. Bug 78190.
{
CHECK(core_message_loop_);
bool old_state = core_message_loop_->NestableTasksAllowed();
@@ -2191,9 +2225,6 @@ void SyncManager::SyncInternal::Shutdown() {
// handles to backing files.
share_.dir_manager.reset();
- // We don't want to process any more events.
- dir_change_hookup_.reset();
-
core_message_loop_ = NULL;
}
@@ -2215,50 +2246,16 @@ void SyncManager::SyncInternal::OnIPAddressChangedImpl() {
// TODO(akalin): CheckServerReachable() can block, which may cause
// jank if we try to shut down sync. Fix this.
connection_manager()->CheckServerReachable();
- sync_manager_->RequestNudge();
+ RequestNudge(FROM_HERE);
}
-// Listen to model changes, filter out ones initiated by the sync API, and
-// saves the rest (hopefully just backend Syncer changes resulting from
-// ApplyUpdates) to data_->changelist.
-void SyncManager::SyncInternal::HandleChannelEvent(
- const syncable::DirectoryChangeEvent& event) {
- if (event.todo == syncable::DirectoryChangeEvent::TRANSACTION_COMPLETE) {
- // Safe to perform slow I/O operations now, go ahead and commit.
- HandleTransactionCompleteChangeEvent(event);
- return;
- } else if (event.todo == syncable::DirectoryChangeEvent::TRANSACTION_ENDING) {
- HandleTransactionEndingChangeEvent(event);
- return;
- } else if (event.todo == syncable::DirectoryChangeEvent::CALCULATE_CHANGES) {
- if (event.writer == syncable::SYNCAPI) {
- HandleCalculateChangesChangeEventFromSyncApi(event);
- return;
- }
- HandleCalculateChangesChangeEventFromSyncer(event);
- return;
- } else if (event.todo == syncable::DirectoryChangeEvent::SHUTDOWN) {
- dir_change_hookup_.reset();
- }
-}
-
-void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent(
- const syncable::DirectoryChangeEvent& event) {
- // This notification happens immediately after the channel mutex is released
- // This allows work to be performed without holding the WriteTransaction lock
- // but before the transaction is finished.
- DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::TRANSACTION_COMPLETE);
- if (observers_.size() <= 0)
- return;
-
- // Call commit
- for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
- if (model_has_change_.test(i)) {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnChangesComplete(syncable::ModelTypeFromInt(i)));
- model_has_change_.reset(i);
- }
- }
+void SyncManager::SyncInternal::OnServerConnectionEvent(
+ const ServerConnectionEvent2& event) {
+ ServerConnectionEvent legacy;
+ legacy.what_happened = ServerConnectionEvent::STATUS_CHANGED;
+ legacy.connection_code = event.connection_code;
+ legacy.server_reachable = event.server_reachable;
+ HandleServerConnectionEvent(legacy);
}
void SyncManager::SyncInternal::HandleServerConnectionEvent(
@@ -2279,55 +2276,74 @@ void SyncManager::SyncInternal::HandleServerConnectionEvent(
}
}
-void SyncManager::SyncInternal::HandleTransactionEndingChangeEvent(
- const syncable::DirectoryChangeEvent& event) {
+void SyncManager::SyncInternal::HandleTransactionCompleteChangeEvent(
+ const syncable::ModelTypeBitSet& models_with_changes) {
+ // This notification happens immediately after the transaction mutex is
+ // released. This allows work to be performed without blocking other threads
+ // from acquiring a transaction.
+ if (observers_.size() <= 0)
+ return;
+
+ // Call commit.
+ for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
+ if (models_with_changes.test(i)) {
+ FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
+ OnChangesComplete(syncable::ModelTypeFromInt(i)));
+ }
+ }
+}
+
+ModelTypeBitSet SyncManager::SyncInternal::HandleTransactionEndingChangeEvent(
+ syncable::BaseTransaction* trans) {
// This notification happens immediately before a syncable WriteTransaction
// falls out of scope. It happens while the channel mutex is still held,
// and while the transaction mutex is held, so it cannot be re-entrant.
- DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::TRANSACTION_ENDING);
if (observers_.size() <= 0 || ChangeBuffersAreEmpty())
- return;
+ return ModelTypeBitSet();
// This will continue the WriteTransaction using a read only wrapper.
// This is the last chance for read to occur in the WriteTransaction
// that's closing. This special ReadTransaction will not close the
// underlying transaction.
- ReadTransaction trans(GetUserShare(), event.trans);
+ ReadTransaction read_trans(GetUserShare(), trans);
+ syncable::ModelTypeBitSet models_with_changes;
for (int i = 0; i < syncable::MODEL_TYPE_COUNT; ++i) {
if (change_buffers_[i].IsEmpty())
continue;
vector<ChangeRecord> ordered_changes;
- change_buffers_[i].GetAllChangesInTreeOrder(&trans, &ordered_changes);
+ change_buffers_[i].GetAllChangesInTreeOrder(&read_trans, &ordered_changes);
if (!ordered_changes.empty()) {
FOR_EACH_OBSERVER(
SyncManager::Observer, observers_,
- OnChangesApplied(syncable::ModelTypeFromInt(i), &trans,
+ OnChangesApplied(syncable::ModelTypeFromInt(i), &read_trans,
&ordered_changes[0], ordered_changes.size()));
- model_has_change_.set(i, true);
+ models_with_changes.set(i, true);
}
change_buffers_[i].Clear();
}
+ return models_with_changes;
}
void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi(
- const syncable::DirectoryChangeEvent& event) {
- // We have been notified about a user action changing the bookmark model.
- DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::CALCULATE_CHANGES);
- DCHECK(event.writer == syncable::SYNCAPI ||
- event.writer == syncable::UNITTEST);
+ const OriginalEntries& originals,
+ const WriterTag& writer,
+ syncable::BaseTransaction* trans) {
+ // We have been notified about a user action changing a sync model.
+ DCHECK(writer == syncable::SYNCAPI ||
+ writer == syncable::UNITTEST);
LOG_IF(WARNING, !ChangeBuffersAreEmpty()) <<
"CALCULATE_CHANGES called with unapplied old changes.";
bool exists_unsynced_items = false;
bool only_preference_changes = true;
syncable::ModelTypeBitSet model_types;
- for (syncable::OriginalEntries::const_iterator i = event.originals->begin();
- i != event.originals->end() && !exists_unsynced_items;
+ for (syncable::OriginalEntries::const_iterator i = originals.begin();
+ i != originals.end() && !exists_unsynced_items;
++i) {
int64 id = i->ref(syncable::META_HANDLE);
- syncable::Entry e(event.trans, syncable::GET_BY_HANDLE, id);
+ syncable::Entry e(trans, syncable::GET_BY_HANDLE, id);
DCHECK(e.good());
syncable::ModelType model_type = e.GetModelType();
@@ -2349,10 +2365,12 @@ void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncApi(
if (exists_unsynced_items && syncer_thread()) {
int nudge_delay = only_preference_changes ?
kPreferencesNudgeDelayMilliseconds : kDefaultNudgeDelayMilliseconds;
- syncer_thread()->NudgeSyncerWithDataTypes(
- nudge_delay,
- SyncerThread::kLocal,
- model_types);
+ core_message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SyncInternal::RequestNudgeWithDataTypes,
+ TimeDelta::FromMilliseconds(nudge_delay),
+ browser_sync::NUDGE_SOURCE_LOCAL,
+ model_types,
+ FROM_HERE));
}
}
@@ -2387,19 +2405,21 @@ void SyncManager::SyncInternal::SetExtraChangeRecordData(int64 id,
}
void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer(
- const syncable::DirectoryChangeEvent& event) {
+ const OriginalEntries& originals,
+ const WriterTag& writer,
+ syncable::BaseTransaction* trans) {
// We only expect one notification per sync step, so change_buffers_ should
// contain no pending entries.
- DCHECK_EQ(event.todo, syncable::DirectoryChangeEvent::CALCULATE_CHANGES);
- DCHECK(event.writer == syncable::SYNCER ||
- event.writer == syncable::UNITTEST);
+ DCHECK(writer == syncable::SYNCER ||
+ writer == syncable::UNITTEST);
LOG_IF(WARNING, !ChangeBuffersAreEmpty()) <<
"CALCULATE_CHANGES called with unapplied old changes.";
- for (syncable::OriginalEntries::const_iterator i = event.originals->begin();
- i != event.originals->end(); ++i) {
+ Cryptographer* crypto = dir_manager()->GetCryptographer(trans);
+ for (syncable::OriginalEntries::const_iterator i = originals.begin();
+ i != originals.end(); ++i) {
int64 id = i->ref(syncable::META_HANDLE);
- syncable::Entry e(event.trans, syncable::GET_BY_HANDLE, id);
+ syncable::Entry e(trans, syncable::GET_BY_HANDLE, id);
bool existed_before = !i->ref(syncable::IS_DEL);
bool exists_now = e.good() && !e.Get(syncable::IS_DEL);
DCHECK(e.good());
@@ -2414,12 +2434,11 @@ void SyncManager::SyncInternal::HandleCalculateChangesChangeEventFromSyncer(
else if (!exists_now && existed_before)
change_buffers_[type].PushDeletedItem(id);
else if (exists_now && existed_before &&
- VisiblePropertiesDiffer(*i, e, dir_manager()->cryptographer())) {
+ VisiblePropertiesDiffer(*i, e, crypto)) {
change_buffers_[type].PushUpdatedItem(id, VisiblePositionsDiffer(*i, e));
}
- SetExtraChangeRecordData(id, type, &change_buffers_[type],
- dir_manager()->cryptographer(), *i,
+ SetExtraChangeRecordData(id, type, &change_buffers_[type], crypto, *i,
existed_before, exists_now);
}
}
@@ -2428,6 +2447,22 @@ SyncManager::Status SyncManager::SyncInternal::GetStatus() {
return allstatus_.status();
}
+void SyncManager::SyncInternal::RequestNudge(
+ const tracked_objects::Location& location) {
+ if (syncer_thread())
+ syncer_thread()->ScheduleNudge(
+ TimeDelta::FromMilliseconds(0), browser_sync::NUDGE_SOURCE_LOCAL,
+ ModelTypeBitSet(), location);
+}
+
+void SyncManager::SyncInternal::RequestNudgeWithDataTypes(
+ const TimeDelta& delay,
+ browser_sync::NudgeSource source, const ModelTypeBitSet& types,
+ const tracked_objects::Location& nudge_location) {
+ if (syncer_thread())
+ syncer_thread()->ScheduleNudge(delay, source, types, nudge_location);
+}
+
void SyncManager::SyncInternal::OnSyncEngineEvent(
const SyncEngineEvent& event) {
if (observers_.size() <= 0)
@@ -2459,8 +2494,7 @@ void SyncManager::SyncInternal::OnSyncEngineEvent(
if (enabled_types.count(syncable::PASSWORDS) > 0)
encrypted_types.insert(syncable::PASSWORDS);
if (!encrypted_types.empty()) {
- Cryptographer* cryptographer =
- GetUserShare()->dir_manager->cryptographer();
+ Cryptographer* cryptographer = trans.GetCryptographer();
if (!cryptographer->is_ready() && !cryptographer->has_pending_keys()) {
if (!nigori.encrypted().blob().empty()) {
DCHECK(!cryptographer->CanDecrypt(nigori.encrypted()));
@@ -2491,33 +2525,22 @@ void SyncManager::SyncInternal::OnSyncEngineEvent(
OnSyncCycleCompleted(event.snapshot));
}
- if (notifier_options_.notification_method !=
- notifier::NOTIFICATION_SERVER) {
- // TODO(chron): Consider changing this back to track has_more_to_sync
- // only notify peers if a successful commit has occurred.
- bool new_pending_notification =
- (event.snapshot->syncer_status.num_successful_commits > 0);
+ // This is here for tests, which are still using p2p notifications.
+ // SendNotification does not do anything if we are using server based
+ // notifications.
+ // TODO(chron): Consider changing this back to track has_more_to_sync
+ // only notify peers if a successful commit has occurred.
+ bool new_notification =
+ (event.snapshot->syncer_status.num_successful_commits > 0);
+ if (new_notification) {
core_message_loop_->PostTask(
FROM_HERE,
NewRunnableMethod(
this,
- &SyncManager::SyncInternal::SendPendingXMPPNotification,
- new_pending_notification));
+ &SyncManager::SyncInternal::SendNotification));
}
}
- if (event.what_happened == SyncEngineEvent::SYNCER_THREAD_PAUSED) {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnPaused());
- return;
- }
-
- if (event.what_happened == SyncEngineEvent::SYNCER_THREAD_RESUMED) {
- FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
- OnResumed());
- return;
- }
-
if (event.what_happened == SyncEngineEvent::STOP_SYNCING_PERMANENTLY) {
FOR_EACH_OBSERVER(SyncManager::Observer, observers_,
OnStopSyncingPermanently());
@@ -2583,6 +2606,16 @@ void SyncManager::SyncInternal::ProcessMessage(
parent_router_->RouteJsEvent(
"onGetNotificationStateFinished",
browser_sync::JsArgList(return_args), sender);
+ } else if (name == "getNotificationInfo") {
+ if (!parent_router_) {
+ LogNoRouter(name, args);
+ return;
+ }
+
+ ListValue return_args;
+ return_args.Append(NotificationInfoToValue(notification_info_map_));
+ parent_router_->RouteJsEvent("onGetNotificationInfoFinished",
+ browser_sync::JsArgList(return_args), sender);
} else if (name == "getRootNode") {
if (!parent_router_) {
LogNoRouter(name, args);
@@ -2603,6 +2636,14 @@ void SyncManager::SyncInternal::ProcessMessage(
}
parent_router_->RouteJsEvent(
"onGetNodeByIdFinished", ProcessGetNodeByIdMessage(args), sender);
+ } else if (name == "findNodesContainingString") {
+ if (!parent_router_) {
+ LogNoRouter(name, args);
+ return;
+ }
+ parent_router_->RouteJsEvent(
+ "onFindNodesContainingStringFinished",
+ ProcessFindNodesContainingString(args), sender);
} else {
VLOG(1) << "Dropping unknown message " << name
<< " with args " << args.ToString();
@@ -2635,13 +2676,28 @@ browser_sync::JsArgList SyncManager::SyncInternal::ProcessGetNodeByIdMessage(
return browser_sync::JsArgList(return_args);
}
+browser_sync::JsArgList SyncManager::SyncInternal::
+ ProcessFindNodesContainingString(
+ const browser_sync::JsArgList& args) {
+ std::string query;
+ ListValue return_args;
+ if (!args.Get().GetString(0, &query)) {
+ return_args.Append(new ListValue());
+ return browser_sync::JsArgList(return_args);
+ }
+
+ ListValue* result = FindNodesContainingString(query);
+ return_args.Append(result);
+ return browser_sync::JsArgList(return_args);
+}
+
void SyncManager::SyncInternal::OnNotificationStateChange(
bool notifications_enabled) {
VLOG(1) << "P2P: Notifications enabled = "
<< (notifications_enabled ? "true" : "false");
allstatus_.SetNotificationsEnabled(notifications_enabled);
if (syncer_thread()) {
- syncer_thread()->SetNotificationsEnabled(notifications_enabled);
+ syncer_thread()->set_notifications_enabled(notifications_enabled);
}
if (parent_router_) {
ListValue args;
@@ -2650,89 +2706,29 @@ void SyncManager::SyncInternal::OnNotificationStateChange(
parent_router_->RouteJsEvent("onSyncNotificationStateChange",
browser_sync::JsArgList(args), NULL);
}
- if ((notifier_options_.notification_method !=
- notifier::NOTIFICATION_SERVER) && notifications_enabled) {
- // Nudge the syncer thread when notifications are enabled, in case there is
- // any data that has not yet been synced. If we are listening to
- // server-issued notifications, we are already guaranteed to receive a
- // notification on a successful connection.
- if (syncer_thread()) {
- syncer_thread()->NudgeSyncer(0, SyncerThread::kLocal);
- }
-
- // Send a notification as soon as subscriptions are on
- // (see http://code.google.com/p/chromium/issues/detail?id=38563 ).
- core_message_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(
- this,
- &SyncManager::SyncInternal::SendPendingXMPPNotification,
- true));
- }
}
-void SyncManager::SyncInternal::TalkMediatorLogin(
- const std::string& email, const std::string& token) {
- DCHECK_EQ(MessageLoop::current(), core_message_loop_);
- DCHECK(!email.empty());
- DCHECK(!token.empty());
- InitializeTalkMediator();
- talk_mediator()->SetAuthToken(email, token, SYNC_SERVICE_NAME);
- talk_mediator()->Login();
+void SyncManager::SyncInternal::UpdateNotificationInfo(
+ const syncable::ModelTypePayloadMap& type_payloads) {
+ for (syncable::ModelTypePayloadMap::const_iterator it = type_payloads.begin();
+ it != type_payloads.end(); ++it) {
+ NotificationInfo* info = &notification_info_map_[it->first];
+ info->total_count++;
+ info->payload = it->second;
+ }
}
void SyncManager::SyncInternal::OnIncomingNotification(
- const IncomingNotificationData& notification_data) {
- browser_sync::sessions::TypePayloadMap model_types_with_payloads;
-
- // Check if the service url is a sync URL. An empty service URL is
- // treated as a legacy sync notification. If we're listening to
- // server-issued notifications, no need to check the service_url.
- if (notifier_options_.notification_method ==
- notifier::NOTIFICATION_SERVER) {
- VLOG(1) << "Sync received server notification from " <<
- notification_data.service_url << ": " <<
- notification_data.service_specific_data;
- syncable::ModelTypeBitSet model_types;
- const std::string& model_type_list = notification_data.service_url;
- const std::string& notification_payload =
- notification_data.service_specific_data;
-
- if (!syncable::ModelTypeBitSetFromString(model_type_list, &model_types)) {
- LOG(DFATAL) << "Could not extract model types from server data.";
- model_types.set();
- }
-
- model_types_with_payloads =
- browser_sync::sessions::MakeTypePayloadMapFromBitSet(model_types,
- notification_payload);
- } else if (notification_data.service_url.empty() ||
- (notification_data.service_url ==
- browser_sync::kSyncLegacyServiceUrl) ||
- (notification_data.service_url ==
- browser_sync::kSyncServiceUrl)) {
- VLOG(1) << "Sync received P2P notification.";
-
- // Catch for sync integration tests (uses p2p). Just set all enabled
- // datatypes.
- ModelSafeRoutingInfo routes;
- registrar_->GetModelSafeRoutingInfo(&routes);
- model_types_with_payloads =
- browser_sync::sessions::MakeTypePayloadMapFromRoutingInfo(routes,
- std::string());
- } else {
- LOG(WARNING) << "Notification fron unexpected source: "
- << notification_data.service_url;
- }
-
- if (!model_types_with_payloads.empty()) {
+ const syncable::ModelTypePayloadMap& type_payloads) {
+ if (!type_payloads.empty()) {
if (syncer_thread()) {
- syncer_thread()->NudgeSyncerWithPayloads(
- kSyncerThreadDelayMsec,
- SyncerThread::kNotification,
- model_types_with_payloads);
+ syncer_thread()->ScheduleNudgeWithPayloads(
+ TimeDelta::FromMilliseconds(kSyncerThreadDelayMsec),
+ browser_sync::NUDGE_SOURCE_NOTIFICATION,
+ type_payloads, FROM_HERE);
}
allstatus_.IncrementNotificationsReceived();
+ UpdateNotificationInfo(type_payloads);
} else {
LOG(WARNING) << "Sync received notification without any type information.";
}
@@ -2741,9 +2737,9 @@ void SyncManager::SyncInternal::OnIncomingNotification(
ListValue args;
ListValue* changed_types = new ListValue();
args.Append(changed_types);
- for (browser_sync::sessions::TypePayloadMap::const_iterator
- it = model_types_with_payloads.begin();
- it != model_types_with_payloads.end(); ++it) {
+ for (syncable::ModelTypePayloadMap::const_iterator
+ it = type_payloads.begin();
+ it != type_payloads.end(); ++it) {
const std::string& model_type_str =
syncable::ModelTypeToString(it->first);
changed_types->Append(Value::CreateStringValue(model_type_str));
@@ -2753,13 +2749,8 @@ void SyncManager::SyncInternal::OnIncomingNotification(
}
}
-void SyncManager::SyncInternal::OnOutgoingNotification() {
- DCHECK_NE(notifier_options_.notification_method,
- notifier::NOTIFICATION_SERVER);
- allstatus_.IncrementNotificationsSent();
-}
-
-void SyncManager::SyncInternal::WriteState(const std::string& state) {
+void SyncManager::SyncInternal::StoreState(
+ const std::string& state) {
syncable::ScopedDirLookup lookup(dir_manager(), username_for_share());
if (!lookup.good()) {
LOG(ERROR) << "Could not write notification state";
@@ -2816,7 +2807,7 @@ BaseTransaction::BaseTransaction(UserShare* share)
DCHECK(share && share->dir_manager.get());
lookup_ = new syncable::ScopedDirLookup(share->dir_manager.get(),
share->name);
- cryptographer_ = share->dir_manager->cryptographer();
+ cryptographer_ = share->dir_manager->GetCryptographer(this);
if (!(lookup_->good()))
DCHECK(false) << "ScopedDirLookup failed on valid DirManager.";
}
@@ -2841,10 +2832,11 @@ void SyncManager::TriggerOnNotificationStateChangeForTest(
void SyncManager::TriggerOnIncomingNotificationForTest(
const syncable::ModelTypeBitSet& model_types) {
- IncomingNotificationData notification_data;
- notification_data.service_url = model_types.to_string();
- // Here we rely on the default notification method being SERVER.
- data_->OnIncomingNotification(notification_data);
+ syncable::ModelTypePayloadMap model_types_with_payloads =
+ syncable::ModelTypePayloadMapFromBitSet(model_types,
+ std::string());
+
+ data_->OnIncomingNotification(model_types_with_payloads);
}
} // namespace sync_api
diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h
index 3629793..1011a29 100644
--- a/chrome/browser/sync/engine/syncapi.h
+++ b/chrome/browser/sync/engine/syncapi.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -43,8 +43,10 @@
#include <vector>
#include "base/basictypes.h"
+#include "base/callback.h"
#include "base/gtest_prod_util.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/tracked.h"
#include "build/build_config.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
#include "chrome/browser/sync/syncable/autofill_migration.h"
@@ -65,9 +67,9 @@ struct SyncSessionSnapshot;
}
}
-namespace notifier {
-struct NotifierOptions;
-}
+namespace sync_notifier {
+class SyncNotifier;
+} // namespace sync_notifier
// Forward declarations of internal class types so that sync API objects
// may have opaque pointers to these types.
@@ -100,7 +102,6 @@ class TypedUrlSpecifics;
namespace sync_api {
-// Forward declarations of classes to be defined later in this file.
class BaseTransaction;
class HttpPostProviderFactory;
class SyncManager;
@@ -256,6 +257,10 @@ class BaseNode {
// Transfers ownership of the DictionaryValue to the caller.
DictionaryValue* ToValue() const;
+ // Does a case in-sensitive search for a given string, which must be
+ // lower case.
+ bool ContainsString(const std::string& lowercase_query) const;
+
protected:
BaseNode();
virtual ~BaseNode();
@@ -803,12 +808,6 @@ class SyncManager {
// message, unless otherwise specified, produces undefined behavior.
virtual void OnInitializationComplete() = 0;
- // The syncer thread has been paused.
- virtual void OnPaused() = 0;
-
- // The syncer thread has been resumed.
- virtual void OnResumed() = 0;
-
// We are no longer permitted to communicate with the server. Sync should
// be disabled and state cleaned up at once. This can happen for a number
// of reasons, e.g. swapping from a test instance to production, or a
@@ -828,6 +827,8 @@ class SyncManager {
virtual ~Observer();
};
+ typedef Callback0::Type ModeChangeCallback;
+
// Create an uninitialized SyncManager. Callers must Init() before using.
SyncManager();
virtual ~SyncManager();
@@ -844,7 +845,7 @@ class SyncManager {
// |model_safe_worker| ownership is given to the SyncManager.
// |user_agent| is a 7-bit ASCII string suitable for use as the User-Agent
// HTTP header. Used internally when collecting stats to classify clients.
- // |notifier_options| contains options specific to sync notifications.
+ // |sync_notifier| used to listen for notifications, not owned.
bool Init(const FilePath& database_location,
const char* sync_server_and_path,
int sync_server_port,
@@ -853,7 +854,7 @@ class SyncManager {
browser_sync::ModelSafeWorkerRegistrar* registrar,
const char* user_agent,
const SyncCredentials& credentials,
- const notifier::NotifierOptions& notifier_options,
+ sync_notifier::SyncNotifier* sync_notifier,
const std::string& restored_key_for_bootstrapping,
bool setup_for_test_mode);
@@ -884,9 +885,8 @@ class SyncManager {
// Update tokens that we're using in Sync. Email must stay the same.
void UpdateCredentials(const SyncCredentials& credentials);
- // Update the set of enabled sync types. Usually called when the user disables
- // or enables a sync type.
- void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
+ // Called when the user disables or enables a sync type.
+ void UpdateEnabledTypes();
// Start the SyncerThread.
// TODO(tim): With the new impl, this would mean starting "NORMAL" operation.
@@ -911,19 +911,11 @@ class SyncManager {
// types, as we do not currently support decrypting datatypes.
void EncryptDataTypes(const syncable::ModelTypeSet& encrypted_types);
- // Requests the syncer thread to pause. The observer's OnPause
- // method will be called when the syncer thread is paused. Returns
- // false if the syncer thread can not be paused (e.g. if it is not
- // started).
- // TODO(tim): Deprecated.
- bool RequestPause();
-
- // Requests the syncer thread to resume. The observer's OnResume
- // method will be called when the syncer thread is resumed. Returns
- // false if the syncer thread can not be resumed (e.g. if it is not
- // paused).
- // TODO(tim): Deprecated.
- bool RequestResume();
+ // Puts the SyncerThread into a mode where no normal nudge or poll traffic
+ // will occur, but calls to RequestConfig will be supported. If |callback|
+ // is provided, it will be invoked (from the internal SyncerThread) when
+ // the thread has changed to configuration mode.
+ void StartConfigurationMode(ModeChangeCallback* callback);
// For the new SyncerThread impl, this switches the mode of operation to
// CONFIGURATION_MODE and schedules a config task to fetch updates for
@@ -932,7 +924,7 @@ class SyncManager {
// Request a nudge of the syncer, which will cause the syncer thread
// to run at the next available opportunity.
- void RequestNudge();
+ void RequestNudge(const tracked_objects::Location& nudge_location);
// Request a clearing of all data on the server
void RequestClearServerData();
@@ -1022,77 +1014,6 @@ class SyncManager {
DISALLOW_COPY_AND_ASSIGN(SyncManager);
};
-// An interface the embedding application (e.g. Chromium) implements to
-// provide required HTTP POST functionality to the syncer backend.
-// This interface is designed for one-time use. You create one, use it, and
-// create another if you want to make a subsequent POST.
-// TODO(timsteele): Bug 1482576. Consider splitting syncapi.h into two files:
-// one for the API defining the exports, which doesn't need to be included from
-// anywhere internally, and another file for the interfaces like this one.
-class HttpPostProviderInterface {
- public:
- HttpPostProviderInterface() { }
- virtual ~HttpPostProviderInterface() { }
-
- // Use specified user agent string when POSTing. If not called a default UA
- // may be used.
- virtual void SetUserAgent(const char* user_agent) = 0;
-
- // Add additional headers to the request.
- virtual void SetExtraRequestHeaders(const char * headers) = 0;
-
- // Set the URL to POST to.
- virtual void SetURL(const char* url, int port) = 0;
-
- // Set the type, length and content of the POST payload.
- // |content_type| is a null-terminated MIME type specifier.
- // |content| is a data buffer; Do not interpret as a null-terminated string.
- // |content_length| is the total number of chars in |content|. It is used to
- // assign/copy |content| data.
- virtual void SetPostPayload(const char* content_type, int content_length,
- const char* content) = 0;
-
- // Returns true if the URL request succeeded. If the request failed,
- // os_error() may be non-zero and hence contain more information.
- virtual bool MakeSynchronousPost(int* os_error_code, int* response_code) = 0;
-
- // Get the length of the content returned in the HTTP response.
- // This does not count the trailing null-terminating character returned
- // by GetResponseContent, so it is analogous to calling string.length.
- virtual int GetResponseContentLength() const = 0;
-
- // Get the content returned in the HTTP response.
- // This is a null terminated string of characters.
- // Value should be copied.
- virtual const char* GetResponseContent() const = 0;
-
- // Get the value of a header returned in the HTTP response.
- // If the header is not present, returns the empty string.
- virtual const std::string GetResponseHeaderValue(
- const std::string& name) const = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(HttpPostProviderInterface);
-};
-
-// A factory to create HttpPostProviders to hide details about the
-// implementations and dependencies.
-// A factory instance itself should be owned by whomever uses it to create
-// HttpPostProviders.
-class HttpPostProviderFactory {
- public:
- // Obtain a new HttpPostProviderInterface instance, owned by caller.
- virtual HttpPostProviderInterface* Create() = 0;
-
- // When the interface is no longer needed (ready to be cleaned up), clients
- // must call Destroy().
- // This allows actual HttpPostProvider subclass implementations to be
- // reference counted, which is useful if a particular implementation uses
- // multiple threads to serve network requests.
- virtual void Destroy(HttpPostProviderInterface* http) = 0;
- virtual ~HttpPostProviderFactory() { }
-};
-
} // namespace sync_api
#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCAPI_H_
diff --git a/chrome/browser/sync/engine/syncapi_unittest.cc b/chrome/browser/sync/engine/syncapi_unittest.cc
index 38b72ab..54c09bd 100644
--- a/chrome/browser/sync/engine/syncapi_unittest.cc
+++ b/chrome/browser/sync/engine/syncapi_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,13 +10,15 @@
#include "base/basictypes.h"
#include "base/format_macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_temp_dir.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/scoped_temp_dir.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "base/values.h"
+#include "chrome/browser/sync/engine/http_post_provider_factory.h"
+#include "chrome/browser/sync/engine/http_post_provider_interface.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/js_arg_list.h"
@@ -24,6 +26,8 @@
#include "chrome/browser/sync/js_event_handler.h"
#include "chrome/browser/sync/js_event_router.h"
#include "chrome/browser/sync/js_test_util.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
#include "chrome/browser/sync/protocol/proto_value_conversions.h"
#include "chrome/browser/sync/sessions/sync_session.h"
@@ -32,10 +36,9 @@
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
#include "chrome/browser/sync/util/cryptographer.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "chrome/test/sync/engine/test_user_share.h"
#include "chrome/test/values_test_util.h"
#include "content/browser/browser_thread.h"
-#include "jingle/notifier/base/notifier_options.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -54,6 +57,7 @@ using syncable::ModelTypeSet;
using test::ExpectDictionaryValue;
using test::ExpectStringValue;
using testing::_;
+using testing::AtLeast;
using testing::Invoke;
using testing::SaveArg;
using testing::StrictMock;
@@ -164,34 +168,29 @@ int64 MakeServerNodeForType(UserShare* share,
class SyncApiTest : public testing::Test {
public:
virtual void SetUp() {
- setter_upper_.SetUp();
- share_.dir_manager.reset(setter_upper_.manager());
- share_.name = setter_upper_.name();
+ test_user_share_.SetUp();
}
virtual void TearDown() {
- // |share_.dir_manager| does not actually own its value.
- ignore_result(share_.dir_manager.release());
- setter_upper_.TearDown();
+ test_user_share_.TearDown();
}
protected:
- UserShare share_;
- browser_sync::TestDirectorySetterUpper setter_upper_;
+ browser_sync::TestUserShare test_user_share_;
};
TEST_F(SyncApiTest, SanityCheckTest) {
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
EXPECT_TRUE(trans.GetWrappedTrans() != NULL);
}
{
- WriteTransaction trans(&share_);
+ WriteTransaction trans(test_user_share_.user_share());
EXPECT_TRUE(trans.GetWrappedTrans() != NULL);
}
{
// No entries but root should exist
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
// Metahandle 1 can be root, sanity check 2
EXPECT_FALSE(node.InitByIdLookup(2));
@@ -200,16 +199,17 @@ TEST_F(SyncApiTest, SanityCheckTest) {
TEST_F(SyncApiTest, BasicTagWrite) {
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode root_node(&trans);
root_node.InitByRootLookup();
EXPECT_EQ(root_node.GetFirstChildId(), 0);
}
- ignore_result(MakeNode(&share_, syncable::BOOKMARKS, "testtag"));
+ ignore_result(MakeNode(test_user_share_.user_share(),
+ syncable::BOOKMARKS, "testtag"));
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS,
"testtag"));
@@ -239,18 +239,21 @@ TEST_F(SyncApiTest, GenerateSyncableHash) {
TEST_F(SyncApiTest, ModelTypesSiloed) {
{
- WriteTransaction trans(&share_);
+ WriteTransaction trans(test_user_share_.user_share());
ReadNode root_node(&trans);
root_node.InitByRootLookup();
EXPECT_EQ(root_node.GetFirstChildId(), 0);
}
- ignore_result(MakeNode(&share_, syncable::BOOKMARKS, "collideme"));
- ignore_result(MakeNode(&share_, syncable::PREFERENCES, "collideme"));
- ignore_result(MakeNode(&share_, syncable::AUTOFILL, "collideme"));
+ ignore_result(MakeNode(test_user_share_.user_share(),
+ syncable::BOOKMARKS, "collideme"));
+ ignore_result(MakeNode(test_user_share_.user_share(),
+ syncable::PREFERENCES, "collideme"));
+ ignore_result(MakeNode(test_user_share_.user_share(),
+ syncable::AUTOFILL, "collideme"));
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode bookmarknode(&trans);
EXPECT_TRUE(bookmarknode.InitByClientTagLookup(syncable::BOOKMARKS,
@@ -272,13 +275,13 @@ TEST_F(SyncApiTest, ModelTypesSiloed) {
TEST_F(SyncApiTest, ReadMissingTagsFails) {
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
"testtag"));
}
{
- WriteTransaction trans(&share_);
+ WriteTransaction trans(test_user_share_.user_share());
WriteNode node(&trans);
EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
"testtag"));
@@ -293,7 +296,7 @@ TEST_F(SyncApiTest, TestDeleteBehavior) {
std::wstring test_title(L"test1");
{
- WriteTransaction trans(&share_);
+ WriteTransaction trans(test_user_share_.user_share());
ReadNode root_node(&trans);
root_node.InitByRootLookup();
@@ -314,7 +317,7 @@ TEST_F(SyncApiTest, TestDeleteBehavior) {
// Ensure we can delete something with a tag.
{
- WriteTransaction trans(&share_);
+ WriteTransaction trans(test_user_share_.user_share());
WriteNode wnode(&trans);
EXPECT_TRUE(wnode.InitByClientTagLookup(syncable::BOOKMARKS,
"testtag"));
@@ -327,7 +330,7 @@ TEST_F(SyncApiTest, TestDeleteBehavior) {
// Lookup of a node which was deleted should return failure,
// but have found some data about the node.
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
EXPECT_FALSE(node.InitByClientTagLookup(syncable::BOOKMARKS,
"testtag"));
@@ -337,7 +340,7 @@ TEST_F(SyncApiTest, TestDeleteBehavior) {
}
{
- WriteTransaction trans(&share_);
+ WriteTransaction trans(test_user_share_.user_share());
ReadNode folder_node(&trans);
EXPECT_TRUE(folder_node.InitByIdLookup(folder_id));
@@ -354,7 +357,7 @@ TEST_F(SyncApiTest, TestDeleteBehavior) {
// Now look up should work.
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
EXPECT_TRUE(node.InitByClientTagLookup(syncable::BOOKMARKS,
"testtag"));
@@ -365,9 +368,12 @@ TEST_F(SyncApiTest, TestDeleteBehavior) {
TEST_F(SyncApiTest, WriteAndReadPassword) {
KeyParams params = {"localhost", "username", "passphrase"};
- share_.dir_manager->cryptographer()->AddKey(params);
{
- WriteTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
+ trans.GetCryptographer()->AddKey(params);
+ }
+ {
+ WriteTransaction trans(test_user_share_.user_share());
ReadNode root_node(&trans);
root_node.InitByRootLookup();
@@ -379,7 +385,7 @@ TEST_F(SyncApiTest, WriteAndReadPassword) {
password_node.SetPasswordSpecifics(data);
}
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode root_node(&trans);
root_node.InitByRootLookup();
@@ -420,25 +426,23 @@ void CheckNodeValue(const BaseNode& node, const DictionaryValue& value) {
ADD_FAILURE();
}
}
- {
- scoped_ptr<DictionaryValue> expected_specifics(
- browser_sync::EntitySpecificsToValue(
- node.GetEntry()->Get(syncable::SPECIFICS)));
- Value* specifics = NULL;
- EXPECT_TRUE(value.Get("specifics", &specifics));
- EXPECT_TRUE(Value::Equals(specifics, expected_specifics.get()));
- }
ExpectInt64Value(node.GetExternalId(), value, "externalId");
ExpectInt64Value(node.GetPredecessorId(), value, "predecessorId");
ExpectInt64Value(node.GetSuccessorId(), value, "successorId");
ExpectInt64Value(node.GetFirstChildId(), value, "firstChildId");
+ {
+ scoped_ptr<DictionaryValue> expected_entry(node.GetEntry()->ToValue());
+ Value* entry = NULL;
+ EXPECT_TRUE(value.Get("entry", &entry));
+ EXPECT_TRUE(Value::Equals(entry, expected_entry.get()));
+ }
EXPECT_EQ(11u, value.size());
}
} // namespace
TEST_F(SyncApiTest, BaseNodeToValue) {
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
node.InitByRootLookup();
scoped_ptr<DictionaryValue> value(node.ToValue());
@@ -518,10 +522,11 @@ class MockExtraChangeRecordData
} // namespace
TEST_F(SyncApiTest, ChangeRecordToValue) {
- int64 child_id = MakeNode(&share_, syncable::BOOKMARKS, "testtag");
+ int64 child_id = MakeNode(test_user_share_.user_share(),
+ syncable::BOOKMARKS, "testtag");
sync_pb::EntitySpecifics child_specifics;
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
ReadNode node(&trans);
EXPECT_TRUE(node.InitByIdLookup(child_id));
child_specifics = node.GetEntry()->Get(syncable::SPECIFICS);
@@ -529,7 +534,7 @@ TEST_F(SyncApiTest, ChangeRecordToValue) {
// Add
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
SyncManager::ChangeRecord record;
record.action = SyncManager::ChangeRecord::ACTION_ADD;
record.id = 1;
@@ -541,7 +546,7 @@ TEST_F(SyncApiTest, ChangeRecordToValue) {
// Update
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
SyncManager::ChangeRecord record;
record.action = SyncManager::ChangeRecord::ACTION_UPDATE;
record.id = child_id;
@@ -553,7 +558,7 @@ TEST_F(SyncApiTest, ChangeRecordToValue) {
// Delete (no extra)
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
SyncManager::ChangeRecord record;
record.action = SyncManager::ChangeRecord::ACTION_DELETE;
record.id = child_id + 1;
@@ -564,7 +569,7 @@ TEST_F(SyncApiTest, ChangeRecordToValue) {
// Delete (with extra)
{
- ReadTransaction trans(&share_);
+ ReadTransaction trans(test_user_share_.user_share());
SyncManager::ChangeRecord record;
record.action = SyncManager::ChangeRecord::ACTION_DELETE;
record.id = child_id + 1;
@@ -612,28 +617,67 @@ class SyncManagerObserverMock : public SyncManager::Observer {
MOCK_METHOD1(OnPassphraseRequired, void(bool)); // NOLINT
MOCK_METHOD0(OnPassphraseFailed, void()); // NOLINT
MOCK_METHOD1(OnPassphraseAccepted, void(const std::string&)); // NOLINT
- MOCK_METHOD0(OnPaused, void()); // NOLINT
- MOCK_METHOD0(OnResumed, void()); // NOLINT
MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT
MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT
+ MOCK_METHOD1(OnMigrationNeededForTypes, void(const ModelTypeSet&));
MOCK_METHOD0(OnClearServerDataFailed, void()); // NOLINT
MOCK_METHOD0(OnClearServerDataSucceeded, void()); // NOLINT
MOCK_METHOD1(OnEncryptionComplete, void(const ModelTypeSet&)); // NOLINT
};
+class SyncNotifierMock : public sync_notifier::SyncNotifier {
+ public:
+ MOCK_METHOD1(AddObserver, void(sync_notifier::SyncNotifierObserver*));
+ MOCK_METHOD1(RemoveObserver, void(sync_notifier::SyncNotifierObserver*));
+ MOCK_METHOD1(SetState, void(const std::string&));
+ MOCK_METHOD2(UpdateCredentials,
+ void(const std::string&, const std::string&));
+ MOCK_METHOD1(UpdateEnabledTypes,
+ void(const syncable::ModelTypeSet&));
+ MOCK_METHOD0(SendNotification, void());
+};
+
class SyncManagerTest : public testing::Test,
public ModelSafeWorkerRegistrar {
protected:
- SyncManagerTest() : ui_thread_(BrowserThread::UI, &ui_loop_) {}
+ SyncManagerTest()
+ : ui_thread_(BrowserThread::UI, &ui_loop_),
+ sync_notifier_observer_(NULL),
+ update_enabled_types_call_count_(0) {}
// Test implementation.
void SetUp() {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+
+ SyncCredentials credentials;
+ credentials.email = "foo@bar.com";
+ credentials.sync_token = "sometoken";
+
+ sync_notifier_mock_.reset(new StrictMock<SyncNotifierMock>());
+ EXPECT_CALL(*sync_notifier_mock_, AddObserver(_)).
+ WillOnce(Invoke(this, &SyncManagerTest::SyncNotifierAddObserver));
+ EXPECT_CALL(*sync_notifier_mock_, SetState(""));
+ EXPECT_CALL(*sync_notifier_mock_,
+ UpdateCredentials(credentials.email, credentials.sync_token));
+ EXPECT_CALL(*sync_notifier_mock_, UpdateEnabledTypes(_)).
+ Times(AtLeast(1)).
+ WillRepeatedly(
+ Invoke(this, &SyncManagerTest::SyncNotifierUpdateEnabledTypes));
+ EXPECT_CALL(*sync_notifier_mock_, RemoveObserver(_)).
+ WillOnce(Invoke(this, &SyncManagerTest::SyncNotifierRemoveObserver));
+
+ EXPECT_FALSE(sync_notifier_observer_);
+
sync_manager_.Init(temp_dir_.path(), "bogus", 0, false,
new TestHttpPostProviderFactory(), this, "bogus",
- SyncCredentials(), notifier::NotifierOptions(),
- "", true /* setup_for_test_mode */);
+ credentials, sync_notifier_mock_.get(), "",
+ true /* setup_for_test_mode */);
+
+ EXPECT_TRUE(sync_notifier_observer_);
sync_manager_.AddObserver(&observer_);
+
+ EXPECT_EQ(1, update_enabled_types_call_count_);
+
ModelSafeRoutingInfo routes;
GetModelSafeRoutingInfo(&routes);
for (ModelSafeRoutingInfo::iterator i = routes.begin(); i != routes.end();
@@ -650,6 +694,7 @@ class SyncManagerTest : public testing::Test,
void TearDown() {
sync_manager_.RemoveObserver(&observer_);
sync_manager_.Shutdown();
+ EXPECT_FALSE(sync_notifier_observer_);
}
// ModelSafeWorkerRegistrar implementation.
@@ -674,14 +719,14 @@ class SyncManagerTest : public testing::Test,
return false;
// Set the nigori cryptographer information.
- Cryptographer* cryptographer = share->dir_manager->cryptographer();
+ WriteTransaction trans(share);
+ Cryptographer* cryptographer = trans.GetCryptographer();
if (!cryptographer)
return false;
KeyParams params = {"localhost", "dummy", "foobar"};
cryptographer->AddKey(params);
sync_pb::NigoriSpecifics nigori;
cryptographer->GetKeys(nigori.mutable_encrypted());
- WriteTransaction trans(share);
WriteNode node(&trans);
node.InitByIdLookup(nigori_id);
node.SetNigoriSpecifics(nigori);
@@ -694,6 +739,31 @@ class SyncManagerTest : public testing::Test,
return type_roots_[type];
}
+ void SyncNotifierAddObserver(
+ sync_notifier::SyncNotifierObserver* sync_notifier_observer) {
+ EXPECT_EQ(NULL, sync_notifier_observer_);
+ sync_notifier_observer_ = sync_notifier_observer;
+ }
+
+ void SyncNotifierRemoveObserver(
+ sync_notifier::SyncNotifierObserver* sync_notifier_observer) {
+ EXPECT_EQ(sync_notifier_observer_, sync_notifier_observer);
+ sync_notifier_observer_ = NULL;
+ }
+
+ void SyncNotifierUpdateEnabledTypes(
+ const syncable::ModelTypeSet& types) {
+ ModelSafeRoutingInfo routes;
+ GetModelSafeRoutingInfo(&routes);
+ syncable::ModelTypeSet expected_types;
+ for (ModelSafeRoutingInfo::const_iterator it = routes.begin();
+ it != routes.end(); ++it) {
+ expected_types.insert(it->first);
+ }
+ EXPECT_EQ(expected_types, types);
+ ++update_enabled_types_call_count_;
+ }
+
private:
// Needed by |ui_thread_|.
MessageLoopForUI ui_loop_;
@@ -703,12 +773,22 @@ class SyncManagerTest : public testing::Test,
ScopedTempDir temp_dir_;
// Sync Id's for the roots of the enabled datatypes.
std::map<ModelType, int64> type_roots_;
+ scoped_ptr<StrictMock<SyncNotifierMock> > sync_notifier_mock_;
protected:
SyncManager sync_manager_;
StrictMock<SyncManagerObserverMock> observer_;
+ sync_notifier::SyncNotifierObserver* sync_notifier_observer_;
+ int update_enabled_types_call_count_;
};
+TEST_F(SyncManagerTest, UpdateEnabledTypes) {
+ EXPECT_EQ(1, update_enabled_types_call_count_);
+ // Triggers SyncNotifierUpdateEnabledTypes.
+ sync_manager_.UpdateEnabledTypes();
+ EXPECT_EQ(2, update_enabled_types_call_count_);
+}
+
TEST_F(SyncManagerTest, ParentJsEventRouter) {
StrictMock<MockJsEventRouter> event_router;
browser_sync::JsBackend* js_backend = sync_manager_.GetJsBackend();
diff --git a/chrome/browser/sync/engine/syncer.cc b/chrome/browser/sync/engine/syncer.cc
index ac20600..2f24ee1 100644
--- a/chrome/browser/sync/engine/syncer.cc
+++ b/chrome/browser/sync/engine/syncer.cc
@@ -271,9 +271,6 @@ void Syncer::SyncShare(sessions::SyncSession* session,
break;
}
case SYNCER_END: {
- VLOG(1) << "Syncer End";
- SyncerEndCommand syncer_end_command;
- syncer_end_command.Execute(session);
break;
}
default:
@@ -284,11 +281,9 @@ void Syncer::SyncShare(sessions::SyncSession* session,
current_step = next_step;
}
- // Always send out a cycle ended notification, regardless of end-state.
- SyncEngineEvent event(SyncEngineEvent::SYNC_CYCLE_ENDED);
- sessions::SyncSessionSnapshot snapshot(session->TakeSnapshot());
- event.snapshot = &snapshot;
- session->context()->NotifyListeners(event);
+ VLOG(1) << "Syncer End";
+ SyncerEndCommand syncer_end_command;
+ syncer_end_command.Execute(session);
return;
}
diff --git a/chrome/browser/sync/engine/syncer.h b/chrome/browser/sync/engine/syncer.h
index 3178f69..4a86005 100644
--- a/chrome/browser/sync/engine/syncer.h
+++ b/chrome/browser/sync/engine/syncer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,7 +12,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/gtest_prod_util.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/sync/engine/conflict_resolver.h"
#include "chrome/browser/sync/engine/syncer_types.h"
diff --git a/chrome/browser/sync/engine/syncer_end_command.cc b/chrome/browser/sync/engine/syncer_end_command.cc
index dcdfcc1..516bbd0 100644
--- a/chrome/browser/sync/engine/syncer_end_command.cc
+++ b/chrome/browser/sync/engine/syncer_end_command.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -15,11 +15,13 @@ SyncerEndCommand::SyncerEndCommand() {}
SyncerEndCommand::~SyncerEndCommand() {}
void SyncerEndCommand::ExecuteImpl(sessions::SyncSession* session) {
- sessions::StatusController* status(session->status_controller());
- status->set_syncing(false);
- session->context()->set_previous_session_routing_info(
- session->routing_info());
- session->context()->set_last_snapshot(session->TakeSnapshot());
+ // Always send out a cycle ended notification, regardless of end-state.
+ session->status_controller()->set_syncing(false);
+ SyncEngineEvent event(SyncEngineEvent::SYNC_CYCLE_ENDED);
+ sessions::SyncSessionSnapshot snapshot(session->TakeSnapshot());
+ event.snapshot = &snapshot;
+ session->context()->NotifyListeners(event);
+ VLOG(1) << this << " sent sync end snapshot";
}
} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/syncer_proto_util.cc b/chrome/browser/sync/engine/syncer_proto_util.cc
index 5dbb202..a3a6177 100644
--- a/chrome/browser/sync/engine/syncer_proto_util.cc
+++ b/chrome/browser/sync/engine/syncer_proto_util.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/sync/protocol/service_constants.h"
#include "chrome/browser/sync/sessions/sync_session.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable-inl.h"
#include "chrome/browser/sync/syncable/syncable.h"
@@ -78,6 +79,19 @@ void LogResponseProfilingData(const ClientToServerResponse& response) {
} // namespace
+// static
+void SyncerProtoUtil::HandleMigrationDoneResponse(
+ const sync_pb::ClientToServerResponse* response,
+ sessions::SyncSession* session) {
+ LOG_IF(ERROR, 0 >= response->migrated_data_type_id_size())
+ << "MIGRATION_DONE but no types specified.";
+ syncable::ModelTypeSet to_migrate;
+ for (int i = 0; i < response->migrated_data_type_id_size(); i++) {
+ to_migrate.insert(syncable::GetModelTypeFromExtensionFieldNumber(
+ response->migrated_data_type_id(i)));
+ }
+ session->status_controller()->set_types_needing_local_migration(to_migrate);
+}
// static
bool SyncerProtoUtil::VerifyResponseBirthday(syncable::Directory* dir,
@@ -225,6 +239,9 @@ bool SyncerProtoUtil::PostClientToServerMessage(
return false;
case ClientToServerResponse::TRANSIENT_ERROR:
return false;
+ case ClientToServerResponse::MIGRATION_DONE:
+ HandleMigrationDoneResponse(response, session);
+ return false;
case ClientToServerResponse::USER_NOT_ACTIVATED:
case ClientToServerResponse::AUTH_INVALID:
case ClientToServerResponse::ACCESS_DENIED:
diff --git a/chrome/browser/sync/engine/syncer_proto_util.h b/chrome/browser/sync/engine/syncer_proto_util.h
index ca68ec3..d55e277 100644
--- a/chrome/browser/sync/engine/syncer_proto_util.h
+++ b/chrome/browser/sync/engine/syncer_proto_util.h
@@ -102,6 +102,12 @@ class SyncerProtoUtil {
static bool VerifyResponseBirthday(syncable::Directory* dir,
const sync_pb::ClientToServerResponse* response);
+ // Builds and sends a SyncEngineEvent to begin migration for types (specified
+ // in notification).
+ static void HandleMigrationDoneResponse(
+ const sync_pb::ClientToServerResponse* response,
+ sessions::SyncSession* session);
+
// Post the message using the scm, and do some processing on the returned
// headers. Decode the server response.
static bool PostAndProcessHeaders(browser_sync::ServerConnectionManager* scm,
diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc
index d9240ed..8660373 100644
--- a/chrome/browser/sync/engine/syncer_thread.cc
+++ b/chrome/browser/sync/engine/syncer_thread.cc
@@ -1,32 +1,14 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/engine/syncer_thread.h"
#include <algorithm>
-#include <queue>
-#include <string>
-#include <vector>
#include "base/rand_util.h"
-#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "build/build_config.h"
-#include "chrome/browser/sync/engine/model_safe_worker.h"
-#include "chrome/browser/sync/engine/net/server_connection_manager.h"
#include "chrome/browser/sync/engine/syncer.h"
-#include "chrome/browser/sync/sessions/sync_session.h"
-#include "jingle/notifier/listener/notification_constants.h"
-
-#if defined(OS_MACOSX)
-#include <CoreFoundation/CFNumber.h>
-#include <IOKit/IOTypes.h>
-#include <IOKit/IOKitLib.h>
-#endif
-
-using std::priority_queue;
-using std::min;
-using base::Time;
+
using base::TimeDelta;
using base::TimeTicks;
@@ -35,862 +17,861 @@ namespace browser_sync {
using sessions::SyncSession;
using sessions::SyncSessionSnapshot;
using sessions::SyncSourceInfo;
-using sessions::TypePayloadMap;
-
-// We use high values here to ensure that failure to receive poll updates from
-// the server doesn't result in rapid-fire polling from the client due to low
-// local limits.
-const int SyncerThread::kDefaultShortPollIntervalSeconds = 3600 * 8;
-const int SyncerThread::kDefaultLongPollIntervalSeconds = 3600 * 12;
-
-// TODO(tim): This is used to regulate the short poll (when notifications are
-// disabled) based on user idle time. If it is set to a smaller value than
-// the short poll interval, it basically does nothing; for now, this is what
-// we want and allows stronger control over the poll rate from the server. We
-// should probably re-visit this code later and figure out if user idle time
-// is really something we want and make sure it works, if it is.
-const int SyncerThread::kDefaultMaxPollIntervalMs = 30 * 60 * 1000;
-
-// Backoff interval randomization factor.
-static const int kBackoffRandomizationFactor = 2;
-
-const int SyncerThread::kMaxBackoffSeconds = 60 * 60 * 4; // 4 hours.
-
-SyncerThread::ProtectedFields::ProtectedFields()
- : stop_syncer_thread_(false),
- pause_requested_(false),
- paused_(false),
- syncer_(NULL),
- connected_(false),
- pending_nudge_source_(kUnknown) {}
-
-SyncerThread::ProtectedFields::~ProtectedFields() {}
-
-void SyncerThread::NudgeSyncerWithPayloads(
- int milliseconds_from_now,
- NudgeSource source,
- const TypePayloadMap& model_types_with_payloads) {
- base::AutoLock lock(lock_);
- if (vault_.syncer_ == NULL) {
- return;
- }
+using syncable::ModelTypePayloadMap;
+using syncable::ModelTypeBitSet;
+using sync_pb::GetUpdatesCallerInfo;
- NudgeSyncImpl(milliseconds_from_now, source, model_types_with_payloads);
-}
+SyncerThread::DelayProvider::DelayProvider() {}
+SyncerThread::DelayProvider::~DelayProvider() {}
-void SyncerThread::NudgeSyncerWithDataTypes(
- int milliseconds_from_now,
- NudgeSource source,
- const syncable::ModelTypeBitSet& model_types) {
- base::AutoLock lock(lock_);
- if (vault_.syncer_ == NULL) {
- return;
- }
+SyncerThread::WaitInterval::WaitInterval() {}
+SyncerThread::WaitInterval::~WaitInterval() {}
- TypePayloadMap model_types_with_payloads =
- sessions::MakeTypePayloadMapFromBitSet(model_types, std::string());
- NudgeSyncImpl(milliseconds_from_now, source, model_types_with_payloads);
+SyncerThread::SyncSessionJob::SyncSessionJob() {}
+SyncerThread::SyncSessionJob::~SyncSessionJob() {}
+
+SyncerThread::SyncSessionJob::SyncSessionJob(SyncSessionJobPurpose purpose,
+ base::TimeTicks start,
+ linked_ptr<sessions::SyncSession> session, bool is_canary_job,
+ const tracked_objects::Location& nudge_location) : purpose(purpose),
+ scheduled_start(start),
+ session(session),
+ is_canary_job(is_canary_job),
+ nudge_location(nudge_location) {
+}
+
+TimeDelta SyncerThread::DelayProvider::GetDelay(
+ const base::TimeDelta& last_delay) {
+ return SyncerThread::GetRecommendedDelay(last_delay);
}
-void SyncerThread::NudgeSyncer(
- int milliseconds_from_now,
+GetUpdatesCallerInfo::GetUpdatesSource GetUpdatesFromNudgeSource(
NudgeSource source) {
- base::AutoLock lock(lock_);
- if (vault_.syncer_ == NULL) {
- return;
+ switch (source) {
+ case NUDGE_SOURCE_NOTIFICATION:
+ return GetUpdatesCallerInfo::NOTIFICATION;
+ case NUDGE_SOURCE_LOCAL:
+ return GetUpdatesCallerInfo::LOCAL;
+ case NUDGE_SOURCE_CONTINUATION:
+ return GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION;
+ case NUDGE_SOURCE_UNKNOWN:
+ return GetUpdatesCallerInfo::UNKNOWN;
+ default:
+ NOTREACHED();
+ return GetUpdatesCallerInfo::UNKNOWN;
}
-
- // Set all enabled datatypes.
- ModelSafeRoutingInfo routes;
- session_context_->registrar()->GetModelSafeRoutingInfo(&routes);
- TypePayloadMap model_types_with_payloads =
- sessions::MakeTypePayloadMapFromRoutingInfo(routes, std::string());
- NudgeSyncImpl(milliseconds_from_now, source, model_types_with_payloads);
}
-SyncerThread::SyncerThread(sessions::SyncSessionContext* context)
- : thread_main_started_(false, false),
- thread_("SyncEngine_SyncerThread"),
- vault_field_changed_(&lock_),
- conn_mgr_hookup_(NULL),
- syncer_short_poll_interval_seconds_(kDefaultShortPollIntervalSeconds),
- syncer_long_poll_interval_seconds_(kDefaultLongPollIntervalSeconds),
- syncer_polling_interval_(kDefaultShortPollIntervalSeconds),
- syncer_max_interval_(kDefaultMaxPollIntervalMs),
- session_context_(context),
- disable_idle_detection_(false) {
- DCHECK(context);
+SyncerThread::WaitInterval::WaitInterval(Mode mode, TimeDelta length)
+ : mode(mode), had_nudge(false), length(length) { }
- if (context->connection_manager())
- WatchConnectionManager(context->connection_manager());
+SyncerThread::SyncerThread(sessions::SyncSessionContext* context,
+ Syncer* syncer)
+ : thread_("SyncEngine_SyncerThread"),
+ syncer_short_poll_interval_seconds_(
+ TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)),
+ syncer_long_poll_interval_seconds_(
+ TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)),
+ mode_(NORMAL_MODE),
+ server_connection_ok_(false),
+ delay_provider_(new DelayProvider()),
+ syncer_(syncer),
+ session_context_(context) {
}
SyncerThread::~SyncerThread() {
- conn_mgr_hookup_.reset();
- delete vault_.syncer_;
- CHECK(!thread_.IsRunning());
-}
-
-// Creates and starts a syncer thread.
-// Returns true if it creates a thread or if there's currently a thread running
-// and false otherwise.
-bool SyncerThread::Start() {
- {
- base::AutoLock lock(lock_);
- if (thread_.IsRunning()) {
- return true;
- }
+ DCHECK(!thread_.IsRunning());
+}
+void SyncerThread::CheckServerConnectionManagerStatus(
+ HttpResponse::ServerConnectionCode code) {
+
+ VLOG(1) << "SyncerThread(" << this << ")" << " Server connection changed."
+ << "Old mode: " << server_connection_ok_ << " Code: " << code;
+ // Note, be careful when adding cases here because if the SyncerThread
+ // thinks there is no valid connection as determined by this method, it
+ // will drop out of *all* forward progress sync loops (it won't poll and it
+ // will queue up Talk notifications but not actually call SyncShare) until
+ // some external action causes a ServerConnectionManager to broadcast that
+ // a valid connection has been re-established.
+ if (HttpResponse::CONNECTION_UNAVAILABLE == code ||
+ HttpResponse::SYNC_AUTH_ERROR == code) {
+ server_connection_ok_ = false;
+ VLOG(1) << "SyncerThread(" << this << ")" << " Server connection changed."
+ << " new mode:" << server_connection_ok_;
+ } else if (HttpResponse::SERVER_CONNECTION_OK == code) {
+ server_connection_ok_ = true;
+ VLOG(1) << "SyncerThread(" << this << ")" << " Server connection changed."
+ << " new mode:" << server_connection_ok_;
+ DoCanaryJob();
+ }
+}
+
+void SyncerThread::Start(Mode mode, ModeChangeCallback* callback) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Start called from thread "
+ << MessageLoop::current()->thread_name();
+ if (!thread_.IsRunning()) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Starting thread with mode "
+ << mode;
if (!thread_.Start()) {
- return false;
+ NOTREACHED() << "Unable to start SyncerThread.";
+ return;
}
+ WatchConnectionManager();
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SyncerThread::SendInitialSnapshot));
}
- thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
- &SyncerThread::ThreadMain));
+ VLOG(1) << "SyncerThread(" << this << ")" << " Entering start with mode = "
+ << mode;
- // Wait for notification that our task makes it safely onto the message
- // loop before returning, so the caller can't call Stop before we're
- // actually up and running. This is for consistency with the old pthread
- // impl because pthread_create would do this in one step.
- thread_main_started_.Wait();
- VLOG(1) << "SyncerThread started.";
- return true;
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SyncerThread::StartImpl, mode, callback));
}
-// Stop processing. A max wait of at least 2*server RTT time is recommended.
-// Returns true if we stopped, false otherwise.
-bool SyncerThread::Stop(int max_wait) {
- RequestSyncerExitAndSetThreadStopConditions();
+void SyncerThread::SendInitialSnapshot() {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ scoped_ptr<SyncSession> dummy(new SyncSession(session_context_.get(), this,
+ SyncSourceInfo(), ModelSafeRoutingInfo(),
+ std::vector<ModelSafeWorker*>()));
+ SyncEngineEvent event(SyncEngineEvent::STATUS_CHANGED);
+ sessions::SyncSessionSnapshot snapshot(dummy->TakeSnapshot());
+ event.snapshot = &snapshot;
+ session_context_->NotifyListeners(event);
+}
- // This will join, and finish when ThreadMain terminates.
- thread_.Stop();
- return true;
+void SyncerThread::WatchConnectionManager() {
+ ServerConnectionManager* scm = session_context_->connection_manager();
+ CheckServerConnectionManagerStatus(scm->server_status());
+ scm->AddListener(this);
}
-void SyncerThread::RequestSyncerExitAndSetThreadStopConditions() {
- {
- base::AutoLock lock(lock_);
- // If the thread has been started, then we either already have or are about
- // to enter ThreadMainLoop so we have to proceed with shutdown and wait for
- // it to finish. If the thread has not been started --and we now own the
- // lock-- then we can early out because the caller has not called Start().
- if (!thread_.IsRunning())
- return;
+void SyncerThread::StartImpl(Mode mode, ModeChangeCallback* callback) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Doing StartImpl with mode "
+ << mode;
- VLOG(1) << "SyncerThread::Stop - setting ThreadMain exit condition to true "
- "(vault_.stop_syncer_thread_)";
- // Exit the ThreadMainLoop once the syncer finishes (we tell it to exit
- // below).
- vault_.stop_syncer_thread_ = true;
- if (NULL != vault_.syncer_) {
- // Try to early exit the syncer itself, which could be looping inside
- // SyncShare.
- vault_.syncer_->RequestEarlyExit();
- }
+ // TODO(lipalani): This will leak if startimpl is never run. Fix it using a
+ // ThreadSafeRefcounted object.
+ scoped_ptr<ModeChangeCallback> scoped_callback(callback);
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ DCHECK(!session_context_->account_name().empty());
+ DCHECK(syncer_.get());
+ mode_ = mode;
+ AdjustPolling(NULL); // Will kick start poll timer if needed.
+ if (scoped_callback.get())
+ scoped_callback->Run();
- // stop_syncer_thread_ is now true and the Syncer has been told to exit.
- // We want to wake up all waiters so they can re-examine state. We signal,
- // causing all waiters to try to re-acquire the lock, and then we release
- // the lock, and join on our internal thread which should soon run off the
- // end of ThreadMain.
- vault_field_changed_.Broadcast();
- }
+ // We just changed our mode. See if there are any pending jobs that we could
+ // execute in the new mode.
+ DoPendingJobIfPossible(false);
}
-bool SyncerThread::RequestPause() {
- base::AutoLock lock(lock_);
- if (vault_.pause_requested_ || vault_.paused_)
- return false;
+SyncerThread::JobProcessDecision SyncerThread::DecideWhileInWaitInterval(
+ const SyncSessionJob& job) {
- if (thread_.IsRunning()) {
- // Set the pause request. The syncer thread will read this
- // request, enter the paused state, and send the PAUSED
- // notification.
- vault_.pause_requested_ = true;
- vault_field_changed_.Broadcast();
- VLOG(1) << "Pause requested.";
- } else {
- // If the thread is not running, go directly into the paused state
- // and notify.
- EnterPausedState();
- VLOG(1) << "Paused while not running.";
- }
- return true;
-}
+ DCHECK(wait_interval_.get());
+ DCHECK_NE(job.purpose, SyncSessionJob::CLEAR_USER_DATA);
-void SyncerThread::Notify(SyncEngineEvent::EventCause cause) {
- session_context_->NotifyListeners(SyncEngineEvent(cause));
-}
+ VLOG(1) << "SyncerThread(" << this << ")" << " Wait interval mode : "
+ << wait_interval_->mode << "Wait interval had nudge : "
+ << wait_interval_->had_nudge << "is canary job : "
+ << job.is_canary_job;
-bool SyncerThread::RequestResume() {
- base::AutoLock lock(lock_);
- // Only valid to request a resume when we are already paused or we
- // have a pause pending.
- if (!(vault_.paused_ || vault_.pause_requested_))
- return false;
-
- if (thread_.IsRunning()) {
- if (vault_.pause_requested_) {
- // If pause was requested we have not yet paused. In this case,
- // the resume cancels the pause request.
- vault_.pause_requested_ = false;
- vault_field_changed_.Broadcast();
- Notify(SyncEngineEvent::SYNCER_THREAD_RESUMED);
- VLOG(1) << "Pending pause canceled by resume.";
- } else {
- // Unpause and notify.
- vault_.paused_ = false;
- vault_field_changed_.Broadcast();
- }
- } else {
- ExitPausedState();
- VLOG(1) << "Resumed while not running.";
- }
- return true;
-}
+ if (job.purpose == SyncSessionJob::POLL)
+ return DROP;
-void SyncerThread::OnReceivedLongPollIntervalUpdate(
- const base::TimeDelta& new_interval) {
- syncer_long_poll_interval_seconds_ = static_cast<int>(
- new_interval.InSeconds());
-}
+ DCHECK(job.purpose == SyncSessionJob::NUDGE ||
+ job.purpose == SyncSessionJob::CONFIGURATION);
+ if (wait_interval_->mode == WaitInterval::THROTTLED)
+ return SAVE;
-void SyncerThread::OnReceivedShortPollIntervalUpdate(
- const base::TimeDelta& new_interval) {
- syncer_short_poll_interval_seconds_ = static_cast<int>(
- new_interval.InSeconds());
-}
+ DCHECK_EQ(wait_interval_->mode, WaitInterval::EXPONENTIAL_BACKOFF);
+ if (job.purpose == SyncSessionJob::NUDGE) {
+ if (mode_ == CONFIGURATION_MODE)
+ return SAVE;
-void SyncerThread::OnSilencedUntil(const base::TimeTicks& silenced_until) {
- silenced_until_ = silenced_until;
+ // If we already had one nudge then just drop this nudge. We will retry
+ // later when the timer runs out.
+ return wait_interval_->had_nudge ? DROP : CONTINUE;
+ }
+ // This is a config job.
+ return job.is_canary_job ? CONTINUE : SAVE;
}
-bool SyncerThread::IsSyncingCurrentlySilenced() {
- // We should ignore reads from silenced_until_ under ThreadSanitizer
- // since this is a benign race.
- ANNOTATE_IGNORE_READS_BEGIN();
- bool ret = (silenced_until_ - TimeTicks::Now()) >= TimeDelta::FromSeconds(0);
- ANNOTATE_IGNORE_READS_END();
- return ret;
-}
+SyncerThread::JobProcessDecision SyncerThread::DecideOnJob(
+ const SyncSessionJob& job) {
+ if (job.purpose == SyncSessionJob::CLEAR_USER_DATA)
+ return CONTINUE;
-void SyncerThread::OnShouldStopSyncingPermanently() {
- RequestSyncerExitAndSetThreadStopConditions();
- Notify(SyncEngineEvent::STOP_SYNCING_PERMANENTLY);
-}
+ if (wait_interval_.get())
+ return DecideWhileInWaitInterval(job);
-void SyncerThread::ThreadMainLoop() {
- // This is called with lock_ acquired.
- lock_.AssertAcquired();
- VLOG(1) << "In thread main loop.";
-
- // Use the short poll value by default.
- vault_.current_wait_interval_.poll_delta =
- TimeDelta::FromSeconds(syncer_short_poll_interval_seconds_);
- int user_idle_milliseconds = 0;
- TimeTicks last_sync_time;
- bool initial_sync_for_thread = true;
- bool continue_sync_cycle = false;
-
-#if defined(OS_LINUX)
- idle_query_.reset(new IdleQueryLinux());
-#endif
-
- if (vault_.syncer_ == NULL) {
- VLOG(1) << "Syncer thread waiting for database initialization.";
- while (vault_.syncer_ == NULL && !vault_.stop_syncer_thread_)
- vault_field_changed_.Wait();
- VLOG_IF(1, !(vault_.syncer_ == NULL)) << "Syncer was found after DB "
- "started.";
+ if (mode_ == CONFIGURATION_MODE) {
+ if (job.purpose == SyncSessionJob::NUDGE)
+ return SAVE;
+ else if (job.purpose == SyncSessionJob::CONFIGURATION)
+ return CONTINUE;
+ else
+ return DROP;
}
- while (!vault_.stop_syncer_thread_) {
- // The Wait()s in these conditionals using |vault_| are not TimedWait()s (as
- // below) because we cannot poll until these conditions are met, so we wait
- // indefinitely.
-
- // If we are not connected, enter WaitUntilConnectedOrQuit() which
- // will return only when the network is connected or a quit is
- // requested. Note that it is possible to exit
- // WaitUntilConnectedOrQuit() in the paused state which will be
- // handled by the next statement.
- if (!vault_.connected_ && !initial_sync_for_thread) {
- WaitUntilConnectedOrQuit();
- continue;
- }
+ // We are in normal mode.
+ DCHECK_EQ(mode_, NORMAL_MODE);
+ DCHECK_NE(job.purpose, SyncSessionJob::CONFIGURATION);
- // Check if we should be paused or if a pause was requested. Note
- // that we don't check initial_sync_for_thread here since we want
- // the pause to happen regardless if it is the initial sync or not.
- if (vault_.pause_requested_ || vault_.paused_) {
- PauseUntilResumedOrQuit();
- continue;
- }
+ // Freshness condition
+ if (job.scheduled_start < last_sync_session_end_time_) {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Dropping job because of freshness";
+ return DROP;
+ }
- const TimeTicks next_poll = last_sync_time +
- vault_.current_wait_interval_.poll_delta;
- bool throttled = vault_.current_wait_interval_.mode ==
- WaitInterval::THROTTLED;
- // If we are throttled, we must wait. Otherwise, wait until either the next
- // nudge (if one exists) or the poll interval.
- TimeTicks end_wait = next_poll;
- if (!throttled && !vault_.pending_nudge_time_.is_null()) {
- end_wait = std::min(end_wait, vault_.pending_nudge_time_);
- }
- VLOG(1) << "end_wait is " << end_wait.ToInternalValue()
- << "\nnext_poll is " << next_poll.ToInternalValue();
-
- // We block until the CV is signaled (e.g a control field changed, loss of
- // network connection, nudge, spurious, etc), or the poll interval elapses.
- TimeDelta sleep_time = end_wait - TimeTicks::Now();
- if (!initial_sync_for_thread && sleep_time > TimeDelta::FromSeconds(0)) {
- vault_field_changed_.TimedWait(sleep_time);
-
- if (TimeTicks::Now() < end_wait) {
- // Didn't timeout. Could be a spurious signal, or a signal corresponding
- // to an actual change in one of our control fields. By continuing here
- // we perform the typical "always recheck conditions when signaled",
- // (typically handled by a while(condition_not_met) cv.wait() construct)
- // because we jump to the top of the loop. The main difference is we
- // recalculate the wait interval, but last_sync_time won't have changed.
- // So if we were signaled by a nudge (for ex.) we'll grab the new nudge
- // off the queue and wait for that delta. If it was a spurious signal,
- // we'll keep waiting for the same moment in time as we just were.
- continue;
- }
- }
+ if (server_connection_ok_)
+ return CONTINUE;
- // Handle a nudge, caused by either a notification or a local bookmark
- // event. This will also update the source of the following SyncMain call.
- VLOG(1) << "Calling Sync Main at time " << Time::Now().ToInternalValue();
- bool nudged = false;
- scoped_ptr<SyncSession> session;
- session.reset(SyncMain(vault_.syncer_,
- throttled, continue_sync_cycle, &initial_sync_for_thread, &nudged));
-
- // Update timing information for how often these datatypes are triggering
- // nudges.
- base::TimeTicks now = TimeTicks::Now();
- if (!last_sync_time.is_null()) {
- TypePayloadMap::const_iterator iter;
- for (iter = session->source().types.begin();
- iter != session->source().types.end();
- ++iter) {
- syncable::PostTimeToTypeHistogram(iter->first,
- now - last_sync_time);
- }
- }
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Bad server connection. Using that to decide on job.";
+ return job.purpose == SyncSessionJob::NUDGE ? SAVE : DROP;
+}
+
+void SyncerThread::InitOrCoalescePendingJob(const SyncSessionJob& job) {
+ DCHECK(job.purpose != SyncSessionJob::CONFIGURATION);
+ if (pending_nudge_.get() == NULL) {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Creating a pending nudge job";
+ SyncSession* s = job.session.get();
+ scoped_ptr<SyncSession> session(new SyncSession(s->context(),
+ s->delegate(), s->source(), s->routing_info(), s->workers()));
- last_sync_time = now;
+ SyncSessionJob new_job(SyncSessionJob::NUDGE, job.scheduled_start,
+ make_linked_ptr(session.release()), false, job.nudge_location);
+ pending_nudge_.reset(new SyncSessionJob(new_job));
- VLOG(1) << "Updating the next polling time after SyncMain";
- vault_.current_wait_interval_ = CalculatePollingWaitTime(
- static_cast<int>(vault_.current_wait_interval_.poll_delta.InSeconds()),
- &user_idle_milliseconds, &continue_sync_cycle, nudged);
+ return;
}
-#if defined(OS_LINUX)
- idle_query_.reset();
-#endif
-}
-void SyncerThread::SetConnected(bool connected) {
- DCHECK(!thread_.IsRunning());
- vault_.connected_ = connected;
-}
+ VLOG(1) << "SyncerThread(" << this << ")" << " Coalescing a pending nudge";
+ pending_nudge_->session->Coalesce(*(job.session.get()));
+ pending_nudge_->scheduled_start = job.scheduled_start;
-void SyncerThread::SetSyncerPollingInterval(base::TimeDelta interval) {
- // TODO(timsteele): Use TimeDelta internally.
- syncer_polling_interval_ = static_cast<int>(interval.InSeconds());
+ // Unfortunately the nudge location cannot be modified. So it stores the
+ // location of the first caller.
}
-void SyncerThread::SetSyncerShortPollInterval(base::TimeDelta interval) {
- // TODO(timsteele): Use TimeDelta internally.
- syncer_short_poll_interval_seconds_ =
- static_cast<int>(interval.InSeconds());
-}
+bool SyncerThread::ShouldRunJob(const SyncSessionJob& job) {
+ JobProcessDecision decision = DecideOnJob(job);
+ VLOG(1) << "SyncerThread(" << this << ")" << " Should run job, decision: "
+ << decision << " Job purpose " << job.purpose << "mode " << mode_;
+ if (decision != SAVE)
+ return decision == CONTINUE;
-void SyncerThread::WaitUntilConnectedOrQuit() {
- VLOG(1) << "Syncer thread waiting for connection.";
- Notify(SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION);
+ DCHECK(job.purpose == SyncSessionJob::NUDGE || job.purpose ==
+ SyncSessionJob::CONFIGURATION);
- bool is_paused = vault_.paused_;
+ SaveJob(job);
+ return false;
+}
- while (!vault_.connected_ && !vault_.stop_syncer_thread_) {
- if (!is_paused && vault_.pause_requested_) {
- // If we get a pause request while waiting for a connection,
- // enter the paused state.
- EnterPausedState();
- is_paused = true;
- VLOG(1) << "Syncer thread entering disconnected pause.";
- }
+void SyncerThread::SaveJob(const SyncSessionJob& job) {
+ DCHECK(job.purpose != SyncSessionJob::CLEAR_USER_DATA);
+ if (job.purpose == SyncSessionJob::NUDGE) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Saving a nudge job";
+ InitOrCoalescePendingJob(job);
+ } else if (job.purpose == SyncSessionJob::CONFIGURATION){
+ VLOG(1) << "SyncerThread(" << this << ")" << " Saving a configuration job";
+ DCHECK(wait_interval_.get());
+ DCHECK(mode_ == CONFIGURATION_MODE);
- if (is_paused && !vault_.paused_) {
- ExitPausedState();
- is_paused = false;
- VLOG(1) << "Syncer thread exiting disconnected pause.";
- }
+ SyncSession* old = job.session.get();
+ SyncSession* s(new SyncSession(session_context_.get(), this,
+ old->source(), old->routing_info(), old->workers()));
+ SyncSessionJob new_job(job.purpose, TimeTicks::Now(),
+ make_linked_ptr(s), false, job.nudge_location);
+ wait_interval_->pending_configure_job.reset(new SyncSessionJob(new_job));
+ } // drop the rest.
+}
- vault_field_changed_.Wait();
+// Functor for std::find_if to search by ModelSafeGroup.
+struct ModelSafeWorkerGroupIs {
+ explicit ModelSafeWorkerGroupIs(ModelSafeGroup group) : group(group) {}
+ bool operator()(ModelSafeWorker* w) {
+ return group == w->GetModelSafeGroup();
}
+ ModelSafeGroup group;
+};
- if (!vault_.stop_syncer_thread_) {
- Notify(SyncEngineEvent::SYNCER_THREAD_CONNECTED);
- VLOG(1) << "Syncer thread found connection.";
+void SyncerThread::ScheduleClearUserData() {
+ if (!thread_.IsRunning()) {
+ NOTREACHED();
+ return;
}
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SyncerThread::ScheduleClearUserDataImpl));
}
-void SyncerThread::PauseUntilResumedOrQuit() {
- VLOG(1) << "Syncer thread entering pause.";
- // If pause was requested (rather than already being paused), send
- // the PAUSED notification.
- if (vault_.pause_requested_)
- EnterPausedState();
-
- // Thread will get stuck here until either a resume is requested
- // or shutdown is started.
- while (vault_.paused_ && !vault_.stop_syncer_thread_)
- vault_field_changed_.Wait();
+void SyncerThread::ScheduleNudge(const TimeDelta& delay,
+ NudgeSource source, const ModelTypeBitSet& types,
+ const tracked_objects::Location& nudge_location) {
+ if (!thread_.IsRunning()) {
+ NOTREACHED();
+ return;
+ }
- // Notify that we have resumed if we are not shutting down.
- if (!vault_.stop_syncer_thread_)
- ExitPausedState();
+ VLOG(1) << "SyncerThread(" << this << ")" << " Nudge scheduled";
- VLOG(1) << "Syncer thread exiting pause.";
+ ModelTypePayloadMap types_with_payloads =
+ syncable::ModelTypePayloadMapFromBitSet(types, std::string());
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SyncerThread::ScheduleNudgeImpl, delay,
+ GetUpdatesFromNudgeSource(source), types_with_payloads, false,
+ nudge_location));
}
-void SyncerThread::EnterPausedState() {
- lock_.AssertAcquired();
- vault_.pause_requested_ = false;
- vault_.paused_ = true;
- vault_field_changed_.Broadcast();
- Notify(SyncEngineEvent::SYNCER_THREAD_PAUSED);
-}
+void SyncerThread::ScheduleNudgeWithPayloads(const TimeDelta& delay,
+ NudgeSource source, const ModelTypePayloadMap& types_with_payloads,
+ const tracked_objects::Location& nudge_location) {
+ if (!thread_.IsRunning()) {
+ NOTREACHED();
+ return;
+ }
+
+ VLOG(1) << "SyncerThread(" << this << ")" << " Nudge scheduled with payloads";
-void SyncerThread::ExitPausedState() {
- lock_.AssertAcquired();
- vault_.paused_ = false;
- vault_field_changed_.Broadcast();
- Notify(SyncEngineEvent::SYNCER_THREAD_RESUMED);
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SyncerThread::ScheduleNudgeImpl, delay,
+ GetUpdatesFromNudgeSource(source), types_with_payloads, false,
+ nudge_location));
}
-void SyncerThread::DisableIdleDetection() {
- disable_idle_detection_ = true;
+void SyncerThread::ScheduleClearUserDataImpl() {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ SyncSession* session = new SyncSession(session_context_.get(), this,
+ SyncSourceInfo(), ModelSafeRoutingInfo(),
+ std::vector<ModelSafeWorker*>());
+ ScheduleSyncSessionJob(TimeDelta::FromSeconds(0),
+ SyncSessionJob::CLEAR_USER_DATA, session, FROM_HERE);
}
-// We check how long the user's been idle and sync less often if the machine is
-// not in use. The aim is to reduce server load.
-SyncerThread::WaitInterval SyncerThread::CalculatePollingWaitTime(
- int last_poll_wait, // Time in seconds.
- int* user_idle_milliseconds,
- bool* continue_sync_cycle,
- bool was_nudged) {
- lock_.AssertAcquired(); // We access 'vault' in here, so we need the lock.
- WaitInterval return_interval;
+void SyncerThread::ScheduleNudgeImpl(const TimeDelta& delay,
+ GetUpdatesCallerInfo::GetUpdatesSource source,
+ const ModelTypePayloadMap& types_with_payloads,
+ bool is_canary_job, const tracked_objects::Location& nudge_location) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- // Server initiated throttling trumps everything.
- if (!silenced_until_.is_null()) {
- // We don't need to reset other state, it can continue where it left off.
- return_interval.mode = WaitInterval::THROTTLED;
- return_interval.poll_delta = silenced_until_ - TimeTicks::Now();
- return return_interval;
- }
+ VLOG(1) << "SyncerThread(" << this << ")" << " Running Schedule nudge impl";
+ // Note we currently nudge for all types regardless of the ones incurring
+ // the nudge. Doing different would throw off some syncer commands like
+ // CleanupDisabledTypes. We may want to change this in the future.
+ SyncSourceInfo info(source, types_with_payloads);
- bool is_continuing_sync_cyle = *continue_sync_cycle;
- *continue_sync_cycle = false;
+ SyncSession* session(CreateSyncSession(info));
+ SyncSessionJob job(SyncSessionJob::NUDGE, TimeTicks::Now() + delay,
+ make_linked_ptr(session), is_canary_job,
+ nudge_location);
- // Determine if the syncer has unfinished work to do.
- SyncSessionSnapshot* snapshot = session_context_->previous_session_snapshot();
- const bool syncer_has_work_to_do = snapshot &&
- (snapshot->num_server_changes_remaining > 0 ||
- snapshot->unsynced_count > 0);
- VLOG(1) << "syncer_has_work_to_do is " << syncer_has_work_to_do;
+ session = NULL;
+ if (!ShouldRunJob(job))
+ return;
- // First calculate the expected wait time, figuring in any backoff because of
- // user idle time. next_wait is in seconds
- syncer_polling_interval_ = (!session_context_->notifications_enabled()) ?
- syncer_short_poll_interval_seconds_ :
- syncer_long_poll_interval_seconds_;
- int default_next_wait = syncer_polling_interval_;
- return_interval.poll_delta = TimeDelta::FromSeconds(default_next_wait);
-
- if (syncer_has_work_to_do) {
- // Provide exponential backoff due to consecutive errors, else attempt to
- // complete the work as soon as possible.
- if (is_continuing_sync_cyle) {
- return_interval.mode = WaitInterval::EXPONENTIAL_BACKOFF;
- if (was_nudged && vault_.current_wait_interval_.mode ==
- WaitInterval::EXPONENTIAL_BACKOFF) {
- // We were nudged, it failed, and we were already in backoff.
- return_interval.had_nudge_during_backoff = true;
- // Keep exponent for exponential backoff the same in this case.
- return_interval.poll_delta = vault_.current_wait_interval_.poll_delta;
- } else {
- // We weren't nudged, or we were in a NORMAL wait interval until now.
- return_interval.poll_delta = TimeDelta::FromSeconds(
- GetRecommendedDelaySeconds(last_poll_wait));
- }
- } else {
- // No consecutive error.
- return_interval.poll_delta = TimeDelta::FromSeconds(
- GetRecommendedDelaySeconds(0));
+ if (pending_nudge_.get()) {
+ if (IsBackingOff() && delay > TimeDelta::FromSeconds(1)) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Dropping the nudge because"
+ << "we are in backoff";
+ return;
}
- *continue_sync_cycle = true;
- } else if (!session_context_->notifications_enabled()) {
- // Ensure that we start exponential backoff from our base polling
- // interval when we are not continuing a sync cycle.
- last_poll_wait = std::max(last_poll_wait, syncer_polling_interval_);
-
- // Did the user start interacting with the computer again?
- // If so, revise our idle time (and probably next_sync_time) downwards
- int new_idle_time = disable_idle_detection_ ? 0 : UserIdleTime();
- if (new_idle_time < *user_idle_milliseconds) {
- *user_idle_milliseconds = new_idle_time;
+
+ VLOG(1) << "SyncerThread(" << this << ")" << " Coalescing pending nudge";
+ pending_nudge_->session->Coalesce(*(job.session.get()));
+
+ if (!IsBackingOff()) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Dropping a nudge because"
+ << " we are not in backoff and the job was coalesced";
+ return;
+ } else {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Rescheduling pending nudge";
+ SyncSession* s = pending_nudge_->session.get();
+ job.session.reset(new SyncSession(s->context(), s->delegate(),
+ s->source(), s->routing_info(), s->workers()));
+ pending_nudge_.reset();
}
- return_interval.poll_delta = TimeDelta::FromMilliseconds(
- CalculateSyncWaitTime(last_poll_wait * 1000,
- *user_idle_milliseconds));
- DCHECK_GE(return_interval.poll_delta.InSeconds(), default_next_wait);
}
- VLOG(1) << "Sync wait: idle " << default_next_wait
- << " non-idle or backoff " << return_interval.poll_delta.InSeconds();
-
- return return_interval;
+ // TODO(lipalani) - pass the job itself to ScheduleSyncSessionJob.
+ ScheduleSyncSessionJob(delay, SyncSessionJob::NUDGE, job.session.release(),
+ nudge_location);
}
-void SyncerThread::ThreadMain() {
- base::AutoLock lock(lock_);
- // Signal Start() to let it know we've made it safely onto the message loop,
- // and unblock it's caller.
- thread_main_started_.Signal();
- ThreadMainLoop();
- VLOG(1) << "Syncer thread ThreadMain is done.";
- Notify(SyncEngineEvent::SYNCER_THREAD_EXITING);
-}
+// Helper to extract the routing info and workers corresponding to types in
+// |types| from |registrar|.
+void GetModelSafeParamsForTypes(const ModelTypeBitSet& types,
+ ModelSafeWorkerRegistrar* registrar, ModelSafeRoutingInfo* routes,
+ std::vector<ModelSafeWorker*>* workers) {
+ ModelSafeRoutingInfo r_tmp;
+ std::vector<ModelSafeWorker*> w_tmp;
+ registrar->GetModelSafeRoutingInfo(&r_tmp);
+ registrar->GetWorkers(&w_tmp);
-SyncSession* SyncerThread::SyncMain(Syncer* syncer, bool was_throttled,
- bool continue_sync_cycle, bool* initial_sync_for_thread,
- bool* was_nudged) {
- CHECK(syncer);
+ bool passive_group_added = false;
- // Since we are initiating a new session for which we are the delegate, we
- // are not currently silenced so reset this state for the next session which
- // may need to use it.
- silenced_until_ = base::TimeTicks();
+ typedef std::vector<ModelSafeWorker*>::const_iterator iter;
+ for (size_t i = syncable::FIRST_REAL_MODEL_TYPE; i < types.size(); ++i) {
+ if (!types.test(i))
+ continue;
+ syncable::ModelType t = syncable::ModelTypeFromInt(i);
+ DCHECK_EQ(1U, r_tmp.count(t));
+ (*routes)[t] = r_tmp[t];
+ iter it = std::find_if(w_tmp.begin(), w_tmp.end(),
+ ModelSafeWorkerGroupIs(r_tmp[t]));
+ if (it != w_tmp.end()) {
+ iter it2 = std::find_if(workers->begin(), workers->end(),
+ ModelSafeWorkerGroupIs(r_tmp[t]));
+ if (it2 == workers->end())
+ workers->push_back(*it);
+
+ if (r_tmp[t] == GROUP_PASSIVE)
+ passive_group_added = true;
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ // Always add group passive.
+ if (passive_group_added == false) {
+ iter it = std::find_if(w_tmp.begin(), w_tmp.end(),
+ ModelSafeWorkerGroupIs(GROUP_PASSIVE));
+ if (it != w_tmp.end())
+ workers->push_back(*it);
+ else
+ NOTREACHED();
+ }
+}
+
+void SyncerThread::ScheduleConfig(const ModelTypeBitSet& types) {
+ if (!thread_.IsRunning()) {
+ NOTREACHED();
+ return;
+ }
+ VLOG(1) << "SyncerThread(" << this << ")" << " Scheduling a config";
ModelSafeRoutingInfo routes;
std::vector<ModelSafeWorker*> workers;
- session_context_->registrar()->GetModelSafeRoutingInfo(&routes);
- session_context_->registrar()->GetWorkers(&workers);
- SyncSourceInfo info(GetAndResetNudgeSource(was_throttled,
- continue_sync_cycle, initial_sync_for_thread, was_nudged));
- scoped_ptr<SyncSession> session;
-
- base::AutoUnlock unlock(lock_);
- do {
- session.reset(new SyncSession(session_context_.get(), this,
- info, routes, workers));
- VLOG(1) << "Calling SyncShare.";
- syncer->SyncShare(session.get());
- } while (session->HasMoreToSync() && silenced_until_.is_null());
-
- VLOG(1) << "Done calling SyncShare.";
- return session.release();
-}
-
-SyncSourceInfo SyncerThread::GetAndResetNudgeSource(bool was_throttled,
- bool continue_sync_cycle,
- bool* initial_sync,
- bool* was_nudged) {
- bool nudged = false;
- NudgeSource nudge_source = kUnknown;
- TypePayloadMap model_types_with_payloads;
- // Has the previous sync cycle completed?
- if (continue_sync_cycle)
- nudge_source = kContinuation;
- // Update the nudge source if a new nudge has come through during the
- // previous sync cycle.
- if (!vault_.pending_nudge_time_.is_null()) {
- if (!was_throttled) {
- nudge_source = vault_.pending_nudge_source_;
- model_types_with_payloads = vault_.pending_nudge_types_;
- nudged = true;
- }
- VLOG(1) << "Clearing pending nudge from " << vault_.pending_nudge_source_
- << " at tick " << vault_.pending_nudge_time_.ToInternalValue();
- vault_.pending_nudge_source_ = kUnknown;
- vault_.pending_nudge_types_.clear();
- vault_.pending_nudge_time_ = base::TimeTicks();
+ GetModelSafeParamsForTypes(types, session_context_->registrar(),
+ &routes, &workers);
+
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ this, &SyncerThread::ScheduleConfigImpl, routes, workers,
+ GetUpdatesCallerInfo::FIRST_UPDATE));
+}
+
+void SyncerThread::ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info,
+ const std::vector<ModelSafeWorker*>& workers,
+ const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+
+ VLOG(1) << "SyncerThread(" << this << ")" << " ScheduleConfigImpl...";
+ // TODO(tim): config-specific GetUpdatesCallerInfo value?
+ SyncSession* session = new SyncSession(session_context_.get(), this,
+ SyncSourceInfo(source,
+ syncable::ModelTypePayloadMapFromRoutingInfo(
+ routing_info, std::string())),
+ routing_info, workers);
+ ScheduleSyncSessionJob(TimeDelta::FromSeconds(0),
+ SyncSessionJob::CONFIGURATION, session, FROM_HERE);
+}
+
+void SyncerThread::ScheduleSyncSessionJob(const base::TimeDelta& delay,
+ SyncSessionJob::SyncSessionJobPurpose purpose,
+ sessions::SyncSession* session,
+ const tracked_objects::Location& nudge_location) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+
+ SyncSessionJob job(purpose, TimeTicks::Now() + delay,
+ make_linked_ptr(session), false, nudge_location);
+ if (purpose == SyncSessionJob::NUDGE) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Resetting pending_nudge in"
+ << " ScheduleSyncSessionJob";
+ DCHECK(!pending_nudge_.get() || pending_nudge_->session.get() == session);
+ pending_nudge_.reset(new SyncSessionJob(job));
}
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Posting job to execute in DoSyncSessionJob. Job purpose "
+ << job.purpose;
+ MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(this,
+ &SyncerThread::DoSyncSessionJob, job),
+ delay.InMilliseconds());
+}
+
+void SyncerThread::SetSyncerStepsForPurpose(
+ SyncSessionJob::SyncSessionJobPurpose purpose,
+ SyncerStep* start, SyncerStep* end) {
+ *end = SYNCER_END;
+ switch (purpose) {
+ case SyncSessionJob::CONFIGURATION:
+ *start = DOWNLOAD_UPDATES;
+ *end = APPLY_UPDATES;
+ return;
+ case SyncSessionJob::CLEAR_USER_DATA:
+ *start = CLEAR_PRIVATE_DATA;
+ return;
+ case SyncSessionJob::NUDGE:
+ case SyncSessionJob::POLL:
+ *start = SYNCER_BEGIN;
+ return;
+ default:
+ NOTREACHED();
+ }
+}
- *was_nudged = nudged;
-
- // TODO(tim): Hack for bug 64136 to correctly tag continuations that result
- // from syncer having more work to do. This will be handled properly with
- // the message loop based syncer thread, bug 26339.
- return MakeSyncSourceInfo(nudged || nudge_source == kContinuation,
- nudge_source, model_types_with_payloads, initial_sync);
-}
-
-SyncSourceInfo SyncerThread::MakeSyncSourceInfo(bool nudged,
- NudgeSource nudge_source,
- const TypePayloadMap& model_types_with_payloads,
- bool* initial_sync) {
- sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source =
- sync_pb::GetUpdatesCallerInfo::UNKNOWN;
- if (*initial_sync) {
- updates_source = sync_pb::GetUpdatesCallerInfo::FIRST_UPDATE;
- *initial_sync = false;
- } else if (!nudged) {
- updates_source = sync_pb::GetUpdatesCallerInfo::PERIODIC;
- } else {
- switch (nudge_source) {
- case kNotification:
- updates_source = sync_pb::GetUpdatesCallerInfo::NOTIFICATION;
- break;
- case kLocal:
- updates_source = sync_pb::GetUpdatesCallerInfo::LOCAL;
- break;
- case kContinuation:
- updates_source = sync_pb::GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION;
- break;
- case kClearPrivateData:
- updates_source = sync_pb::GetUpdatesCallerInfo::CLEAR_PRIVATE_DATA;
- break;
- case kUnknown:
- default:
- updates_source = sync_pb::GetUpdatesCallerInfo::UNKNOWN;
- break;
- }
+void SyncerThread::DoSyncSessionJob(const SyncSessionJob& job) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ if (!ShouldRunJob(job)) {
+ LOG(WARNING) << "Dropping nudge at DoSyncSessionJob, source = "
+ << job.session->source().updates_source;
+ return;
}
- TypePayloadMap sync_source_types;
- if (model_types_with_payloads.empty()) {
- // No datatypes requested. This must be a poll so set all enabled datatypes.
- ModelSafeRoutingInfo routes;
- session_context_->registrar()->GetModelSafeRoutingInfo(&routes);
- sync_source_types = sessions::MakeTypePayloadMapFromRoutingInfo(routes,
- std::string());
+ if (job.purpose == SyncSessionJob::NUDGE) {
+ if (pending_nudge_.get() == NULL || pending_nudge_->session != job.session)
+ return; // Another nudge must have been scheduled in in the meantime.
+ pending_nudge_.reset();
+ }
+ VLOG(1) << "SyncerThread(" << this << ")" << " DoSyncSessionJob. job purpose "
+ << job.purpose;
+
+ SyncerStep begin(SYNCER_BEGIN);
+ SyncerStep end(SYNCER_END);
+ SetSyncerStepsForPurpose(job.purpose, &begin, &end);
+
+ bool has_more_to_sync = true;
+ while (ShouldRunJob(job) && has_more_to_sync) {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " SyncerThread: Calling SyncShare.";
+ // Synchronously perform the sync session from this thread.
+ syncer_->SyncShare(job.session.get(), begin, end);
+ has_more_to_sync = job.session->HasMoreToSync();
+ if (has_more_to_sync)
+ job.session->ResetTransientState();
+ }
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " SyncerThread: Done SyncShare looping.";
+ FinishSyncSessionJob(job);
+}
+
+void SyncerThread::UpdateCarryoverSessionState(const SyncSessionJob& old_job) {
+ if (old_job.purpose == SyncSessionJob::CONFIGURATION) {
+ // Whatever types were part of a configuration task will have had updates
+ // downloaded. For that reason, we make sure they get recorded in the
+ // event that they get disabled at a later time.
+ ModelSafeRoutingInfo r(session_context_->previous_session_routing_info());
+ if (!r.empty()) {
+ ModelSafeRoutingInfo temp_r;
+ ModelSafeRoutingInfo old_info(old_job.session->routing_info());
+ std::set_union(r.begin(), r.end(), old_info.begin(), old_info.end(),
+ std::insert_iterator<ModelSafeRoutingInfo>(temp_r, temp_r.begin()));
+ session_context_->set_previous_session_routing_info(temp_r);
+ }
} else {
- sync_source_types = model_types_with_payloads;
+ session_context_->set_previous_session_routing_info(
+ old_job.session->routing_info());
}
+}
- return SyncSourceInfo(updates_source, sync_source_types);
-}
-
-void SyncerThread::CreateSyncer(const std::string& dirname) {
- base::AutoLock lock(lock_);
- VLOG(1) << "Creating syncer up for: " << dirname;
- // The underlying database structure is ready, and we should create
- // the syncer.
- CHECK(vault_.syncer_ == NULL);
- vault_.syncer_ = new Syncer();
- vault_field_changed_.Broadcast();
-}
-
-// Sets |*connected| to false if it is currently true but |code| suggests that
-// the current network configuration and/or auth state cannot be used to make
-// forward progress, and user intervention (e.g changing server URL or auth
-// credentials) is likely necessary. If |*connected| is false, set it to true
-// if |code| suggests that we just recently made healthy contact with the
-// server.
-static inline void CheckConnected(bool* connected,
- HttpResponse::ServerConnectionCode code,
- base::ConditionVariable* condvar) {
- if (*connected) {
- // Note, be careful when adding cases here because if the SyncerThread
- // thinks there is no valid connection as determined by this method, it
- // will drop out of *all* forward progress sync loops (it won't poll and it
- // will queue up Talk notifications but not actually call SyncShare) until
- // some external action causes a ServerConnectionManager to broadcast that
- // a valid connection has been re-established.
- if (HttpResponse::CONNECTION_UNAVAILABLE == code ||
- HttpResponse::SYNC_AUTH_ERROR == code) {
- *connected = false;
- condvar->Broadcast();
+void SyncerThread::FinishSyncSessionJob(const SyncSessionJob& job) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ // Update timing information for how often datatypes are triggering nudges.
+ base::TimeTicks now = TimeTicks::Now();
+ if (!last_sync_session_end_time_.is_null()) {
+ ModelTypePayloadMap::const_iterator iter;
+ for (iter = job.session->source().types.begin();
+ iter != job.session->source().types.end();
+ ++iter) {
+ syncable::PostTimeToTypeHistogram(iter->first,
+ now - last_sync_session_end_time_);
}
+ }
+ last_sync_session_end_time_ = now;
+ UpdateCarryoverSessionState(job);
+ if (IsSyncingCurrentlySilenced()) {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " We are currently throttled. So not scheduling the next sync.";
+ SaveJob(job);
+ return; // Nothing to do.
+ }
+
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Updating the next polling time after SyncMain";
+ ScheduleNextSync(job);
+}
+
+void SyncerThread::ScheduleNextSync(const SyncSessionJob& old_job) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ DCHECK(!old_job.session->HasMoreToSync());
+ // Note: |num_server_changes_remaining| > 0 here implies that we received a
+ // broken response while trying to download all updates, because the Syncer
+ // will loop until this value is exhausted. Also, if unsynced_handles exist
+ // but HasMoreToSync is false, this implies that the Syncer determined no
+ // forward progress was possible at this time (an error, such as an HTTP
+ // 500, is likely to have occurred during commit).
+ const bool work_to_do =
+ old_job.session->status_controller()->num_server_changes_remaining() > 0
+ || old_job.session->status_controller()->unsynced_handles().size() > 0;
+ VLOG(1) << "SyncerThread(" << this << ")" << " syncer has work to do: "
+ << work_to_do;
+
+ AdjustPolling(&old_job);
+
+ // TODO(tim): Old impl had special code if notifications disabled. Needed?
+ if (!work_to_do) {
+ // Success implies backoff relief. Note that if this was a "one-off" job
+ // (i.e. purpose == SyncSessionJob::CLEAR_USER_DATA), if there was
+ // work_to_do before it ran this wont have changed, as jobs like this don't
+ // run a full sync cycle. So we don't need special code here.
+ wait_interval_.reset();
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Job suceeded so not scheduling more jobs";
+ return;
+ }
+
+ if (old_job.session->source().updates_source ==
+ GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION) {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Job failed with source continuation";
+ // We don't seem to have made forward progress. Start or extend backoff.
+ HandleConsecutiveContinuationError(old_job);
+ } else if (IsBackingOff()) {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " A nudge during backoff failed";
+ // We weren't continuing but we're in backoff; must have been a nudge.
+ DCHECK_EQ(SyncSessionJob::NUDGE, old_job.purpose);
+ DCHECK(!wait_interval_->had_nudge);
+ wait_interval_->had_nudge = true;
+ wait_interval_->timer.Reset();
} else {
- if (HttpResponse::SERVER_CONNECTION_OK == code) {
- *connected = true;
- condvar->Broadcast();
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " Failed. Schedule a job with continuation as source";
+ // We weren't continuing and we aren't in backoff. Schedule a normal
+ // continuation.
+ if (old_job.purpose == SyncSessionJob::CONFIGURATION) {
+ ScheduleConfigImpl(old_job.session->routing_info(),
+ old_job.session->workers(),
+ GetUpdatesFromNudgeSource(NUDGE_SOURCE_CONTINUATION));
+ } else {
+ // For all other purposes(nudge and poll) we schedule a retry nudge.
+ ScheduleNudgeImpl(TimeDelta::FromSeconds(0),
+ GetUpdatesFromNudgeSource(NUDGE_SOURCE_CONTINUATION),
+ old_job.session->source().types, false, FROM_HERE);
}
}
}
-void SyncerThread::WatchConnectionManager(ServerConnectionManager* conn_mgr) {
- conn_mgr_hookup_.reset(NewEventListenerHookup(conn_mgr->channel(), this,
- &SyncerThread::HandleServerConnectionEvent));
- CheckConnected(&vault_.connected_, conn_mgr->server_status(),
- &vault_field_changed_);
+void SyncerThread::AdjustPolling(const SyncSessionJob* old_job) {
+ DCHECK(thread_.IsRunning());
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+
+ TimeDelta poll = (!session_context_->notifications_enabled()) ?
+ syncer_short_poll_interval_seconds_ :
+ syncer_long_poll_interval_seconds_;
+ bool rate_changed = !poll_timer_.IsRunning() ||
+ poll != poll_timer_.GetCurrentDelay();
+
+ if (old_job && old_job->purpose != SyncSessionJob::POLL && !rate_changed)
+ poll_timer_.Reset();
+
+ if (!rate_changed)
+ return;
+
+ // Adjust poll rate.
+ poll_timer_.Stop();
+ poll_timer_.Start(poll, this, &SyncerThread::PollTimerCallback);
}
-void SyncerThread::HandleServerConnectionEvent(
- const ServerConnectionEvent& event) {
- if (ServerConnectionEvent::STATUS_CHANGED == event.what_happened) {
- base::AutoLock lock(lock_);
- CheckConnected(&vault_.connected_, event.connection_code,
- &vault_field_changed_);
+void SyncerThread::HandleConsecutiveContinuationError(
+ const SyncSessionJob& old_job) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ // This if conditions should be compiled out in retail builds.
+ if (IsBackingOff()) {
+ DCHECK(wait_interval_->timer.IsRunning() || old_job.is_canary_job);
}
+ SyncSession* old = old_job.session.get();
+ SyncSession* s(new SyncSession(session_context_.get(), this,
+ old->source(), old->routing_info(), old->workers()));
+ TimeDelta length = delay_provider_->GetDelay(
+ IsBackingOff() ? wait_interval_->length : TimeDelta::FromSeconds(1));
+
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " In handle continuation error. Old job purpose is "
+ << old_job.purpose;
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " In Handle continuation error. The time delta(ms) is: "
+ << length.InMilliseconds();
+
+ // This will reset the had_nudge variable as well.
+ wait_interval_.reset(new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF,
+ length));
+ if (old_job.purpose == SyncSessionJob::CONFIGURATION) {
+ SyncSessionJob job(old_job.purpose, TimeTicks::Now() + length,
+ make_linked_ptr(s), false, FROM_HERE);
+ wait_interval_->pending_configure_job.reset(new SyncSessionJob(job));
+ } else {
+ // We are not in configuration mode. So wait_interval's pending job
+ // should be null.
+ DCHECK(wait_interval_->pending_configure_job.get() == NULL);
+
+ // TODO(lipalani) - handle clear user data.
+ InitOrCoalescePendingJob(old_job);
+ }
+ wait_interval_->timer.Start(length, this, &SyncerThread::DoCanaryJob);
}
-int SyncerThread::GetRecommendedDelaySeconds(int base_delay_seconds) {
- if (base_delay_seconds >= kMaxBackoffSeconds)
- return kMaxBackoffSeconds;
+// static
+TimeDelta SyncerThread::GetRecommendedDelay(const TimeDelta& last_delay) {
+ if (last_delay.InSeconds() >= kMaxBackoffSeconds)
+ return TimeDelta::FromSeconds(kMaxBackoffSeconds);
// This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2
- int backoff_s =
- std::max(1, base_delay_seconds * kBackoffRandomizationFactor);
+ int64 backoff_s =
+ std::max(static_cast<int64>(1),
+ last_delay.InSeconds() * kBackoffRandomizationFactor);
// Flip a coin to randomize backoff interval by +/- 50%.
int rand_sign = base::RandInt(0, 1) * 2 - 1;
// Truncation is adequate for rounding here.
backoff_s = backoff_s +
- (rand_sign * (base_delay_seconds / kBackoffRandomizationFactor));
+ (rand_sign * (last_delay.InSeconds() / kBackoffRandomizationFactor));
// Cap the backoff interval.
- backoff_s = std::max(1, std::min(backoff_s, kMaxBackoffSeconds));
+ backoff_s = std::max(static_cast<int64>(1),
+ std::min(backoff_s, kMaxBackoffSeconds));
- return backoff_s;
+ return TimeDelta::FromSeconds(backoff_s);
}
-// Inputs and return value in milliseconds.
-int SyncerThread::CalculateSyncWaitTime(int last_interval, int user_idle_ms) {
- // syncer_polling_interval_ is in seconds
- int syncer_polling_interval_ms = syncer_polling_interval_ * 1000;
-
- // This is our default and lower bound.
- int next_wait = syncer_polling_interval_ms;
-
- // Get idle time, bounded by max wait.
- int idle = min(user_idle_ms, syncer_max_interval_);
+void SyncerThread::Stop() {
+ VLOG(1) << "SyncerThread(" << this << ")" << " stop called";
+ syncer_->RequestEarlyExit(); // Safe to call from any thread.
+ session_context_->connection_manager()->RemoveListener(this);
+ thread_.Stop();
+}
- // If the user has been idle for a while, we'll start decreasing the poll
- // rate.
- if (idle >= kPollBackoffThresholdMultiplier * syncer_polling_interval_ms) {
- next_wait = std::min(GetRecommendedDelaySeconds(
- last_interval / 1000), syncer_max_interval_ / 1000) * 1000;
+void SyncerThread::DoCanaryJob() {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Do canary job";
+ DoPendingJobIfPossible(true);
+}
+
+void SyncerThread::DoPendingJobIfPossible(bool is_canary_job) {
+ SyncSessionJob* job_to_execute = NULL;
+ if (mode_ == CONFIGURATION_MODE && wait_interval_.get()
+ && wait_interval_->pending_configure_job.get()) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Found pending configure job";
+ job_to_execute = wait_interval_->pending_configure_job.get();
+ } else if (mode_ == NORMAL_MODE && pending_nudge_.get()) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Found pending nudge job";
+ // Pending jobs mostly have time from the past. Reset it so this job
+ // will get executed.
+ if (pending_nudge_->scheduled_start < TimeTicks::Now())
+ pending_nudge_->scheduled_start = TimeTicks::Now();
+
+ scoped_ptr<SyncSession> session(CreateSyncSession(
+ pending_nudge_->session->source()));
+
+ // Also the routing info might have been changed since we cached the
+ // pending nudge. Update it by coalescing to the latest.
+ pending_nudge_->session->Coalesce(*(session.get()));
+ // The pending nudge would be cleared in the DoSyncSessionJob function.
+ job_to_execute = pending_nudge_.get();
}
- return next_wait;
-}
-
-// Called with mutex_ already locked.
-void SyncerThread::NudgeSyncImpl(
- int milliseconds_from_now,
- NudgeSource source,
- const TypePayloadMap& model_types_with_payloads) {
- // TODO(sync): Add the option to reset the backoff state machine.
- // This is needed so nudges that are a result of the user's desire
- // to download updates for a new data type can be satisfied quickly.
- if (vault_.current_wait_interval_.mode == WaitInterval::THROTTLED ||
- vault_.current_wait_interval_.had_nudge_during_backoff) {
- // Drop nudges on the floor if we've already had one since starting this
- // stage of exponential backoff or we are throttled.
- return;
+ if (job_to_execute != NULL) {
+ VLOG(1) << "SyncerThread(" << this << ")" << " Executing pending job";
+ SyncSessionJob copy = *job_to_execute;
+ copy.is_canary_job = is_canary_job;
+ DoSyncSessionJob(copy);
}
+}
- // Union the current TypePayloadMap with any from nudges that may have already
- // posted (coalesce the nudge datatype information).
- // TODO(tim): It seems weird to do this if the sources don't match up (e.g.
- // if pending_source is kLocal and |source| is kClearPrivateData).
- sessions::CoalescePayloads(&vault_.pending_nudge_types_,
- model_types_with_payloads);
-
- const TimeTicks nudge_time = TimeTicks::Now() +
- TimeDelta::FromMilliseconds(milliseconds_from_now);
- if (nudge_time <= vault_.pending_nudge_time_) {
- VLOG(1) << "Nudge for source " << source
- << " dropped due to existing later pending nudge";
- return;
- }
+SyncSession* SyncerThread::CreateSyncSession(const SyncSourceInfo& source) {
+ ModelSafeRoutingInfo routes;
+ std::vector<ModelSafeWorker*> workers;
+ session_context_->registrar()->GetModelSafeRoutingInfo(&routes);
+ session_context_->registrar()->GetWorkers(&workers);
+ SyncSourceInfo info(source);
- VLOG(1) << "Replacing pending nudge for source " << source
- << " at " << nudge_time.ToInternalValue();
+ SyncSession* session(new SyncSession(session_context_.get(), this, info,
+ routes, workers));
- vault_.pending_nudge_source_ = source;
- vault_.pending_nudge_time_ = nudge_time;
- vault_field_changed_.Broadcast();
+ return session;
}
-void SyncerThread::SetNotificationsEnabled(bool notifications_enabled) {
- base::AutoLock lock(lock_);
- session_context_->set_notifications_enabled(notifications_enabled);
+void SyncerThread::PollTimerCallback() {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ ModelSafeRoutingInfo r;
+ ModelTypePayloadMap types_with_payloads =
+ syncable::ModelTypePayloadMapFromRoutingInfo(r, std::string());
+ SyncSourceInfo info(GetUpdatesCallerInfo::PERIODIC, types_with_payloads);
+ SyncSession* s = CreateSyncSession(info);
+ ScheduleSyncSessionJob(TimeDelta::FromSeconds(0), SyncSessionJob::POLL, s,
+ FROM_HERE);
}
-// Returns the amount of time since the user last interacted with the computer,
-// in milliseconds
-int SyncerThread::UserIdleTime() {
-#if defined(OS_WIN)
- LASTINPUTINFO last_input_info;
- last_input_info.cbSize = sizeof(LASTINPUTINFO);
-
- // Get time in windows ticks since system start of last activity.
- BOOL b = ::GetLastInputInfo(&last_input_info);
- if (b == TRUE)
- return ::GetTickCount() - last_input_info.dwTime;
-#elif defined(OS_MACOSX)
- // It would be great to do something like:
- //
- // return 1000 *
- // CGEventSourceSecondsSinceLastEventType(
- // kCGEventSourceStateCombinedSessionState,
- // kCGAnyInputEventType);
- //
- // Unfortunately, CGEvent* lives in ApplicationServices, and we're a daemon
- // and can't link that high up the food chain. Thus this mucking in IOKit.
-
- io_service_t hid_service =
- IOServiceGetMatchingService(kIOMasterPortDefault,
- IOServiceMatching("IOHIDSystem"));
- if (!hid_service) {
- LOG(WARNING) << "Could not obtain IOHIDSystem";
- return 0;
- }
+void SyncerThread::Unthrottle() {
+ DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode);
+ VLOG(1) << "SyncerThread(" << this << ")" << " Unthrottled..";
+ DoCanaryJob();
+ wait_interval_.reset();
+}
- CFTypeRef object = IORegistryEntryCreateCFProperty(hid_service,
- CFSTR("HIDIdleTime"),
- kCFAllocatorDefault,
- 0);
- if (!object) {
- LOG(WARNING) << "Could not get IOHIDSystem's HIDIdleTime property";
- IOObjectRelease(hid_service);
- return 0;
- }
+void SyncerThread::Notify(SyncEngineEvent::EventCause cause) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ session_context_->NotifyListeners(SyncEngineEvent(cause));
+}
- int64 idle_time; // in nanoseconds
- Boolean success = false;
- if (CFGetTypeID(object) == CFNumberGetTypeID()) {
- success = CFNumberGetValue((CFNumberRef)object,
- kCFNumberSInt64Type,
- &idle_time);
- } else {
- LOG(WARNING) << "IOHIDSystem's HIDIdleTime property isn't a number!";
- }
+bool SyncerThread::IsBackingOff() const {
+ return wait_interval_.get() && wait_interval_->mode ==
+ WaitInterval::EXPONENTIAL_BACKOFF;
+}
- CFRelease(object);
- IOObjectRelease(hid_service);
+void SyncerThread::OnSilencedUntil(const base::TimeTicks& silenced_until) {
+ wait_interval_.reset(new WaitInterval(WaitInterval::THROTTLED,
+ silenced_until - TimeTicks::Now()));
+ wait_interval_->timer.Start(wait_interval_->length, this,
+ &SyncerThread::Unthrottle);
+}
- if (!success) {
- LOG(WARNING) << "Could not get IOHIDSystem's HIDIdleTime property's value";
- return 0;
- }
- return idle_time / 1000000; // nano to milli
-#elif defined(OS_LINUX)
- if (idle_query_.get())
- return idle_query_->IdleTime();
- return 0;
-#else
- static bool was_logged = false;
- if (!was_logged) {
- was_logged = true;
- VLOG(1) << "UserIdleTime unimplemented on this platform, synchronization "
- "will not throttle when user idle";
- }
-#endif
+bool SyncerThread::IsSyncingCurrentlySilenced() {
+ return wait_interval_.get() && wait_interval_->mode ==
+ WaitInterval::THROTTLED;
+}
- return 0;
+void SyncerThread::OnReceivedShortPollIntervalUpdate(
+ const base::TimeDelta& new_interval) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ syncer_short_poll_interval_seconds_ = new_interval;
+}
+
+void SyncerThread::OnReceivedLongPollIntervalUpdate(
+ const base::TimeDelta& new_interval) {
+ DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
+ syncer_long_poll_interval_seconds_ = new_interval;
+}
+
+void SyncerThread::OnShouldStopSyncingPermanently() {
+ VLOG(1) << "SyncerThread(" << this << ")"
+ << " OnShouldStopSyncingPermanently";
+ syncer_->RequestEarlyExit(); // Thread-safe.
+ Notify(SyncEngineEvent::STOP_SYNCING_PERMANENTLY);
+}
+
+void SyncerThread::OnServerConnectionEvent(
+ const ServerConnectionEvent2& event) {
+ thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SyncerThread::CheckServerConnectionManagerStatus,
+ event.connection_code));
+}
+
+void SyncerThread::set_notifications_enabled(bool notifications_enabled) {
+ session_context_->set_notifications_enabled(notifications_enabled);
}
-} // namespace browser_sync
+} // browser_sync
diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/syncer_thread.h
index b8bf724..47e2ac7 100644
--- a/chrome/browser/sync/engine/syncer_thread.h
+++ b/chrome/browser/sync/engine/syncer_thread.h
@@ -1,75 +1,177 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
// A class to run the syncer on a thread.
-// This is the default implementation of SyncerThread whose Stop implementation
-// does not support a timeout, but is greatly simplified.
#ifndef CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
#define CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
#pragma once
-#include <list>
-#include <string>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
-#include "base/synchronization/condition_variable.h"
+#include "base/callback.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/task.h"
#include "base/threading/thread.h"
#include "base/time.h"
-#include "base/synchronization/waitable_event.h"
-#include "chrome/browser/sync/engine/syncer_types.h"
+#include "base/timer.h"
+#include "chrome/browser/sync/engine/nudge_source.h"
+#include "chrome/browser/sync/engine/polling_constants.h"
+#include "chrome/browser/sync/engine/syncer.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "chrome/browser/sync/engine/net/server_connection_manager.h"
#include "chrome/browser/sync/sessions/sync_session.h"
-#include "chrome/browser/sync/syncable/model_type.h"
-#include "chrome/common/deprecated/event_sys-inl.h"
-
-#if defined(OS_LINUX)
-#include "chrome/browser/sync/engine/idle_query_linux.h"
-#endif
-
-class EventListenerHookup;
+#include "chrome/browser/sync/sessions/sync_session_context.h"
namespace browser_sync {
-class ModelSafeWorker;
-class ServerConnectionManager;
-class Syncer;
-class URLFactory;
struct ServerConnectionEvent;
-class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
- public sessions::SyncSession::Delegate {
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadTest, CalculateSyncWaitTime);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadTest, CalculatePollingWaitTime);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Polling);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Nudge);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, NudgeWithDataTypes);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest,
- NudgeWithDataTypesCoalesced);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, NudgeWithPayloads);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest,
- NudgeWithPayloadsCoalesced);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Throttling);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, AuthInvalid);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, Pause);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StartWhenNotConnected);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, PauseWhenNotConnected);
- FRIEND_TEST_ALL_PREFIXES(SyncerThreadWithSyncerTest, StopSyncPermanently);
- friend class SyncerThreadWithSyncerTest;
- friend class SyncerThreadFactory;
+class SyncerThread : public sessions::SyncSession::Delegate,
+ public ServerConnectionEventListener {
public:
- // Encapsulates the parameters that make up an interval on which the
- // syncer thread is sleeping.
+ enum Mode {
+ // In this mode, the thread only performs configuration tasks. This is
+ // designed to make the case where we want to download updates for a
+ // specific type only, and not continue syncing until we are moved into
+ // normal mode.
+ CONFIGURATION_MODE,
+ // Resumes polling and allows nudges, drops configuration tasks. Runs
+ // through entire sync cycle.
+ NORMAL_MODE,
+ };
+
+ // Takes ownership of both |context| and |syncer|.
+ SyncerThread(sessions::SyncSessionContext* context, Syncer* syncer);
+ virtual ~SyncerThread();
+
+ typedef Callback0::Type ModeChangeCallback;
+
+ // Change the mode of operation.
+ // We don't use a lock when changing modes, so we won't cause currently
+ // scheduled jobs to adhere to the new mode. We could protect it, but it
+ // doesn't buy very much as a) a session could already be in progress and it
+ // will continue no matter what, b) the scheduled sessions already contain
+ // all their required state and won't be affected by potential change at
+ // higher levels (i.e. the registrar), and c) we service tasks FIFO, so once
+ // the mode changes all future jobs will be run against the updated mode.
+ // If supplied, |callback| will be invoked when the mode has been
+ // changed to |mode| *from the SyncerThread*, and not from the caller
+ // thread.
+ void Start(Mode mode, ModeChangeCallback* callback);
+
+ // Joins on the thread as soon as possible (currently running session
+ // completes).
+ void Stop();
+
+ // The meat and potatoes.
+ void ScheduleNudge(const base::TimeDelta& delay, NudgeSource source,
+ const syncable::ModelTypeBitSet& types,
+ const tracked_objects::Location& nudge_location);
+ void ScheduleNudgeWithPayloads(
+ const base::TimeDelta& delay, NudgeSource source,
+ const syncable::ModelTypePayloadMap& types_with_payloads,
+ const tracked_objects::Location& nudge_location);
+ void ScheduleConfig(const syncable::ModelTypeBitSet& types);
+ void ScheduleClearUserData();
+
+ // Change status of notifications in the SyncSessionContext.
+ void set_notifications_enabled(bool notifications_enabled);
+
+ // DDOS avoidance function. Calculates how long we should wait before trying
+ // again after a failed sync attempt, where the last delay was |base_delay|.
+ // TODO(tim): Look at URLRequestThrottlerEntryInterface.
+ static base::TimeDelta GetRecommendedDelay(const base::TimeDelta& base_delay);
+
+ // SyncSession::Delegate implementation.
+ virtual void OnSilencedUntil(const base::TimeTicks& silenced_until);
+ virtual bool IsSyncingCurrentlySilenced();
+ virtual void OnReceivedShortPollIntervalUpdate(
+ const base::TimeDelta& new_interval);
+ virtual void OnReceivedLongPollIntervalUpdate(
+ const base::TimeDelta& new_interval);
+ virtual void OnShouldStopSyncingPermanently();
+
+ // ServerConnectionEventListener implementation.
+ // TODO(tim): schedule a nudge when valid connection detected? in 1 minute?
+ virtual void OnServerConnectionEvent(const ServerConnectionEvent2& event);
+
+ private:
+ enum JobProcessDecision {
+ // Indicates we should continue with the current job.
+ CONTINUE,
+ // Indicates that we should save it to be processed later.
+ SAVE,
+ // Indicates we should drop this job.
+ DROP,
+ };
+
+ struct SyncSessionJob {
+ // An enum used to describe jobs for scheduling purposes.
+ enum SyncSessionJobPurpose {
+ // Our poll timer schedules POLL jobs periodically based on a server
+ // assigned poll interval.
+ POLL,
+ // A nudge task can come from a variety of components needing to force
+ // a sync. The source is inferable from |session.source()|.
+ NUDGE,
+ // The user invoked a function in the UI to clear their entire account
+ // and stop syncing (globally).
+ CLEAR_USER_DATA,
+ // Typically used for fetching updates for a subset of the enabled types
+ // during initial sync or reconfiguration. We don't run all steps of
+ // the sync cycle for these (e.g. CleanupDisabledTypes is skipped).
+ CONFIGURATION,
+ };
+ SyncSessionJob();
+ SyncSessionJob(SyncSessionJobPurpose purpose, base::TimeTicks start,
+ linked_ptr<sessions::SyncSession> session, bool is_canary_job,
+ const tracked_objects::Location& nudge_location);
+ ~SyncSessionJob();
+ SyncSessionJobPurpose purpose;
+ base::TimeTicks scheduled_start;
+ linked_ptr<sessions::SyncSession> session;
+ bool is_canary_job;
+
+ // This is the location the nudge came from. used for debugging purpose.
+ // In case of multiple nudges getting coalesced this stores the first nudge
+ // that came in.
+ tracked_objects::Location nudge_location;
+ };
+ friend class SyncerThread2Test;
+ friend class SyncerThread2WhiteboxTest;
+
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
+ DropNudgeWhileExponentialBackOff);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, SaveNudge);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, ContinueNudge);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, DropPoll);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, ContinuePoll);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest, ContinueConfiguration);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
+ SaveConfigurationWhileThrottled);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
+ SaveNudgeWhileThrottled);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
+ ContinueClearUserDataUnderAllCircumstances);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
+ ContinueCanaryJobConfig);
+ FRIEND_TEST_ALL_PREFIXES(SyncerThread2WhiteboxTest,
+ ContinueNudgeWhileExponentialBackOff);
+
+ // A component used to get time delays associated with exponential backoff.
+ // Encapsulated into a class to facilitate testing.
+ class DelayProvider {
+ public:
+ DelayProvider();
+ virtual base::TimeDelta GetDelay(const base::TimeDelta& last_delay);
+ virtual ~DelayProvider();
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DelayProvider);
+ };
+
struct WaitInterval {
enum Mode {
- // A wait interval whose duration has not been affected by exponential
- // backoff. The base case for exponential backoff falls in to this case
- // (e.g when the exponent is 1). So far, we don't need a separate case.
- // NORMAL intervals are not nudge-rate limited.
- NORMAL,
// A wait interval whose duration has been affected by exponential
// backoff.
// EXPONENTIAL_BACKOFF intervals are nudge-rate limited to 1 per interval.
@@ -78,286 +180,162 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
// during such an interval.
THROTTLED,
};
+ WaitInterval();
+ ~WaitInterval();
Mode mode;
- // This bool is set to true if we have observed a nudge during during this
- // interval and mode == EXPONENTIAL_BACKOFF.
- bool had_nudge_during_backoff;
- base::TimeDelta poll_delta; // The wait duration until the next poll.
- WaitInterval() : mode(NORMAL), had_nudge_during_backoff(false) { }
+ // This bool is set to true if we have observed a nudge during this
+ // interval and mode == EXPONENTIAL_BACKOFF.
+ bool had_nudge;
+ base::TimeDelta length;
+ base::OneShotTimer<SyncerThread> timer;
+
+ // Configure jobs are saved only when backing off or throttling. So we
+ // expose the pointer here.
+ scoped_ptr<SyncSessionJob> pending_configure_job;
+ WaitInterval(Mode mode, base::TimeDelta length);
};
- enum NudgeSource {
- kUnknown = 0,
- kNotification,
- kLocal,
- kContinuation,
- kClearPrivateData
- };
- // Server can overwrite these values via client commands.
- // Standard short poll. This is used when XMPP is off.
- static const int kDefaultShortPollIntervalSeconds;
- // Long poll is used when XMPP is on.
- static const int kDefaultLongPollIntervalSeconds;
- // 30 minutes by default. If exponential backoff kicks in, this is the
- // longest possible poll interval.
- static const int kDefaultMaxPollIntervalMs;
- // Maximum interval for exponential backoff.
- static const int kMaxBackoffSeconds;
-
- explicit SyncerThread(sessions::SyncSessionContext* context);
- virtual ~SyncerThread();
+ // Helper to assemble a job and post a delayed task to sync.
+ void ScheduleSyncSessionJob(
+ const base::TimeDelta& delay,
+ SyncSessionJob::SyncSessionJobPurpose purpose,
+ sessions::SyncSession* session,
+ const tracked_objects::Location& nudge_location);
- virtual void WatchConnectionManager(ServerConnectionManager* conn_mgr);
-
- // Starts a syncer thread.
- // Returns true if it creates a thread or if there's currently a thread
- // running and false otherwise.
- virtual bool Start();
-
- // Stop processing. |max_wait| doesn't do anything in this version.
- virtual bool Stop(int max_wait);
-
- // Request that the thread pauses. Returns false if the request can
- // not be completed (e.g. the thread is not running). When the
- // thread actually pauses, a SyncEngineEvent::PAUSED event notification
- // will be sent to the relay channel.
- virtual bool RequestPause();
-
- // Request that the thread resumes from pause. Returns false if the
- // request can not be completed (e.g. the thread is not running or
- // is not currently paused). When the thread actually resumes, a
- // SyncEngineEvent::RESUMED event notification will be sent to the relay
- // channel.
- virtual bool RequestResume();
-
- // Nudges the syncer to sync with a delay specified. This API is for access
- // from the SyncerThread's controller and will cause a mutex lock.
- virtual void NudgeSyncer(int milliseconds_from_now, NudgeSource source);
-
- // Same as |NudgeSyncer|, but supports tracking the datatypes that caused
- // the nudge to occur.
- virtual void NudgeSyncerWithDataTypes(
- int milliseconds_from_now,
- NudgeSource source,
- const syncable::ModelTypeBitSet& model_types);
-
- // Same as |NudgeSyncer|, but supports including a payload for passing on to
- // the download updates command. Datatypes with payloads are also considered
- // to have caused a nudged to occur and treated accordingly.
- virtual void NudgeSyncerWithPayloads(
- int milliseconds_from_now,
- NudgeSource source,
- const sessions::TypePayloadMap& model_types_with_payloads);
-
- void SetNotificationsEnabled(bool notifications_enabled);
-
- // Call this when a directory is opened
- void CreateSyncer(const std::string& dirname);
-
- // DDOS avoidance function. The argument and return value is in seconds
- static int GetRecommendedDelaySeconds(int base_delay_seconds);
-
- protected:
- virtual void ThreadMain();
- void ThreadMainLoop();
-
- virtual void SetConnected(bool connected);
-
- virtual void SetSyncerPollingInterval(base::TimeDelta interval);
- virtual void SetSyncerShortPollInterval(base::TimeDelta interval);
-
- // Needed to emulate the behavior of pthread_create, which synchronously
- // started the thread and set the value of thread_running_ to true.
- // We can't quite match that because we asynchronously post the task,
- // which opens a window for Stop to get called before the task actually
- // makes it. To prevent this, we block Start() until we're sure it's ok.
- base::WaitableEvent thread_main_started_;
-
- // Handle of the running thread.
- base::Thread thread_;
+ // Invoke the Syncer to perform a sync.
+ void DoSyncSessionJob(const SyncSessionJob& job);
- // Fields that are modified / accessed by multiple threads go in this struct
- // for clarity and explicitness.
- struct ProtectedFields {
- ProtectedFields();
- ~ProtectedFields();
+ // Called after the Syncer has performed the sync represented by |job|, to
+ // reset our state.
+ void FinishSyncSessionJob(const SyncSessionJob& job);
- // False when we want to stop the thread.
- bool stop_syncer_thread_;
+ // Record important state that might be needed in future syncs, such as which
+ // data types may require cleanup.
+ void UpdateCarryoverSessionState(const SyncSessionJob& old_job);
- // True when a pause was requested.
- bool pause_requested_;
+ // Helper to FinishSyncSessionJob to schedule the next sync operation.
+ void ScheduleNextSync(const SyncSessionJob& old_job);
- // True when the thread is paused.
- bool paused_;
+ // Helper to configure polling intervals. Used by Start and ScheduleNextSync.
+ void AdjustPolling(const SyncSessionJob* old_job);
- Syncer* syncer_;
+ // Helper to ScheduleNextSync in case of consecutive sync errors.
+ void HandleConsecutiveContinuationError(const SyncSessionJob& old_job);
- // State of the server connection.
- bool connected_;
+ // Determines if it is legal to run |job| by checking current
+ // operational mode, backoff or throttling, freshness
+ // (so we don't make redundant syncs), and connection.
+ bool ShouldRunJob(const SyncSessionJob& job);
- // kUnknown if there is no pending nudge. (Theoretically, there
- // could be a pending nudge of type kUnknown, so it's better to
- // check pending_nudge_time_.)
- NudgeSource pending_nudge_source_;
+ // Decide whether we should CONTINUE, SAVE or DROP the job.
+ JobProcessDecision DecideOnJob(const SyncSessionJob& job);
- // Map of all datatypes that are requesting a nudge. Can be union
- // from multiple nudges that are coalesced. In addition, we
- // optionally track a payload associated with each datatype (most recent
- // payload overwrites old ones). These payloads are used by the download
- // updates command and can contain datatype specific information the server
- // might use.
- sessions::TypePayloadMap pending_nudge_types_;
+ // Decide on whether to CONTINUE, SAVE or DROP the job when we are in
+ // backoff mode.
+ JobProcessDecision DecideWhileInWaitInterval(const SyncSessionJob& job);
- // null iff there is no pending nudge.
- base::TimeTicks pending_nudge_time_;
+ // Saves the job for future execution. Note: It drops all the poll jobs.
+ void SaveJob(const SyncSessionJob& job);
- // The wait interval for to the current iteration of our main loop. This is
- // only written to by the syncer thread, and since the only reader from a
- // different thread (NudgeSync) is called at totally random times, we don't
- // really need to access mutually exclusively as the data races that exist
- // are intrinsic, but do so anyway and avoid using 'volatile'.
- WaitInterval current_wait_interval_;
- } vault_;
+ // Coalesces the current job with the pending nudge.
+ void InitOrCoalescePendingJob(const SyncSessionJob& job);
- // Gets signaled whenever a thread outside of the syncer thread changes a
- // protected field in the vault_.
- base::ConditionVariable vault_field_changed_;
+ // 'Impl' here refers to real implementation of public functions, running on
+ // |thread_|.
+ void StartImpl(Mode mode, ModeChangeCallback* callback);
+ void ScheduleNudgeImpl(
+ const base::TimeDelta& delay,
+ sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
+ const syncable::ModelTypePayloadMap& types_with_payloads,
+ bool is_canary_job, const tracked_objects::Location& nudge_location);
+ void ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info,
+ const std::vector<ModelSafeWorker*>& workers,
+ const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source);
+ void ScheduleClearUserDataImpl();
- // Used to lock everything in |vault_|.
- base::Lock lock_;
+ // Returns true if the client is currently in exponential backoff.
+ bool IsBackingOff() const;
- private:
- // Threshold multipler for how long before user should be considered idle.
- static const int kPollBackoffThresholdMultiplier = 10;
+ // Helper to signal all listeners registered with |session_context_|.
+ void Notify(SyncEngineEvent::EventCause cause);
- // SyncSession::Delegate implementation.
- virtual void OnSilencedUntil(const base::TimeTicks& silenced_until);
- virtual bool IsSyncingCurrentlySilenced();
- virtual void OnReceivedShortPollIntervalUpdate(
- const base::TimeDelta& new_interval);
- virtual void OnReceivedLongPollIntervalUpdate(
- const base::TimeDelta& new_interval);
- virtual void OnShouldStopSyncingPermanently();
+ // Callback to change backoff state.
+ void DoCanaryJob();
+ void Unthrottle();
- void HandleServerConnectionEvent(const ServerConnectionEvent& event);
-
- // Collect all local state required for a sync and build a SyncSession out of
- // it, reset state for the next time, and performs the sync cycle.
- // See |GetAndResetNudgeSource| for details on what 'reset' means.
- // |was_nudged| is set to true if the session returned is fulfilling a nudge.
- // Returns once the session is finished (HasMoreToSync returns false). The
- // caller owns the returned SyncSession.
- sessions::SyncSession* SyncMain(Syncer* syncer,
- bool was_throttled,
- bool continue_sync_cycle,
- bool* initial_sync_for_thread,
- bool* was_nudged);
-
- // Calculates the next sync wait time and exponential backoff state.
- // last_poll_wait is the time duration of the previous polling timeout which
- // was used. user_idle_milliseconds is updated by this method, and is a report
- // of the full amount of time since the last period of activity for the user.
- // The continue_sync_cycle parameter is used to determine whether or not we
- // are calculating a polling wait time that is a continuation of an sync cycle
- // which terminated while the syncer still had work to do. was_nudged is used
- // in case of exponential backoff so we only allow one nudge per backoff
- // interval.
- WaitInterval CalculatePollingWaitTime(
- int last_poll_wait, // in s
- int* user_idle_milliseconds,
- bool* continue_sync_cycle,
- bool was_nudged);
-
- // Helper to above function, considers effect of user idle time.
- virtual int CalculateSyncWaitTime(int last_wait, int user_idle_ms);
-
- // Resets the source tracking state to a clean slate and returns the current
- // state in a SyncSourceInfo.
- // The initial sync boolean is updated if read as a sentinel. The following
- // two methods work in concert to achieve this goal.
- // If |was_throttled| was true, this still discards elapsed nudges, but we
- // treat the request as a periodic poll rather than a nudge from a source.
- // Builds a SyncSourceInfo and returns whether a nudge occurred in the
- // |was_nudged| parameter.
- sessions::SyncSourceInfo GetAndResetNudgeSource(bool was_throttled,
- bool continue_sync_cycle,
- bool* initial_sync,
- bool* was_nudged);
-
- sessions::SyncSourceInfo MakeSyncSourceInfo(
- bool nudged,
- NudgeSource nudge_source,
- const sessions::TypePayloadMap& model_types_with_payloads,
- bool* initial_sync);
-
- int UserIdleTime();
-
- void WaitUntilConnectedOrQuit();
-
- // The thread will remain in this method until a resume is requested
- // or shutdown is started.
- void PauseUntilResumedOrQuit();
-
- void EnterPausedState();
-
- void ExitPausedState();
-
- // For unit tests only.
- virtual void DisableIdleDetection();
-
- // This sets all conditions for syncer thread termination but does not
- // actually join threads. It is expected that Stop will be called at some
- // time after to fully stop and clean up.
- void RequestSyncerExitAndSetThreadStopConditions();
+ // Executes the pending job. Called whenever an event occurs that may
+ // change conditions permitting a job to run. Like when network connection is
+ // re-established, mode changes etc.
+ void DoPendingJobIfPossible(bool is_canary_job);
- void Notify(SyncEngineEvent::EventCause cause);
+ // The pointer is owned by the caller.
+ browser_sync::sessions::SyncSession* CreateSyncSession(
+ const browser_sync::sessions::SyncSourceInfo& info);
+
+ // Creates a session for a poll and performs the sync.
+ void PollTimerCallback();
+
+ // Assign |start| and |end| to appropriate SyncerStep values for the
+ // specified |purpose|.
+ void SetSyncerStepsForPurpose(SyncSessionJob::SyncSessionJobPurpose purpose,
+ SyncerStep* start,
+ SyncerStep* end);
- scoped_ptr<EventListenerHookup> conn_mgr_hookup_;
+ // Initializes the hookup between the ServerConnectionManager and us.
+ void WatchConnectionManager();
+
+ // Used to update |server_connection_ok_|, see below.
+ void CheckServerConnectionManagerStatus(
+ HttpResponse::ServerConnectionCode code);
+
+ // Called once the first time thread_ is started to broadcast an initial
+ // session snapshot containing data like initial_sync_ended. Important when
+ // the client starts up and does not need to perform an initial sync.
+ void SendInitialSnapshot();
+
+ base::Thread thread_;
// Modifiable versions of kDefaultLongPollIntervalSeconds which can be
// updated by the server.
- int syncer_short_poll_interval_seconds_;
- int syncer_long_poll_interval_seconds_;
-
- // The time we wait between polls in seconds. This is used as lower bound on
- // our wait time. Updated once per loop from the command line flag.
- int syncer_polling_interval_;
-
- // The upper bound on the nominal wait between polls in seconds. Note that
- // this bounds the "nominal" poll interval, while the the actual interval
- // also takes previous failures into account.
- int syncer_max_interval_;
-
- // This causes syncer to start syncing ASAP. If the rate of requests is too
- // high the request will be silently dropped. mutex_ should be held when
- // this is called.
- void NudgeSyncImpl(
- int milliseconds_from_now,
- NudgeSource source,
- const sessions::TypePayloadMap& model_types_with_payloads);
-
-#if defined(OS_LINUX)
- // On Linux, we need this information in order to query idle time.
- scoped_ptr<IdleQueryLinux> idle_query_;
-#endif
+ base::TimeDelta syncer_short_poll_interval_seconds_;
+ base::TimeDelta syncer_long_poll_interval_seconds_;
- scoped_ptr<sessions::SyncSessionContext> session_context_;
+ // Periodic timer for polling. See AdjustPolling.
+ base::RepeatingTimer<SyncerThread> poll_timer_;
+
+ // The mode of operation. We don't use a lock, see Start(...) comment.
+ Mode mode_;
+
+ // TODO(tim): Bug 26339. This needs to track more than just time I think,
+ // since the nudges could be for different types. Current impl doesn't care.
+ base::TimeTicks last_sync_session_end_time_;
- // Set whenever the server instructs us to stop sending it requests until
- // a specified time, and reset for each call to SyncShare. (Note that the
- // WaitInterval::THROTTLED contract is such that we don't call SyncShare at
- // all until the "silenced until" embargo expires.)
- base::TimeTicks silenced_until_;
+ // Have we observed a valid server connection?
+ bool server_connection_ok_;
- // Useful for unit tests
- bool disable_idle_detection_;
+ // Tracks in-flight nudges so we can coalesce.
+ scoped_ptr<SyncSessionJob> pending_nudge_;
+
+ // Current wait state. Null if we're not in backoff and not throttled.
+ scoped_ptr<WaitInterval> wait_interval_;
+
+ scoped_ptr<DelayProvider> delay_provider_;
+
+ // Invoked to run through the sync cycle.
+ scoped_ptr<Syncer> syncer_;
+
+ scoped_ptr<sessions::SyncSessionContext> session_context_;
DISALLOW_COPY_AND_ASSIGN(SyncerThread);
};
+
} // namespace browser_sync
+// The SyncerThread manages its own internal thread and thus outlives it. We
+// don't need refcounting for posting tasks to this internal thread.
+DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::SyncerThread);
+
#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD_H_
diff --git a/chrome/browser/sync/engine/syncer_thread2.cc b/chrome/browser/sync/engine/syncer_thread2.cc
deleted file mode 100644
index ef46360..0000000
--- a/chrome/browser/sync/engine/syncer_thread2.cc
+++ /dev/null
@@ -1,589 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "chrome/browser/sync/engine/syncer_thread2.h"
-
-#include <algorithm>
-
-#include "base/rand_util.h"
-#include "chrome/browser/sync/engine/syncer.h"
-
-using base::TimeDelta;
-using base::TimeTicks;
-
-namespace browser_sync {
-
-using sessions::SyncSession;
-using sessions::SyncSessionSnapshot;
-using sessions::SyncSourceInfo;
-using sessions::TypePayloadMap;
-using syncable::ModelTypeBitSet;
-using sync_pb::GetUpdatesCallerInfo;
-
-namespace s3 {
-
-struct SyncerThread::WaitInterval {
- enum Mode {
- // A wait interval whose duration has been affected by exponential
- // backoff.
- // EXPONENTIAL_BACKOFF intervals are nudge-rate limited to 1 per interval.
- EXPONENTIAL_BACKOFF,
- // A server-initiated throttled interval. We do not allow any syncing
- // during such an interval.
- THROTTLED,
- };
- Mode mode;
-
- // This bool is set to true if we have observed a nudge during this
- // interval and mode == EXPONENTIAL_BACKOFF.
- bool had_nudge;
- base::TimeDelta length;
- base::OneShotTimer<SyncerThread> timer;
- WaitInterval(Mode mode, base::TimeDelta length);
-};
-
-struct SyncerThread::SyncSessionJob {
- SyncSessionJobPurpose purpose;
- base::TimeTicks scheduled_start;
- linked_ptr<sessions::SyncSession> session;
-};
-
-SyncerThread::DelayProvider::DelayProvider() {}
-SyncerThread::DelayProvider::~DelayProvider() {}
-
-TimeDelta SyncerThread::DelayProvider::GetDelay(
- const base::TimeDelta& last_delay) {
- return SyncerThread::GetRecommendedDelay(last_delay);
-}
-
-SyncerThread::WaitInterval::WaitInterval(Mode mode, TimeDelta length)
- : mode(mode), had_nudge(false), length(length) { }
-
-SyncerThread::SyncerThread(sessions::SyncSessionContext* context,
- Syncer* syncer)
- : thread_("SyncEngine_SyncerThread"),
- syncer_short_poll_interval_seconds_(
- TimeDelta::FromSeconds(kDefaultShortPollIntervalSeconds)),
- syncer_long_poll_interval_seconds_(
- TimeDelta::FromSeconds(kDefaultLongPollIntervalSeconds)),
- mode_(NORMAL_MODE),
- server_connection_ok_(false),
- delay_provider_(new DelayProvider()),
- syncer_(syncer),
- session_context_(context) {
-}
-
-SyncerThread::~SyncerThread() {
- DCHECK(!thread_.IsRunning());
-}
-
-void SyncerThread::Start(Mode mode) {
- if (!thread_.IsRunning() && !thread_.Start()) {
- NOTREACHED() << "Unable to start SyncerThread.";
- return;
- }
-
- thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SyncerThread::StartImpl, mode));
-}
-
-void SyncerThread::StartImpl(Mode mode) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- DCHECK(!session_context_->account_name().empty());
- DCHECK(syncer_.get());
- mode_ = mode;
- AdjustPolling(NULL); // Will kick start poll timer if needed.
-}
-
-bool SyncerThread::ShouldRunJob(SyncSessionJobPurpose purpose,
- const TimeTicks& scheduled_start) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
-
- // Check wait interval.
- if (wait_interval_.get()) {
- // TODO(tim): Consider different handling for CLEAR_USER_DATA (i.e. permit
- // when throttled).
- if (wait_interval_->mode == WaitInterval::THROTTLED)
- return false;
-
- DCHECK_EQ(wait_interval_->mode, WaitInterval::EXPONENTIAL_BACKOFF);
- if ((purpose != NUDGE) || wait_interval_->had_nudge)
- return false;
- }
-
- // Mode / purpose contract (See 'Mode' enum in header). Don't run jobs that
- // were intended for a normal sync if we are in configuration mode, and vice
- // versa.
- switch (mode_) {
- case CONFIGURATION_MODE:
- if (purpose != CONFIGURATION)
- return false;
- break;
- case NORMAL_MODE:
- if (purpose == CONFIGURATION)
- return false;
- break;
- default:
- NOTREACHED() << "Unknown SyncerThread Mode: " << mode_;
- return false;
- }
-
- // Continuation NUDGE tasks have priority over POLLs because they are the
- // only tasks that trigger exponential backoff, so this prevents them from
- // being starved from running (e.g. due to a very, very low poll interval,
- // such as 0ms). It's rare that this would ever matter in practice.
- if (purpose == POLL && (pending_nudge_.get() &&
- pending_nudge_->session->source().updates_source ==
- GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION)) {
- return false;
- }
-
- // Freshness condition.
- if (purpose == NUDGE &&
- (scheduled_start < last_sync_session_end_time_)) {
- return false;
- }
-
- return server_connection_ok_;
-}
-
-GetUpdatesCallerInfo::GetUpdatesSource GetUpdatesFromNudgeSource(
- NudgeSource source) {
- switch (source) {
- case NUDGE_SOURCE_NOTIFICATION:
- return GetUpdatesCallerInfo::NOTIFICATION;
- case NUDGE_SOURCE_LOCAL:
- return GetUpdatesCallerInfo::LOCAL;
- case NUDGE_SOURCE_CONTINUATION:
- return GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION;
- case NUDGE_SOURCE_UNKNOWN:
- return GetUpdatesCallerInfo::UNKNOWN;
- default:
- NOTREACHED();
- return GetUpdatesCallerInfo::UNKNOWN;
- }
-}
-
-// Functor for std::find_if to search by ModelSafeGroup.
-struct ModelSafeWorkerGroupIs {
- explicit ModelSafeWorkerGroupIs(ModelSafeGroup group) : group(group) {}
- bool operator()(ModelSafeWorker* w) {
- return group == w->GetModelSafeGroup();
- }
- ModelSafeGroup group;
-};
-
-void SyncerThread::ScheduleClearUserData() {
- if (!thread_.IsRunning()) {
- NOTREACHED();
- return;
- }
- thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SyncerThread::ScheduleClearUserDataImpl));
-}
-
-void SyncerThread::ScheduleNudge(const TimeDelta& delay,
- NudgeSource source, const ModelTypeBitSet& types) {
- if (!thread_.IsRunning()) {
- NOTREACHED();
- return;
- }
-
- TypePayloadMap types_with_payloads =
- sessions::MakeTypePayloadMapFromBitSet(types, std::string());
- thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SyncerThread::ScheduleNudgeImpl, delay, source,
- types_with_payloads));
-}
-
-void SyncerThread::ScheduleNudgeWithPayloads(const TimeDelta& delay,
- NudgeSource source, const TypePayloadMap& types_with_payloads) {
- if (!thread_.IsRunning()) {
- NOTREACHED();
- return;
- }
-
- thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SyncerThread::ScheduleNudgeImpl, delay, source,
- types_with_payloads));
-}
-
-void SyncerThread::ScheduleClearUserDataImpl() {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- SyncSession* session = new SyncSession(session_context_.get(), this,
- SyncSourceInfo(), ModelSafeRoutingInfo(),
- std::vector<ModelSafeWorker*>());
- ScheduleSyncSessionJob(TimeDelta::FromSeconds(0), CLEAR_USER_DATA, session);
-}
-
-void SyncerThread::ScheduleNudgeImpl(const TimeDelta& delay,
- NudgeSource source, const TypePayloadMap& types_with_payloads) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- TimeTicks rough_start = TimeTicks::Now() + delay;
-
- // Note we currently nudge for all types regardless of the ones incurring
- // the nudge. Doing different would throw off some syncer commands like
- // CleanupDisabledTypes. We may want to change this in the future.
- ModelSafeRoutingInfo routes;
- std::vector<ModelSafeWorker*> workers;
- session_context_->registrar()->GetModelSafeRoutingInfo(&routes);
- session_context_->registrar()->GetWorkers(&workers);
- SyncSourceInfo info(GetUpdatesFromNudgeSource(source),
- types_with_payloads);
-
- scoped_ptr<SyncSession> session(new SyncSession(
- session_context_.get(), this, info, routes, workers));
-
- if (pending_nudge_.get()) {
- if (IsBackingOff() && delay > TimeDelta::FromSeconds(1))
- return;
-
- pending_nudge_->session->Coalesce(*session.get());
- if (!IsBackingOff()) {
- return;
- } else {
- // Re-schedule the current pending nudge.
- SyncSession* s = pending_nudge_->session.get();
- session.reset(new SyncSession(s->context(), s->delegate(), s->source(),
- s->routing_info(), s->workers()));
- pending_nudge_.reset();
- }
- }
- ScheduleSyncSessionJob(delay, NUDGE, session.release());
-}
-
-// Helper to extract the routing info and workers corresponding to types in
-// |types| from |registrar|.
-void GetModelSafeParamsForTypes(const ModelTypeBitSet& types,
- ModelSafeWorkerRegistrar* registrar, ModelSafeRoutingInfo* routes,
- std::vector<ModelSafeWorker*>* workers) {
- ModelSafeRoutingInfo r_tmp;
- std::vector<ModelSafeWorker*> w_tmp;
- registrar->GetModelSafeRoutingInfo(&r_tmp);
- registrar->GetWorkers(&w_tmp);
-
- typedef std::vector<ModelSafeWorker*>::const_iterator iter;
- for (size_t i = syncable::FIRST_REAL_MODEL_TYPE; i < types.size(); ++i) {
- if (!types.test(i))
- continue;
- syncable::ModelType t = syncable::ModelTypeFromInt(i);
- DCHECK_EQ(1U, r_tmp.count(t));
- (*routes)[t] = r_tmp[t];
- iter it = std::find_if(w_tmp.begin(), w_tmp.end(),
- ModelSafeWorkerGroupIs(r_tmp[t]));
- if (it != w_tmp.end())
- workers->push_back(*it);
- else
- NOTREACHED();
- }
-
- iter it = std::find_if(w_tmp.begin(), w_tmp.end(),
- ModelSafeWorkerGroupIs(GROUP_PASSIVE));
- if (it != w_tmp.end())
- workers->push_back(*it);
- else
- NOTREACHED();
-}
-
-void SyncerThread::ScheduleConfig(const ModelTypeBitSet& types) {
- if (!thread_.IsRunning()) {
- NOTREACHED();
- return;
- }
-
- ModelSafeRoutingInfo routes;
- std::vector<ModelSafeWorker*> workers;
- GetModelSafeParamsForTypes(types, session_context_->registrar(),
- &routes, &workers);
-
- thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
- this, &SyncerThread::ScheduleConfigImpl, routes, workers));
-}
-
-void SyncerThread::ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info,
- const std::vector<ModelSafeWorker*>& workers) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
-
- // TODO(tim): config-specific GetUpdatesCallerInfo value?
- SyncSession* session = new SyncSession(session_context_.get(), this,
- SyncSourceInfo(GetUpdatesCallerInfo::FIRST_UPDATE,
- sessions::MakeTypePayloadMapFromRoutingInfo(
- routing_info, std::string())),
- routing_info, workers);
- ScheduleSyncSessionJob(TimeDelta::FromSeconds(0), CONFIGURATION, session);
-}
-
-void SyncerThread::ScheduleSyncSessionJob(const base::TimeDelta& delay,
- SyncSessionJobPurpose purpose, sessions::SyncSession* session) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- SyncSessionJob job = {purpose, TimeTicks::Now() + delay,
- make_linked_ptr(session)};
- if (purpose == NUDGE) {
- DCHECK(!pending_nudge_.get() || pending_nudge_->session.get() == session);
- pending_nudge_.reset(new SyncSessionJob(job));
- }
- MessageLoop::current()->PostDelayedTask(FROM_HERE, NewRunnableMethod(this,
- &SyncerThread::DoSyncSessionJob, job), delay.InMilliseconds());
-}
-
-void SyncerThread::SetSyncerStepsForPurpose(SyncSessionJobPurpose purpose,
- SyncerStep* start, SyncerStep* end) {
- *end = SYNCER_END;
- switch (purpose) {
- case CONFIGURATION:
- *start = DOWNLOAD_UPDATES;
- *end = APPLY_UPDATES;
- return;
- case CLEAR_USER_DATA:
- *start = CLEAR_PRIVATE_DATA;
- return;
- case NUDGE:
- case POLL:
- *start = SYNCER_BEGIN;
- return;
- default:
- NOTREACHED();
- }
-}
-
-void SyncerThread::DoSyncSessionJob(const SyncSessionJob& job) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
-
- if (job.purpose == NUDGE) {
- DCHECK(pending_nudge_.get());
- if (pending_nudge_->session != job.session)
- return; // Another nudge must have been scheduled in in the meantime.
- pending_nudge_.reset();
- }
-
- SyncerStep begin(SYNCER_BEGIN);
- SyncerStep end(SYNCER_END);
- SetSyncerStepsForPurpose(job.purpose, &begin, &end);
-
- bool has_more_to_sync = true;
- bool did_job = false;
- while (ShouldRunJob(job.purpose, job.scheduled_start) && has_more_to_sync) {
- VLOG(1) << "SyncerThread: Calling SyncShare.";
- did_job = true;
- // Synchronously perform the sync session from this thread.
- syncer_->SyncShare(job.session.get(), begin, end);
- has_more_to_sync = job.session->HasMoreToSync();
- if (has_more_to_sync)
- job.session->ResetTransientState();
- }
- VLOG(1) << "SyncerThread: Done SyncShare looping.";
- if (did_job)
- FinishSyncSessionJob(job);
-}
-
-void SyncerThread::FinishSyncSessionJob(const SyncSessionJob& job) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- // Update timing information for how often datatypes are triggering nudges.
- base::TimeTicks now = TimeTicks::Now();
- if (!last_sync_session_end_time_.is_null()) {
- TypePayloadMap::const_iterator iter;
- for (iter = job.session->source().types.begin();
- iter != job.session->source().types.end();
- ++iter) {
- syncable::PostTimeToTypeHistogram(iter->first,
- now - last_sync_session_end_time_);
- }
- }
- last_sync_session_end_time_ = now;
- if (IsSyncingCurrentlySilenced())
- return; // Nothing to do.
-
- VLOG(1) << "Updating the next polling time after SyncMain";
- ScheduleNextSync(job);
-}
-
-void SyncerThread::ScheduleNextSync(const SyncSessionJob& old_job) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- DCHECK(!old_job.session->HasMoreToSync());
- // Note: |num_server_changes_remaining| > 0 here implies that we received a
- // broken response while trying to download all updates, because the Syncer
- // will loop until this value is exhausted. Also, if unsynced_handles exist
- // but HasMoreToSync is false, this implies that the Syncer determined no
- // forward progress was possible at this time (an error, such as an HTTP
- // 500, is likely to have occurred during commit).
- const bool work_to_do =
- old_job.session->status_controller()->num_server_changes_remaining() > 0
- || old_job.session->status_controller()->unsynced_handles().size() > 0;
- VLOG(1) << "syncer has work to do: " << work_to_do;
-
- AdjustPolling(&old_job);
-
- // TODO(tim): Old impl had special code if notifications disabled. Needed?
- if (!work_to_do) {
- // Success implies backoff relief. Note that if this was a "one-off" job
- // (i.e. purpose == CLEAR_USER_DATA), if there was work_to_do before it
- // ran this wont have changed, as jobs like this don't run a full sync
- // cycle. So we don't need special code here.
- wait_interval_.reset();
- return;
- }
-
- if (old_job.session->source().updates_source ==
- GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION) {
- // We don't seem to have made forward progress. Start or extend backoff.
- HandleConsecutiveContinuationError(old_job);
- } else if (IsBackingOff()) {
- // We weren't continuing but we're in backoff; must have been a nudge.
- DCHECK_EQ(NUDGE, old_job.purpose);
- DCHECK(!wait_interval_->had_nudge);
- wait_interval_->had_nudge = true;
- wait_interval_->timer.Reset();
- } else {
- // We weren't continuing and we aren't in backoff. Schedule a normal
- // continuation.
- ScheduleNudgeImpl(TimeDelta::FromSeconds(0), NUDGE_SOURCE_CONTINUATION,
- old_job.session->source().types);
- }
-}
-
-void SyncerThread::AdjustPolling(const SyncSessionJob* old_job) {
- DCHECK(thread_.IsRunning());
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
-
- TimeDelta poll = (!session_context_->notifications_enabled()) ?
- syncer_short_poll_interval_seconds_ :
- syncer_long_poll_interval_seconds_;
- bool rate_changed = !poll_timer_.IsRunning() ||
- poll != poll_timer_.GetCurrentDelay();
-
- if (old_job && old_job->purpose != POLL && !rate_changed)
- poll_timer_.Reset();
-
- if (!rate_changed)
- return;
-
- // Adjust poll rate.
- poll_timer_.Stop();
- poll_timer_.Start(poll, this, &SyncerThread::PollTimerCallback);
-}
-
-void SyncerThread::HandleConsecutiveContinuationError(
- const SyncSessionJob& old_job) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- DCHECK(!IsBackingOff() || !wait_interval_->timer.IsRunning());
- SyncSession* old = old_job.session.get();
- SyncSession* s(new SyncSession(session_context_.get(), this,
- old->source(), old->routing_info(), old->workers()));
- TimeDelta length = delay_provider_->GetDelay(
- IsBackingOff() ? wait_interval_->length : TimeDelta::FromSeconds(1));
- wait_interval_.reset(new WaitInterval(WaitInterval::EXPONENTIAL_BACKOFF,
- length));
- SyncSessionJob job = {NUDGE, TimeTicks::Now() + length,
- make_linked_ptr(s)};
- pending_nudge_.reset(new SyncSessionJob(job));
- wait_interval_->timer.Start(length, this, &SyncerThread::DoCanaryJob);
-}
-
-// static
-TimeDelta SyncerThread::GetRecommendedDelay(const TimeDelta& last_delay) {
- if (last_delay.InSeconds() >= kMaxBackoffSeconds)
- return TimeDelta::FromSeconds(kMaxBackoffSeconds);
-
- // This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2
- int64 backoff_s =
- std::max(static_cast<int64>(1),
- last_delay.InSeconds() * kBackoffRandomizationFactor);
-
- // Flip a coin to randomize backoff interval by +/- 50%.
- int rand_sign = base::RandInt(0, 1) * 2 - 1;
-
- // Truncation is adequate for rounding here.
- backoff_s = backoff_s +
- (rand_sign * (last_delay.InSeconds() / kBackoffRandomizationFactor));
-
- // Cap the backoff interval.
- backoff_s = std::max(static_cast<int64>(1),
- std::min(backoff_s, kMaxBackoffSeconds));
-
- return TimeDelta::FromSeconds(backoff_s);
-}
-
-void SyncerThread::Stop() {
- syncer_->RequestEarlyExit(); // Safe to call from any thread.
- thread_.Stop();
- Notify(SyncEngineEvent::SYNCER_THREAD_EXITING);
-}
-
-void SyncerThread::DoCanaryJob() {
- DCHECK(pending_nudge_.get());
- wait_interval_->had_nudge = false;
- SyncSessionJob copy = {pending_nudge_->purpose,
- pending_nudge_->scheduled_start,
- pending_nudge_->session};
- DoSyncSessionJob(copy);
-}
-
-void SyncerThread::PollTimerCallback() {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- ModelSafeRoutingInfo r;
- std::vector<ModelSafeWorker*> w;
- session_context_->registrar()->GetModelSafeRoutingInfo(&r);
- session_context_->registrar()->GetWorkers(&w);
- TypePayloadMap types_with_payloads =
- sessions::MakeTypePayloadMapFromRoutingInfo(r, std::string());
- SyncSourceInfo info(GetUpdatesCallerInfo::PERIODIC, types_with_payloads);
- SyncSession* s = new SyncSession(session_context_.get(), this, info, r, w);
- ScheduleSyncSessionJob(TimeDelta::FromSeconds(0), POLL, s);
-}
-
-void SyncerThread::Unthrottle() {
- DCHECK_EQ(WaitInterval::THROTTLED, wait_interval_->mode);
- wait_interval_.reset();
-}
-
-void SyncerThread::Notify(SyncEngineEvent::EventCause cause) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- session_context_->NotifyListeners(SyncEngineEvent(cause));
-}
-
-bool SyncerThread::IsBackingOff() const {
- return wait_interval_.get() && wait_interval_->mode ==
- WaitInterval::EXPONENTIAL_BACKOFF;
-}
-
-void SyncerThread::OnSilencedUntil(const base::TimeTicks& silenced_until) {
- wait_interval_.reset(new WaitInterval(WaitInterval::THROTTLED,
- silenced_until - TimeTicks::Now()));
- wait_interval_->timer.Start(wait_interval_->length, this,
- &SyncerThread::Unthrottle);
-}
-
-bool SyncerThread::IsSyncingCurrentlySilenced() {
- return wait_interval_.get() && wait_interval_->mode ==
- WaitInterval::THROTTLED;
-}
-
-void SyncerThread::OnReceivedShortPollIntervalUpdate(
- const base::TimeDelta& new_interval) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- syncer_short_poll_interval_seconds_ = new_interval;
-}
-
-void SyncerThread::OnReceivedLongPollIntervalUpdate(
- const base::TimeDelta& new_interval) {
- DCHECK_EQ(MessageLoop::current(), thread_.message_loop());
- syncer_long_poll_interval_seconds_ = new_interval;
-}
-
-void SyncerThread::OnShouldStopSyncingPermanently() {
- syncer_->RequestEarlyExit(); // Thread-safe.
- Notify(SyncEngineEvent::STOP_SYNCING_PERMANENTLY);
-}
-
-void SyncerThread::OnServerConnectionEvent(
- const ServerConnectionEvent& event) {
- NOTIMPLEMENTED();
-}
-
-void SyncerThread::set_notifications_enabled(bool notifications_enabled) {
- session_context_->set_notifications_enabled(notifications_enabled);
-}
-
-} // s3
-} // browser_sync
diff --git a/chrome/browser/sync/engine/syncer_thread2.h b/chrome/browser/sync/engine/syncer_thread2.h
deleted file mode 100644
index 89e0c96..0000000
--- a/chrome/browser/sync/engine/syncer_thread2.h
+++ /dev/null
@@ -1,227 +0,0 @@
-// Copyright (c) 2011 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.
-//
-// A class to run the syncer on a thread.
-#ifndef CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD2_H_
-#define CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD2_H_
-#pragma once
-
-#include "base/linked_ptr.h"
-#include "base/observer_list.h"
-#include "base/scoped_ptr.h"
-#include "base/task.h"
-#include "base/threading/thread.h"
-#include "base/time.h"
-#include "base/timer.h"
-#include "chrome/browser/sync/engine/nudge_source.h"
-#include "chrome/browser/sync/engine/polling_constants.h"
-#include "chrome/browser/sync/engine/syncer.h"
-#include "chrome/browser/sync/sessions/sync_session.h"
-#include "chrome/browser/sync/sessions/sync_session_context.h"
-
-namespace browser_sync {
-
-struct ServerConnectionEvent;
-
-namespace s3 {
-
-class SyncerThread : public sessions::SyncSession::Delegate {
- public:
- enum Mode {
- // In this mode, the thread only performs configuration tasks. This is
- // designed to make the case where we want to download updates for a
- // specific type only, and not continue syncing until we are moved into
- // normal mode.
- CONFIGURATION_MODE,
- // Resumes polling and allows nudges, drops configuration tasks. Runs
- // through entire sync cycle.
- NORMAL_MODE,
- };
-
- // Takes ownership of both |context| and |syncer|.
- SyncerThread(sessions::SyncSessionContext* context, Syncer* syncer);
- virtual ~SyncerThread();
-
- // Change the mode of operation.
- // We don't use a lock when changing modes, so we won't cause currently
- // scheduled jobs to adhere to the new mode. We could protect it, but it
- // doesn't buy very much as a) a session could already be in progress and it
- // will continue no matter what, b) the scheduled sessions already contain
- // all their required state and won't be affected by potential change at
- // higher levels (i.e. the registrar), and c) we service tasks FIFO, so once
- // the mode changes all future jobs will be run against the updated mode.
- void Start(Mode mode);
-
- // Joins on the thread as soon as possible (currently running session
- // completes).
- void Stop();
-
- // The meat and potatoes.
- void ScheduleNudge(const base::TimeDelta& delay, NudgeSource source,
- const syncable::ModelTypeBitSet& types);
- void ScheduleNudgeWithPayloads(
- const base::TimeDelta& delay, NudgeSource source,
- const sessions::TypePayloadMap& types_with_payloads);
- void ScheduleConfig(const syncable::ModelTypeBitSet& types);
- void ScheduleClearUserData();
-
- // Change status of notifications in the SyncSessionContext.
- void set_notifications_enabled(bool notifications_enabled);
-
- // DDOS avoidance function. Calculates how long we should wait before trying
- // again after a failed sync attempt, where the last delay was |base_delay|.
- // TODO(tim): Look at URLRequestThrottlerEntryInterface.
- static base::TimeDelta GetRecommendedDelay(const base::TimeDelta& base_delay);
-
- // SyncSession::Delegate implementation.
- virtual void OnSilencedUntil(const base::TimeTicks& silenced_until);
- virtual bool IsSyncingCurrentlySilenced();
- virtual void OnReceivedShortPollIntervalUpdate(
- const base::TimeDelta& new_interval);
- virtual void OnReceivedLongPollIntervalUpdate(
- const base::TimeDelta& new_interval);
- virtual void OnShouldStopSyncingPermanently();
-
- private:
- friend class SyncerThread2Test;
-
- // State pertaining to exponential backoff or throttling periods.
- struct WaitInterval;
-
- // An enum used to describe jobs for scheduling purposes.
- enum SyncSessionJobPurpose {
- // Our poll timer schedules POLL jobs periodically based on a server
- // assigned poll interval.
- POLL,
- // A nudge task can come from a variety of components needing to force
- // a sync. The source is inferable from |session.source()|.
- NUDGE,
- // The user invoked a function in the UI to clear their entire account
- // and stop syncing (globally).
- CLEAR_USER_DATA,
- // Typically used for fetching updates for a subset of the enabled types
- // during initial sync or reconfiguration. We don't run all steps of
- // the sync cycle for these (e.g. CleanupDisabledTypes is skipped).
- CONFIGURATION,
- };
-
- // Internal state for every sync task that is scheduled.
- struct SyncSessionJob;
-
- // A component used to get time delays associated with exponential backoff.
- // Encapsulated into a class to facilitate testing.
- class DelayProvider {
- public:
- DelayProvider();
- virtual base::TimeDelta GetDelay(const base::TimeDelta& last_delay);
- virtual ~DelayProvider();
- private:
- DISALLOW_COPY_AND_ASSIGN(DelayProvider);
- };
-
- // Helper to assemble a job and post a delayed task to sync.
- void ScheduleSyncSessionJob(const base::TimeDelta& delay,
- SyncSessionJobPurpose purpose,
- sessions::SyncSession* session);
-
- // Invoke the Syncer to perform a sync.
- void DoSyncSessionJob(const SyncSessionJob& job);
-
- // Called after the Syncer has performed the sync represented by |job|, to
- // reset our state.
- void FinishSyncSessionJob(const SyncSessionJob& job);
-
- // Helper to FinishSyncSessionJob to schedule the next sync operation.
- void ScheduleNextSync(const SyncSessionJob& old_job);
-
- // Helper to configure polling intervals. Used by Start and ScheduleNextSync.
- void AdjustPolling(const SyncSessionJob* old_job);
-
- // Helper to ScheduleNextSync in case of consecutive sync errors.
- void HandleConsecutiveContinuationError(const SyncSessionJob& old_job);
-
- // Determines if it is legal to run a sync job for |purpose| at
- // |scheduled_start|. This checks current operational mode, backoff or
- // throttling, freshness (so we don't make redundant syncs), and connection.
- bool ShouldRunJob(SyncSessionJobPurpose purpose,
- const base::TimeTicks& scheduled_start);
-
- // 'Impl' here refers to real implementation of public functions, running on
- // |thread_|.
- void StartImpl(Mode mode);
- void ScheduleNudgeImpl(const base::TimeDelta& delay,
- NudgeSource source,
- const sessions::TypePayloadMap& types_with_payloads);
- void ScheduleConfigImpl(const ModelSafeRoutingInfo& routing_info,
- const std::vector<ModelSafeWorker*>& workers);
- void ScheduleClearUserDataImpl();
-
- // Returns true if the client is currently in exponential backoff.
- bool IsBackingOff() const;
-
- // Helper to signal all listeners registered with |session_context_|.
- void Notify(SyncEngineEvent::EventCause cause);
-
- // ServerConnectionEventListener implementation.
- // TODO(tim): schedule a nudge when valid connection detected? in 1 minute?
- virtual void OnServerConnectionEvent(const ServerConnectionEvent& event);
-
- // Callback to change backoff state.
- void DoCanaryJob();
- void Unthrottle();
-
- // Creates a session for a poll and performs the sync.
- void PollTimerCallback();
-
- // Assign |start| and |end| to appropriate SyncerStep values for the
- // specified |purpose|.
- void SetSyncerStepsForPurpose(SyncSessionJobPurpose purpose,
- SyncerStep* start,
- SyncerStep* end);
-
- base::Thread thread_;
-
- // Modifiable versions of kDefaultLongPollIntervalSeconds which can be
- // updated by the server.
- base::TimeDelta syncer_short_poll_interval_seconds_;
- base::TimeDelta syncer_long_poll_interval_seconds_;
-
- // Periodic timer for polling. See AdjustPolling.
- base::RepeatingTimer<SyncerThread> poll_timer_;
-
- // The mode of operation. We don't use a lock, see Start(...) comment.
- Mode mode_;
-
- // TODO(tim): Bug 26339. This needs to track more than just time I think,
- // since the nudges could be for different types. Current impl doesn't care.
- base::TimeTicks last_sync_session_end_time_;
-
- // Have we observed a valid server connection?
- bool server_connection_ok_;
-
- // Tracks in-flight nudges so we can coalesce.
- scoped_ptr<SyncSessionJob> pending_nudge_;
-
- // Current wait state. Null if we're not in backoff and not throttled.
- scoped_ptr<WaitInterval> wait_interval_;
-
- scoped_ptr<DelayProvider> delay_provider_;
-
- // Invoked to run through the sync cycle.
- scoped_ptr<Syncer> syncer_;
-
- scoped_ptr<sessions::SyncSessionContext> session_context_;
-
- DISALLOW_COPY_AND_ASSIGN(SyncerThread);
-};
-
-} // namespace s3
-
-} // namespace browser_sync
-
-// The SyncerThread manages its own internal thread and thus outlives it. We
-// don't need refcounting for posting tasks to this internal thread.
-DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::s3::SyncerThread);
-
-#endif // CHROME_BROWSER_SYNC_ENGINE_SYNCER_THREAD2_H_
diff --git a/chrome/browser/sync/engine/syncer_thread2_unittest.cc b/chrome/browser/sync/engine/syncer_thread2_unittest.cc
index 0a54d02..d231a06 100644
--- a/chrome/browser/sync/engine/syncer_thread2_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_thread2_unittest.cc
@@ -6,8 +6,9 @@
#include "base/test/test_timeouts.h"
#include "chrome/browser/sync/engine/mock_model_safe_workers.h"
#include "chrome/browser/sync/engine/syncer.h"
-#include "chrome/browser/sync/engine/syncer_thread2.h"
+#include "chrome/browser/sync/engine/syncer_thread.h"
#include "chrome/browser/sync/sessions/test_util.h"
+#include "chrome/test/sync/engine/mock_connection_manager.h"
#include "chrome/test/sync/engine/test_directory_setter_upper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -36,8 +37,6 @@ class MockSyncer : public Syncer {
SyncerStep));
};
-namespace s3 {
-
// Used when tests want to record syncing activity to examine later.
struct SyncShareRecords {
std::vector<TimeTicks> times;
@@ -46,7 +45,6 @@ struct SyncShareRecords {
// Convenient to use in tests wishing to analyze SyncShare calls over time.
static const size_t kMinNumSamples = 5;
-
class SyncerThread2Test : public testing::Test {
public:
class MockDelayProvider : public SyncerThread::DelayProvider {
@@ -59,18 +57,33 @@ class SyncerThread2Test : public testing::Test {
syncer_ = new MockSyncer();
delay_ = NULL;
registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks());
- context_ = new SyncSessionContext(NULL, syncdb_.manager(),
+ connection_.reset(new MockConnectionManager(syncdb_.manager(), "Test"));
+ connection_->SetServerReachable();
+ context_ = new SyncSessionContext(connection_.get(), syncdb_.manager(),
+ registrar_.get(), std::vector<SyncEngineEventListener*>());
+ context_->set_notifications_enabled(true);
+ context_->set_account_name("Test");
+ syncer_thread_.reset(new SyncerThread(context_, syncer_));
+ }
+
+ virtual void SetUpWithTypes(syncable::ModelTypeBitSet types) {
+ syncdb_.SetUp();
+ syncer_ = new MockSyncer();
+ delay_ = NULL;
+ registrar_.reset(MockModelSafeWorkerRegistrar::PassiveForTypes(types));
+ connection_.reset(new MockConnectionManager(syncdb_.manager(), "Test"));
+ connection_->SetServerReachable();
+ context_ = new SyncSessionContext(connection_.get(), syncdb_.manager(),
registrar_.get(), std::vector<SyncEngineEventListener*>());
context_->set_notifications_enabled(true);
context_->set_account_name("Test");
syncer_thread_.reset(new SyncerThread(context_, syncer_));
- // TODO(tim): Once the SCM is hooked up, remove this.
- syncer_thread_->server_connection_ok_ = true;
}
SyncerThread* syncer_thread() { return syncer_thread_.get(); }
MockSyncer* syncer() { return syncer_; }
MockDelayProvider* delay() { return delay_; }
+ MockConnectionManager* connection() { return connection_.get(); }
TimeDelta zero() { return TimeDelta::FromSeconds(0); }
TimeDelta timeout() {
return TimeDelta::FromMilliseconds(TestTimeouts::action_timeout_ms());
@@ -96,8 +109,9 @@ class SyncerThread2Test : public testing::Test {
bool GetBackoffAndResetTest(base::WaitableEvent* done) {
syncable::ModelTypeBitSet nudge_types;
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types,
+ FROM_HERE);
done->TimedWait(timeout());
TearDown();
done->Reset();
@@ -130,12 +144,17 @@ class SyncerThread2Test : public testing::Test {
event->Signal();
}
- // Compare a ModelTyepBitSet to a TypePayloadMap, ignoring payload values.
- bool CompareModelTypeBitSetToTypePayloadMap(
+ static void QuitMessageLoop() {
+ MessageLoop::current()->Quit();
+ }
+
+ // Compare a ModelTypeBitSet to a ModelTypePayloadMap, ignoring
+ // payload values.
+ bool CompareModelTypeBitSetToModelTypePayloadMap(
const syncable::ModelTypeBitSet& lhs,
- const sessions::TypePayloadMap& rhs) {
+ const syncable::ModelTypePayloadMap& rhs) {
size_t count = 0;
- for (sessions::TypePayloadMap::const_iterator i = rhs.begin();
+ for (syncable::ModelTypePayloadMap::const_iterator i = rhs.begin();
i != rhs.end(); ++i, ++count) {
if (!lhs.test(i->first))
return false;
@@ -145,8 +164,11 @@ class SyncerThread2Test : public testing::Test {
return true;
}
+ SyncSessionContext* context() { return context_; }
+
private:
scoped_ptr<SyncerThread> syncer_thread_;
+ scoped_ptr<MockConnectionManager> connection_;
SyncSessionContext* context_;
MockSyncer* syncer_;
MockDelayProvider* delay_;
@@ -178,7 +200,7 @@ ACTION_P(SignalEvent, event) {
// Test nudge scheduling.
TEST_F(SyncerThread2Test, Nudge) {
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
base::WaitableEvent done(false, false);
SyncShareRecords records;
syncable::ModelTypeBitSet model_types;
@@ -188,11 +210,12 @@ TEST_F(SyncerThread2Test, Nudge) {
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&records, 1U, &done))))
.RetiresOnSaturation();
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types,
+ FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, records.snapshots.size());
- EXPECT_TRUE(CompareModelTypeBitSetToTypePayloadMap(model_types,
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types,
records.snapshots[0]->source.types));
EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
records.snapshots[0]->source.updates_source);
@@ -204,19 +227,162 @@ TEST_F(SyncerThread2Test, Nudge) {
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&records2, 1U, &done))));
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types,
+ FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, records2.snapshots.size());
- EXPECT_TRUE(CompareModelTypeBitSetToTypePayloadMap(model_types,
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types,
records2.snapshots[0]->source.types));
EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
records2.snapshots[0]->source.updates_source);
}
+// Make sure a regular config command is scheduled fine in the absence of any
+// errors.
+TEST_F(SyncerThread2Test, Config) {
+ base::WaitableEvent done(false, false);
+ SyncShareRecords records;
+ syncable::ModelTypeBitSet model_types;
+ model_types[syncable::BOOKMARKS] = true;
+
+ EXPECT_CALL(*syncer(), SyncShare(_,_,_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done))));
+
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
+
+ syncer_thread()->ScheduleConfig(model_types);
+ done.TimedWait(timeout());
+
+ EXPECT_EQ(1U, records.snapshots.size());
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types,
+ records.snapshots[0]->source.types));
+ EXPECT_EQ(GetUpdatesCallerInfo::FIRST_UPDATE,
+ records.snapshots[0]->source.updates_source);
+}
+
+// Simulate a failure and make sure the config request is retried.
+TEST_F(SyncerThread2Test, ConfigWithBackingOff) {
+ base::WaitableEvent done(false, false);
+ base::WaitableEvent* dummy = NULL;
+ UseMockDelayProvider();
+ EXPECT_CALL(*delay(), GetDelay(_))
+ .WillRepeatedly(Return(TimeDelta::FromMilliseconds(1)));
+ SyncShareRecords records;
+ syncable::ModelTypeBitSet model_types;
+ model_types[syncable::BOOKMARKS] = true;
+
+ EXPECT_CALL(*syncer(), SyncShare(_,_,_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
+ WithArg<0>(RecordSyncShare(&records, 1U, dummy))))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done))));
+
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
+
+ syncer_thread()->ScheduleConfig(model_types);
+ done.TimedWait(timeout());
+
+ EXPECT_EQ(2U, records.snapshots.size());
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types,
+ records.snapshots[1]->source.types));
+ EXPECT_EQ(GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION,
+ records.snapshots[1]->source.updates_source);
+}
+
+// Issue 2 config commands. Second one right after the first has failed
+// and make sure LATEST is executed.
+TEST_F(SyncerThread2Test, MultipleConfigWithBackingOff) {
+ syncable::ModelTypeBitSet model_types1, model_types2;
+ model_types1[syncable::BOOKMARKS] = true;
+ model_types2[syncable::AUTOFILL] = true;
+ SetUpWithTypes(model_types1 | model_types2);
+ base::WaitableEvent done(false, false);
+ base::WaitableEvent done1(false, false);
+ base::WaitableEvent* dummy = NULL;
+ UseMockDelayProvider();
+ EXPECT_CALL(*delay(), GetDelay(_))
+ .WillRepeatedly(Return(TimeDelta::FromMilliseconds(30)));
+ SyncShareRecords records;
+
+ EXPECT_CALL(*syncer(), SyncShare(_,_,_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
+ WithArg<0>(RecordSyncShare(&records, 1U, dummy))))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done1))))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done))));
+
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
+
+ syncer_thread()->ScheduleConfig(model_types1);
+
+ // done1 indicates the first config failed.
+ done1.TimedWait(timeout());
+ syncer_thread()->ScheduleConfig(model_types2);
+ done.TimedWait(timeout());
+
+ EXPECT_EQ(3U, records.snapshots.size());
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types2,
+ records.snapshots[2]->source.types));
+ EXPECT_EQ(GetUpdatesCallerInfo::FIRST_UPDATE,
+ records.snapshots[2]->source.updates_source);
+}
+
+// Issue a nudge when the config has failed. Make sure both the config and
+// nudge are executed.
+TEST_F(SyncerThread2Test, NudgeWithConfigWithBackingOff) {
+ syncable::ModelTypeBitSet model_types;
+ model_types[syncable::BOOKMARKS] = true;
+ base::WaitableEvent done(false, false);
+ base::WaitableEvent done1(false, false);
+ base::WaitableEvent done2(false, false);
+ base::WaitableEvent* dummy = NULL;
+ UseMockDelayProvider();
+ EXPECT_CALL(*delay(), GetDelay(_))
+ .WillRepeatedly(Return(TimeDelta::FromMilliseconds(50)));
+ SyncShareRecords records;
+
+ EXPECT_CALL(*syncer(), SyncShare(_,_,_))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
+ WithArg<0>(RecordSyncShare(&records, 1U, dummy))))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateCommitFailed),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done1))))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done2))))
+ .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ WithArg<0>(RecordSyncShare(&records, 1U, &done))));
+
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
+
+ syncer_thread()->ScheduleConfig(model_types);
+ done1.TimedWait(timeout());
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, model_types,
+ FROM_HERE);
+
+ // done2 indicates config suceeded. Now change the mode so nudge can execute.
+ done2.TimedWait(timeout());
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ done.TimedWait(timeout());
+ EXPECT_EQ(4U, records.snapshots.size());
+
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types,
+ records.snapshots[2]->source.types));
+ EXPECT_EQ(GetUpdatesCallerInfo::SYNC_CYCLE_CONTINUATION,
+ records.snapshots[2]->source.updates_source);
+
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(model_types,
+ records.snapshots[3]->source.types));
+ EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
+ records.snapshots[3]->source.updates_source);
+
+}
+
+
// Test that nudges are coalesced.
TEST_F(SyncerThread2Test, NudgeCoalescing) {
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
base::WaitableEvent done(false, false);
SyncShareRecords r;
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
@@ -226,28 +392,31 @@ TEST_F(SyncerThread2Test, NudgeCoalescing) {
types1[syncable::BOOKMARKS] = true;
types2[syncable::AUTOFILL] = true;
types3[syncable::THEMES] = true;
- TimeDelta delay = TimeDelta::FromMilliseconds(20);
+ TimeDelta delay = TimeDelta::FromMilliseconds(
+ TestTimeouts::tiny_timeout_ms());
TimeTicks optimal_time = TimeTicks::Now() + delay;
- syncer_thread()->ScheduleNudge(delay, NUDGE_SOURCE_UNKNOWN, types1);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types2);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3);
+ syncer_thread()->ScheduleNudge(delay, NUDGE_SOURCE_UNKNOWN, types1,
+ FROM_HERE);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types2,
+ FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, r.snapshots.size());
EXPECT_GE(r.times[0], optimal_time);
- EXPECT_TRUE(CompareModelTypeBitSetToTypePayloadMap(types1 | types2 | types3,
- r.snapshots[0]->source.types));
- EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION,
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(
+ types1 | types2, r.snapshots[0]->source.types));
+ EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
r.snapshots[0]->source.updates_source);
SyncShareRecords r2;
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&r2, 1U, &done))));
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_NOTIFICATION, types3,
+ FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, r2.snapshots.size());
- EXPECT_TRUE(CompareModelTypeBitSetToTypePayloadMap(types3,
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(types3,
r2.snapshots[0]->source.types));
EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION,
r2.snapshots[0]->source.updates_source);
@@ -255,10 +424,10 @@ TEST_F(SyncerThread2Test, NudgeCoalescing) {
// Test nudge scheduling.
TEST_F(SyncerThread2Test, NudgeWithPayloads) {
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
base::WaitableEvent done(false, false);
SyncShareRecords records;
- sessions::TypePayloadMap model_types_with_payloads;
+ syncable::ModelTypePayloadMap model_types_with_payloads;
model_types_with_payloads[syncable::BOOKMARKS] = "test";
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
@@ -266,7 +435,7 @@ TEST_F(SyncerThread2Test, NudgeWithPayloads) {
WithArg<0>(RecordSyncShare(&records, 1U, &done))))
.RetiresOnSaturation();
syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_LOCAL,
- model_types_with_payloads);
+ model_types_with_payloads, FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, records.snapshots.size());
@@ -282,7 +451,7 @@ TEST_F(SyncerThread2Test, NudgeWithPayloads) {
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&records2, 1U, &done))));
syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_LOCAL,
- model_types_with_payloads);
+ model_types_with_payloads, FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, records2.snapshots.size());
@@ -293,34 +462,32 @@ TEST_F(SyncerThread2Test, NudgeWithPayloads) {
// Test that nudges are coalesced.
TEST_F(SyncerThread2Test, NudgeWithPayloadsCoalescing) {
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
base::WaitableEvent done(false, false);
SyncShareRecords r;
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&r, 1U, &done))));
- sessions::TypePayloadMap types1, types2, types3;
+ syncable::ModelTypePayloadMap types1, types2, types3;
types1[syncable::BOOKMARKS] = "test1";
types2[syncable::AUTOFILL] = "test2";
types3[syncable::THEMES] = "test3";
- TimeDelta delay = TimeDelta::FromMilliseconds(20);
+ TimeDelta delay = TimeDelta::FromMilliseconds(
+ TestTimeouts::tiny_timeout_ms());
TimeTicks optimal_time = TimeTicks::Now() + delay;
syncer_thread()->ScheduleNudgeWithPayloads(delay, NUDGE_SOURCE_UNKNOWN,
- types1);
+ types1, FROM_HERE);
syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_LOCAL,
- types2);
- syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_NOTIFICATION,
- types3);
+ types2, FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, r.snapshots.size());
EXPECT_GE(r.times[0], optimal_time);
- sessions::TypePayloadMap coalesced_types;
- sessions::CoalescePayloads(&coalesced_types, types1);
- sessions::CoalescePayloads(&coalesced_types, types2);
- sessions::CoalescePayloads(&coalesced_types, types3);
+ syncable::ModelTypePayloadMap coalesced_types;
+ syncable::CoalescePayloads(&coalesced_types, types1);
+ syncable::CoalescePayloads(&coalesced_types, types2);
EXPECT_EQ(coalesced_types, r.snapshots[0]->source.types);
- EXPECT_EQ(GetUpdatesCallerInfo::NOTIFICATION,
+ EXPECT_EQ(GetUpdatesCallerInfo::LOCAL,
r.snapshots[0]->source.updates_source);
SyncShareRecords r2;
@@ -328,7 +495,7 @@ TEST_F(SyncerThread2Test, NudgeWithPayloadsCoalescing) {
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&r2, 1U, &done))));
syncer_thread()->ScheduleNudgeWithPayloads(zero(), NUDGE_SOURCE_NOTIFICATION,
- types3);
+ types3, FROM_HERE);
done.TimedWait(timeout());
EXPECT_EQ(1U, r2.snapshots.size());
EXPECT_EQ(types3, r2.snapshots[0]->source.types);
@@ -347,7 +514,7 @@ TEST_F(SyncerThread2Test, Polling) {
WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
done.TimedWait(timeout());
syncer_thread()->Stop();
@@ -366,7 +533,7 @@ TEST_F(SyncerThread2Test, PollNotificationsDisabled) {
WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
TimeTicks optimal_start = TimeTicks::Now() + poll_interval;
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
done.TimedWait(timeout());
syncer_thread()->Stop();
@@ -387,7 +554,7 @@ TEST_F(SyncerThread2Test, PollIntervalUpdate) {
WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
TimeTicks optimal_start = TimeTicks::Now() + poll1 + poll2;
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
done.TimedWait(timeout());
syncer_thread()->Stop();
@@ -396,13 +563,14 @@ TEST_F(SyncerThread2Test, PollIntervalUpdate) {
// Test that a sync session is run through to completion.
TEST_F(SyncerThread2Test, HasMoreToSync) {
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
base::WaitableEvent done(false, false);
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
.WillOnce(Invoke(sessions::test_util::SimulateHasMoreToSync))
.WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
SignalEvent(&done)));
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet());
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(),
+ FROM_HERE);
done.TimedWait(timeout());
// If more nudges are scheduled, they'll be waited on by TearDown, and would
// cause our expectation to break.
@@ -419,11 +587,12 @@ TEST_F(SyncerThread2Test, ThrottlingDoesThrottle) {
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
.WillOnce(WithArg<0>(sessions::test_util::SimulateThrottled(throttle)));
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types,
+ FROM_HERE);
FlushLastTask(&done);
- syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
syncer_thread()->ScheduleConfig(types);
FlushLastTask(&done);
}
@@ -445,7 +614,7 @@ TEST_F(SyncerThread2Test, ThrottlingExpires) {
WithArg<0>(RecordSyncShare(&records, kMinNumSamples, &done))));
TimeTicks optimal_start = TimeTicks::Now() + poll + throttle1;
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
done.TimedWait(timeout());
syncer_thread()->Stop();
@@ -460,13 +629,15 @@ TEST_F(SyncerThread2Test, ConfigurationMode) {
base::WaitableEvent* dummy = NULL;
syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
EXPECT_CALL(*syncer(), SyncShare(_,_,_))
- .WillOnce(DoAll(Invoke(sessions::test_util::SimulateSuccess),
+ .WillOnce((Invoke(sessions::test_util::SimulateSuccess),
WithArg<0>(RecordSyncShare(&records, 1U, dummy))));
- syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
syncable::ModelTypeBitSet nudge_types;
nudge_types[syncable::AUTOFILL] = true;
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types,
+ FROM_HERE);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, nudge_types,
+ FROM_HERE);
syncable::ModelTypeBitSet config_types;
config_types[syncable::BOOKMARKS] = true;
@@ -477,7 +648,7 @@ TEST_F(SyncerThread2Test, ConfigurationMode) {
syncer_thread()->Stop();
EXPECT_EQ(1U, records.snapshots.size());
- EXPECT_TRUE(CompareModelTypeBitSetToTypePayloadMap(config_types,
+ EXPECT_TRUE(CompareModelTypeBitSetToModelTypePayloadMap(config_types,
records.snapshots[0]->source.types));
}
@@ -538,7 +709,7 @@ TEST_F(SyncerThread2Test, BackoffDropsJobs) {
EXPECT_CALL(*delay(), GetDelay(_))
.WillRepeatedly(Return(TimeDelta::FromDays(1)));
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
ASSERT_TRUE(done.TimedWait(timeout()));
done.Reset();
@@ -555,7 +726,8 @@ TEST_F(SyncerThread2Test, BackoffDropsJobs) {
// We schedule a nudge with enough delay (10X poll interval) that at least
// one or two polls would have taken place. The nudge should succeed.
- syncer_thread()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types);
+ syncer_thread()->ScheduleNudge(poll * 10, NUDGE_SOURCE_LOCAL, types,
+ FROM_HERE);
ASSERT_TRUE(done.TimedWait(timeout()));
done.Reset();
@@ -568,13 +740,15 @@ TEST_F(SyncerThread2Test, BackoffDropsJobs) {
EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(0);
EXPECT_CALL(*delay(), GetDelay(_)).Times(0);
- syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
syncer_thread()->ScheduleConfig(types);
FlushLastTask(&done);
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types,
+ FROM_HERE);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, types,
+ FROM_HERE);
FlushLastTask(&done);
}
@@ -604,7 +778,7 @@ TEST_F(SyncerThread2Test, BackoffElevation) {
.RetiresOnSaturation();
EXPECT_CALL(*delay(), GetDelay(Eq(fourth))).WillOnce(Return(fifth));
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
ASSERT_TRUE(done.TimedWait(timeout()));
EXPECT_GE(r.times[2] - r.times[1], second);
@@ -632,7 +806,7 @@ TEST_F(SyncerThread2Test, BackoffRelief) {
// Optimal start for the post-backoff poll party.
TimeTicks optimal_start = TimeTicks::Now() + poll + backoff;
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
done.TimedWait(timeout());
syncer_thread()->Stop();
@@ -671,8 +845,9 @@ TEST_F(SyncerThread2Test, SyncerSteps) {
base::WaitableEvent done(false, false);
EXPECT_CALL(*syncer(), SyncShare(_, SYNCER_BEGIN, SYNCER_END))
.Times(1);
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
- syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet());
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(),
+ FROM_HERE);
FlushLastTask(&done);
syncer_thread()->Stop();
Mock::VerifyAndClearExpectations(syncer());
@@ -680,15 +855,14 @@ TEST_F(SyncerThread2Test, SyncerSteps) {
// ClearUserData.
EXPECT_CALL(*syncer(), SyncShare(_, CLEAR_PRIVATE_DATA, SYNCER_END))
.Times(1);
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
syncer_thread()->ScheduleClearUserData();
FlushLastTask(&done);
syncer_thread()->Stop();
Mock::VerifyAndClearExpectations(syncer());
-
// Configuration.
EXPECT_CALL(*syncer(), SyncShare(_, DOWNLOAD_UPDATES, APPLY_UPDATES));
- syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE);
+ syncer_thread()->Start(SyncerThread::CONFIGURATION_MODE, NULL);
syncer_thread()->ScheduleConfig(ModelTypeBitSet());
FlushLastTask(&done);
syncer_thread()->Stop();
@@ -700,7 +874,7 @@ TEST_F(SyncerThread2Test, SyncerSteps) {
.WillRepeatedly(SignalEvent(&done));
const TimeDelta poll(TimeDelta::FromMilliseconds(10));
syncer_thread()->OnReceivedLongPollIntervalUpdate(poll);
- syncer_thread()->Start(SyncerThread::NORMAL_MODE);
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
done.TimedWait(timeout());
syncer_thread()->Stop();
Mock::VerifyAndClearExpectations(syncer());
@@ -714,15 +888,46 @@ TEST_F(SyncerThread2Test, DISABLED_NoConfigDuringNormal) {
// Test that starting the syncer thread without a valid connection doesn't
// break things when a connection is detected.
-// Test config tasks don't run during normal mode.
-// TODO(tim): Implement this test and then the functionality!
-TEST_F(SyncerThread2Test, DISABLED_StartWhenNotConnected) {
+TEST_F(SyncerThread2Test, StartWhenNotConnected) {
+ base::WaitableEvent done(false, false);
+ MessageLoop cur;
+ connection()->SetServerNotReachable();
+ EXPECT_CALL(*syncer(), SyncShare(_,_,_)).WillOnce(SignalEvent(&done));
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(),
+ FROM_HERE);
+ FlushLastTask(&done);
+
+ connection()->SetServerReachable();
+ cur.PostTask(FROM_HERE, NewRunnableFunction(
+ &SyncerThread2Test::QuitMessageLoop));
+ cur.Run();
+ // By now, the server connection event should have been posted to the
+ // SyncerThread.
+ FlushLastTask(&done);
+ done.TimedWait(timeout());
+}
+
+TEST_F(SyncerThread2Test, SetsPreviousRoutingInfo) {
+ base::WaitableEvent done(false, false);
+ ModelSafeRoutingInfo info;
+ EXPECT_TRUE(info == context()->previous_session_routing_info());
+ ModelSafeRoutingInfo expected;
+ context()->registrar()->GetModelSafeRoutingInfo(&expected);
+ ASSERT_FALSE(expected.empty());
+ EXPECT_CALL(*syncer(), SyncShare(_,_,_)).Times(1);
+
+ syncer_thread()->Start(SyncerThread::NORMAL_MODE, NULL);
+ syncer_thread()->ScheduleNudge(zero(), NUDGE_SOURCE_LOCAL, ModelTypeBitSet(),
+ FROM_HERE);
+ FlushLastTask(&done);
+ syncer_thread()->Stop();
+
+ EXPECT_TRUE(expected == context()->previous_session_routing_info());
}
-} // namespace s3
} // namespace browser_sync
// SyncerThread won't outlive the test!
-DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::s3::SyncerThread2Test);
-
+DISABLE_RUNNABLE_METHOD_REFCOUNT(browser_sync::SyncerThread2Test);
diff --git a/chrome/browser/sync/engine/syncer_thread2_whitebox_unittest.cc b/chrome/browser/sync/engine/syncer_thread2_whitebox_unittest.cc
new file mode 100644
index 0000000..e041aaa
--- /dev/null
+++ b/chrome/browser/sync/engine/syncer_thread2_whitebox_unittest.cc
@@ -0,0 +1,231 @@
+// Copyright (c) 2011 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.
+
+#include "base/time.h"
+#include "chrome/browser/sync/engine/mock_model_safe_workers.h"
+#include "chrome/browser/sync/engine/syncer_thread.h"
+#include "chrome/browser/sync/sessions/sync_session_context.h"
+#include "chrome/browser/sync/sessions/test_util.h"
+#include "chrome/test/sync/engine/mock_connection_manager.h"
+#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using base::TimeDelta;
+using base::TimeTicks;
+
+namespace browser_sync {
+using sessions::SyncSessionContext;
+using browser_sync::Syncer;
+
+class SyncerThread2WhiteboxTest : public testing::Test {
+ public:
+ virtual void SetUp() {
+ syncdb_.SetUp();
+ Syncer* syncer = new Syncer();
+ registrar_.reset(MockModelSafeWorkerRegistrar::PassiveBookmarks());
+ context_ = new SyncSessionContext(connection_.get(), syncdb_.manager(),
+ registrar_.get(), std::vector<SyncEngineEventListener*>());
+ context_->set_notifications_enabled(true);
+ context_->set_account_name("Test");
+ syncer_thread_.reset(new SyncerThread(context_, syncer));
+ }
+
+ virtual void TearDown() {
+ syncdb_.TearDown();
+ }
+
+ void SetMode(SyncerThread::Mode mode) {
+ syncer_thread_->mode_ = mode;
+ }
+
+ void SetLastSyncedTime(base::TimeTicks ticks) {
+ syncer_thread_->last_sync_session_end_time_ = ticks;
+ }
+
+ void SetServerConnection(bool connected) {
+ syncer_thread_->server_connection_ok_ = connected;
+ }
+
+ void ResetWaitInterval() {
+ syncer_thread_->wait_interval_.reset();
+ }
+
+ void SetWaitIntervalToThrottled() {
+ syncer_thread_->wait_interval_.reset(new SyncerThread::WaitInterval(
+ SyncerThread::WaitInterval::THROTTLED, TimeDelta::FromSeconds(1)));
+ }
+
+ void SetWaitIntervalToExponentialBackoff() {
+ syncer_thread_->wait_interval_.reset(
+ new SyncerThread::WaitInterval(
+ SyncerThread::WaitInterval::EXPONENTIAL_BACKOFF,
+ TimeDelta::FromSeconds(1)));
+ }
+
+ SyncerThread::JobProcessDecision DecideOnJob(
+ const SyncerThread::SyncSessionJob& job) {
+ return syncer_thread_->DecideOnJob(job);
+ }
+
+ void InitializeSyncerOnNormalMode() {
+ SetMode(SyncerThread::NORMAL_MODE);
+ ResetWaitInterval();
+ SetServerConnection(true);
+ SetLastSyncedTime(base::TimeTicks::Now());
+ }
+
+ SyncerThread::JobProcessDecision CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::SyncSessionJobPurpose purpose) {
+ struct SyncerThread::SyncSessionJob job;
+ job.purpose = purpose;
+ job.scheduled_start = TimeTicks::Now();
+ return DecideOnJob(job);
+ }
+
+ protected:
+ scoped_ptr<SyncerThread> syncer_thread_;
+
+ private:
+ scoped_ptr<MockConnectionManager> connection_;
+ SyncSessionContext* context_;
+ //MockDelayProvider* delay_;
+ scoped_ptr<MockModelSafeWorkerRegistrar> registrar_;
+ MockDirectorySetterUpper syncdb_;
+};
+
+TEST_F(SyncerThread2WhiteboxTest, SaveNudge) {
+ InitializeSyncerOnNormalMode();
+
+ // Now set the mode to configure.
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+
+ SyncerThread::JobProcessDecision decision =
+ CreateAndDecideJob(SyncerThread::SyncSessionJob::NUDGE);
+
+ EXPECT_EQ(decision, SyncerThread::SAVE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, ContinueNudge) {
+ InitializeSyncerOnNormalMode();
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::NUDGE);
+
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, DropPoll) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::POLL);
+
+ EXPECT_EQ(decision, SyncerThread::DROP);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, ContinuePoll) {
+ InitializeSyncerOnNormalMode();
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::POLL);
+
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, ContinueConfiguration) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::CONFIGURATION);
+
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, SaveConfigurationWhileThrottled) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+
+ SetWaitIntervalToThrottled();
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::CONFIGURATION);
+
+ EXPECT_EQ(decision, SyncerThread::SAVE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, SaveNudgeWhileThrottled) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+
+ SetWaitIntervalToThrottled();
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::NUDGE);
+
+ EXPECT_EQ(decision, SyncerThread::SAVE);
+
+}
+
+TEST_F(SyncerThread2WhiteboxTest, ContinueClearUserDataUnderAllCircumstances) {
+ InitializeSyncerOnNormalMode();
+
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+ SetWaitIntervalToThrottled();
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::CLEAR_USER_DATA);
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+
+ SetMode(SyncerThread::NORMAL_MODE);
+ SetWaitIntervalToExponentialBackoff();
+ decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::CLEAR_USER_DATA);
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, ContinueNudgeWhileExponentialBackOff) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::NORMAL_MODE);
+ SetWaitIntervalToExponentialBackoff();
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::NUDGE);
+
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, DropNudgeWhileExponentialBackOff) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::NORMAL_MODE);
+ SetWaitIntervalToExponentialBackoff();
+
+ syncer_thread_->wait_interval_->had_nudge = true;
+
+ SyncerThread::JobProcessDecision decision = CreateAndDecideJob(
+ SyncerThread::SyncSessionJob::NUDGE);
+
+ EXPECT_EQ(decision, SyncerThread::DROP);
+}
+
+TEST_F(SyncerThread2WhiteboxTest, ContinueCanaryJobConfig) {
+ InitializeSyncerOnNormalMode();
+ SetMode(SyncerThread::CONFIGURATION_MODE);
+ SetWaitIntervalToExponentialBackoff();
+
+ struct SyncerThread::SyncSessionJob job;
+ job.purpose = SyncerThread::SyncSessionJob::CONFIGURATION;
+ job.scheduled_start = TimeTicks::Now();
+ job.is_canary_job = true;
+ SyncerThread::JobProcessDecision decision = DecideOnJob(job);
+
+ EXPECT_EQ(decision, SyncerThread::CONTINUE);
+}
+
+} // namespace browser_sync
+
+// SyncerThread won't outlive the test!
+DISABLE_RUNNABLE_METHOD_REFCOUNT(
+ browser_sync::SyncerThread2WhiteboxTest);
diff --git a/chrome/browser/sync/engine/syncer_thread_adapter.cc b/chrome/browser/sync/engine/syncer_thread_adapter.cc
deleted file mode 100644
index 34b3a00..0000000
--- a/chrome/browser/sync/engine/syncer_thread_adapter.cc
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "base/time.h"
-#include "chrome/browser/sync/engine/syncer_thread_adapter.h"
-#include "chrome/browser/sync/syncable/model_type.h"
-
-using base::TimeDelta;
-using syncable::ModelTypeBitSet;
-
-namespace browser_sync {
-
-SyncerThreadAdapter::SyncerThreadAdapter(sessions::SyncSessionContext* context,
- bool using_new_impl)
- : legacy_(NULL), new_impl_(NULL), using_new_impl_(using_new_impl) {
- if (using_new_impl_) {
- new_impl_.reset(new s3::SyncerThread(context, new Syncer()));
- new_impl_->Start(s3::SyncerThread::CONFIGURATION_MODE);
- } else {
- legacy_ = new SyncerThread(context);
- }
-}
-
-SyncerThreadAdapter::~SyncerThreadAdapter() {
- legacy_ = NULL;
- new_impl_.reset();
-}
-
-void SyncerThreadAdapter::WatchConnectionManager(
- ServerConnectionManager* conn_mgr) {
- DCHECK(!using_new_impl_);
- legacy_->WatchConnectionManager(conn_mgr);
-}
-
-bool SyncerThreadAdapter::Start() {
- if (using_new_impl_) {
- new_impl_->Start(s3::SyncerThread::NORMAL_MODE);
- return true;
- } else {
- return legacy_->Start();
- }
-}
-
-bool SyncerThreadAdapter::Stop(int max_wait) {
- if (using_new_impl_) {
- new_impl_->Stop();
- return true;
- } else {
- return legacy_->Stop(max_wait);
- }
-}
-
-bool SyncerThreadAdapter::RequestPause() {
- DCHECK(!using_new_impl_);
- return legacy_->RequestPause();
-}
-
-bool SyncerThreadAdapter::RequestResume() {
- DCHECK(!using_new_impl_);
- return legacy_->RequestResume();
-}
-
-s3::NudgeSource LegacyToNewSyncerThreadSource(SyncerThread::NudgeSource s) {
- switch(s) {
- case SyncerThread::kNotification:
- return s3::NUDGE_SOURCE_NOTIFICATION;
- case SyncerThread::kContinuation:
- return s3::NUDGE_SOURCE_CONTINUATION;
- case SyncerThread::kLocal:
- return s3::NUDGE_SOURCE_LOCAL;
- case SyncerThread::kUnknown:
- return s3::NUDGE_SOURCE_UNKNOWN;
- default:
- NOTREACHED();
- return s3::NUDGE_SOURCE_UNKNOWN;
- }
-}
-
-void SyncerThreadAdapter::NudgeSyncer(int milliseconds_from_now,
- SyncerThread::NudgeSource source) {
- if (using_new_impl_) {
- if (source == SyncerThread::kClearPrivateData) {
- new_impl_->ScheduleClearUserData();
- return;
- }
- new_impl_->ScheduleNudge(
- TimeDelta::FromMilliseconds(milliseconds_from_now),
- LegacyToNewSyncerThreadSource(source), ModelTypeBitSet());
- } else {
- legacy_->NudgeSyncer(milliseconds_from_now, source);
- }
-}
-
-void SyncerThreadAdapter::NudgeSyncerWithDataTypes(
- int milliseconds_from_now,
- SyncerThread::NudgeSource source,
- const syncable::ModelTypeBitSet& model_types) {
- DCHECK_NE(SyncerThread::kClearPrivateData, source);
- if (using_new_impl_) {
- new_impl_->ScheduleNudge(
- TimeDelta::FromMilliseconds(milliseconds_from_now),
- LegacyToNewSyncerThreadSource(source), model_types);
- } else {
- legacy_->NudgeSyncerWithDataTypes(milliseconds_from_now, source,
- model_types);
- }
-}
-
-void SyncerThreadAdapter::NudgeSyncerWithPayloads(
- int milliseconds_from_now,
- SyncerThread::NudgeSource source,
- const sessions::TypePayloadMap& model_types_with_payloads) {
- DCHECK_NE(SyncerThread::kClearPrivateData, source);
- if (using_new_impl_) {
- new_impl_->ScheduleNudgeWithPayloads(
- TimeDelta::FromMilliseconds(milliseconds_from_now),
- LegacyToNewSyncerThreadSource(source), model_types_with_payloads);
- } else {
- legacy_->NudgeSyncerWithPayloads(milliseconds_from_now, source,
- model_types_with_payloads);
- }
-}
-
-void SyncerThreadAdapter::SetNotificationsEnabled(bool enabled) {
- if (using_new_impl_)
- new_impl_->set_notifications_enabled(enabled);
- else
- legacy_->SetNotificationsEnabled(enabled);
-}
-
-void SyncerThreadAdapter::CreateSyncer(const std::string& dirname) {
- if (!using_new_impl_)
- legacy_->CreateSyncer(dirname);
- // No-op if using new impl.
-}
-
-s3::SyncerThread* SyncerThreadAdapter::new_impl() {
- return new_impl_.get();
-}
-
-} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/syncer_thread_adapter.h b/chrome/browser/sync/engine/syncer_thread_adapter.h
deleted file mode 100644
index a47bb1a..0000000
--- a/chrome/browser/sync/engine/syncer_thread_adapter.h
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/engine/net/server_connection_manager.h"
-#include "chrome/browser/sync/engine/syncer_thread.h"
-#include "chrome/browser/sync/engine/syncer_thread2.h"
-#include "chrome/browser/sync/sessions/sync_session_context.h"
-#include "chrome/browser/sync/syncable/model_type.h"
-
-namespace browser_sync {
-
-class SyncerThreadAdapter {
- public:
- SyncerThreadAdapter(sessions::SyncSessionContext* context,
- bool using_new_impl);
- ~SyncerThreadAdapter();
-
- // We mimic the SyncerThread interface (an adaptee).
- void WatchConnectionManager(ServerConnectionManager* conn_mgr);
- bool Start();
- bool Stop(int max_wait);
- bool RequestPause();
- bool RequestResume();
- void NudgeSyncer(int milliseconds_from_now,
- SyncerThread::NudgeSource source);
- void NudgeSyncerWithDataTypes(
- int milliseconds_from_now,
- SyncerThread::NudgeSource source,
- const syncable::ModelTypeBitSet& model_types);
- void NudgeSyncerWithPayloads(
- int milliseconds_from_now,
- SyncerThread::NudgeSource source,
- const sessions::TypePayloadMap& model_types_with_payloads);
- void SetNotificationsEnabled(bool enabled);
- void CreateSyncer(const std::string& dirname);
-
- // Expose the new impl to leverage new apis through the adapter.
- s3::SyncerThread* new_impl();
- private:
- scoped_refptr<SyncerThread> legacy_;
- scoped_ptr<s3::SyncerThread> new_impl_;
- bool using_new_impl_;
-
- DISALLOW_COPY_AND_ASSIGN(SyncerThreadAdapter);
-};
-
-} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/syncer_thread_unittest.cc
index f5d8049..7a37f38 100644
--- a/chrome/browser/sync/engine/syncer_thread_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_thread_unittest.cc
@@ -1,1245 +1,3 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
-
-#include <list>
-#include <map>
-
-#include "base/scoped_ptr.h"
-#include "base/synchronization/lock.h"
-#include "base/time.h"
-#include "base/synchronization/waitable_event.h"
-#include "chrome/browser/sync/engine/model_safe_worker.h"
-#include "chrome/browser/sync/engine/syncer_thread.h"
-#include "chrome/browser/sync/engine/syncer_types.h"
-#include "chrome/browser/sync/sessions/sync_session_context.h"
-#include "chrome/browser/sync/util/channel.h"
-#include "chrome/test/sync/engine/mock_connection_manager.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
-#include "chrome/test/sync/sessions/test_scoped_session_event_listener.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using base::TimeTicks;
-using base::TimeDelta;
-using base::WaitableEvent;
-using testing::_;
-using testing::AnyNumber;
-using testing::Field;
-
-namespace browser_sync {
-using sessions::ErrorCounters;
-using sessions::TestScopedSessionEventListener;
-using sessions::SyncSessionContext;
-using sessions::SyncSessionSnapshot;
-using sessions::SyncerStatus;
-
-typedef testing::Test SyncerThreadTest;
-typedef SyncerThread::WaitInterval WaitInterval;
-
-ACTION_P(SignalEvent, event) {
- event->Signal();
-}
-
-SyncSessionSnapshot SessionSnapshotForTest(
- int64 num_server_changes_remaining,
- int64 unsynced_count) {
- std::string download_progress_markers[syncable::MODEL_TYPE_COUNT];
- for (int i = syncable::FIRST_REAL_MODEL_TYPE;
- i < syncable::MODEL_TYPE_COUNT;
- ++i) {
- syncable::ModelType type(syncable::ModelTypeFromInt(i));
- sync_pb::DataTypeProgressMarker token;
- token.set_data_type_id(
- syncable::GetExtensionFieldNumberFromModelType(type));
- token.set_token("foobar");
- token.SerializeToString(&download_progress_markers[i]);
- }
- return SyncSessionSnapshot(SyncerStatus(), ErrorCounters(),
- num_server_changes_remaining, false,
- syncable::ModelTypeBitSet(), download_progress_markers,
- false, false, unsynced_count, 0, false, sessions::SyncSourceInfo());
-}
-
-class ListenerMock : public SyncEngineEventListener {
- public:
- MOCK_METHOD1(OnSyncEngineEvent, void(const SyncEngineEvent&));
-};
-
-class SyncerThreadWithSyncerTest : public testing::Test,
- public ModelSafeWorkerRegistrar,
- public SyncEngineEventListener {
- public:
- SyncerThreadWithSyncerTest()
- : max_wait_time_(TimeDelta::FromSeconds(10)),
- sync_cycle_ended_event_(false, false) {}
- virtual void SetUp() {
- metadb_.SetUp();
- connection_.reset(new MockConnectionManager(metadb_.manager(),
- metadb_.name()));
- worker_ = new ModelSafeWorker();
- std::vector<SyncEngineEventListener*> listeners;
- listeners.push_back(this);
- context_ = new SyncSessionContext(connection_.get(), metadb_.manager(),
- this, listeners);
- context_->set_account_name(metadb_.name());
- syncer_thread_ = new SyncerThread(context_);
- syncer_thread_->SetConnected(true);
- syncable::ModelTypeBitSet expected_types;
- expected_types[syncable::BOOKMARKS] = true;
- connection_->ExpectGetUpdatesRequestTypes(expected_types);
- }
- virtual void TearDown() {
- context_ = NULL;
- syncer_thread_ = NULL;
- connection_.reset();
- metadb_.TearDown();
- }
-
- // ModelSafeWorkerRegistrar implementation.
- virtual void GetWorkers(std::vector<ModelSafeWorker*>* out) {
- out->push_back(worker_.get());
- }
-
- virtual void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) {
- // We're just testing the sync engine here, so we shunt everything to
- // the SyncerThread.
- (*out)[syncable::BOOKMARKS] = GROUP_PASSIVE;
- }
-
- ManuallyOpenedTestDirectorySetterUpper* metadb() { return &metadb_; }
- MockConnectionManager* connection() { return connection_.get(); }
- SyncerThread* syncer_thread() { return syncer_thread_; }
-
- // Waits an indefinite amount of sync cycles for the syncer thread to become
- // throttled. Only call this if a throttle is supposed to occur!
- bool WaitForThrottle() {
- int max_cycles = 5;
- while (max_cycles && !syncer_thread()->IsSyncingCurrentlySilenced()) {
- sync_cycle_ended_event_.TimedWait(max_wait_time_);
- max_cycles--;
- }
-
- return syncer_thread()->IsSyncingCurrentlySilenced();
- }
-
- void WaitForDisconnect() {
- // Wait for the SyncerThread to detect loss of connection, up to a max of
- // 10 seconds to timeout the test.
- base::AutoLock lock(syncer_thread()->lock_);
- TimeTicks start = TimeTicks::Now();
- TimeDelta ten_seconds = TimeDelta::FromSeconds(10);
- while (syncer_thread()->vault_.connected_) {
- syncer_thread()->vault_field_changed_.TimedWait(ten_seconds);
- if (TimeTicks::Now() - start > ten_seconds)
- break;
- }
- EXPECT_FALSE(syncer_thread()->vault_.connected_);
- }
-
- bool Pause(ListenerMock* listener) {
- WaitableEvent event(false, false);
- {
- base::AutoLock lock(syncer_thread()->lock_);
- EXPECT_CALL(*listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_PAUSED))).
- WillOnce(SignalEvent(&event));
- }
- if (!syncer_thread()->RequestPause())
- return false;
- return event.TimedWait(max_wait_time_);
- }
-
- bool Resume(ListenerMock* listener) {
- WaitableEvent event(false, false);
- {
- base::AutoLock lock(syncer_thread()->lock_);
- EXPECT_CALL(*listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_RESUMED))).
- WillOnce(SignalEvent(&event));
- }
- if (!syncer_thread()->RequestResume())
- return false;
- return event.TimedWait(max_wait_time_);
- }
-
- void PreventThreadFromPolling() {
- const TimeDelta poll_interval = TimeDelta::FromMinutes(5);
- syncer_thread()->SetSyncerShortPollInterval(poll_interval);
- }
-
- // Compare a provided TypePayloadMap to the pending nudge info stored in the
- // SyncerThread vault.
- bool CompareNudgeTypesToVault(const sessions::TypePayloadMap& lhs) {
- const sessions::TypePayloadMap& vault_nudge_types =
- syncer_thread()->vault_.pending_nudge_types_;
- return lhs == vault_nudge_types;
- }
-
- // Compare a provided ModelTypeBitset to the pending nudge info stored in the
- // SyncerThread vault. Nudge info in vault must not have any non-empty
- // payloads.
- bool CompareNudgeTypesBitSetToVault(const syncable::ModelTypeBitSet& lhs) {
- sessions::TypePayloadMap model_types_with_payloads =
- sessions::MakeTypePayloadMapFromBitSet(lhs, std::string());
- size_t count = 0;
- for (sessions::TypePayloadMap::const_iterator i =
- syncer_thread()->vault_.pending_nudge_types_.begin();
- i != syncer_thread()->vault_.pending_nudge_types_.end();
- ++i, ++count) {
- if (!lhs.test(i->first))
- return false;
- }
- if (lhs.count() != count)
- return false;
- return true;
- }
-
-
- private:
-
- virtual void OnSyncEngineEvent(const SyncEngineEvent& event) {
- if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED)
- sync_cycle_ended_event_.Signal();
- }
-
- protected:
- TimeDelta max_wait_time_;
- SyncSessionContext* context_;
-
- private:
- ManuallyOpenedTestDirectorySetterUpper metadb_;
- scoped_ptr<MockConnectionManager> connection_;
- scoped_refptr<SyncerThread> syncer_thread_;
- scoped_refptr<ModelSafeWorker> worker_;
- base::WaitableEvent sync_cycle_ended_event_;
- DISALLOW_COPY_AND_ASSIGN(SyncerThreadWithSyncerTest);
-};
-
-class SyncShareIntercept
- : public MockConnectionManager::ResponseCodeOverrideRequestor,
- public MockConnectionManager::MidCommitObserver {
- public:
- SyncShareIntercept() : sync_occured_(false, false),
- allow_multiple_interceptions_(true) {}
- virtual ~SyncShareIntercept() {}
- virtual void Observe() {
- if (!allow_multiple_interceptions_ && !times_sync_occured_.empty())
- FAIL() << "Multiple sync shares occured.";
- times_sync_occured_.push_back(TimeTicks::Now());
- sync_occured_.Signal();
- }
-
- // ResponseCodeOverrideRequestor implementation. This assumes any override
- // requested is intended to silence the SyncerThread.
- virtual void OnOverrideComplete() {
- // We should not see any syncing.
- allow_multiple_interceptions_ = false;
- times_sync_occured_.clear();
- }
-
- void WaitForSyncShare(int at_least_this_many, TimeDelta max_wait) {
- while (at_least_this_many-- > 0)
- sync_occured_.TimedWait(max_wait);
- }
- std::vector<TimeTicks> times_sync_occured() const {
- return times_sync_occured_;
- }
-
- void Reset() {
- allow_multiple_interceptions_ = true;
- times_sync_occured_.clear();
- sync_occured_.Reset();
- }
- private:
- std::vector<TimeTicks> times_sync_occured_;
- base::WaitableEvent sync_occured_;
- bool allow_multiple_interceptions_;
- DISALLOW_COPY_AND_ASSIGN(SyncShareIntercept);
-};
-
-TEST_F(SyncerThreadTest, Construction) {
- SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL,
- std::vector<SyncEngineEventListener*>());
- scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context));
-}
-
-TEST_F(SyncerThreadTest, StartStop) {
- SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL,
- std::vector<SyncEngineEventListener*>());
- scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context));
- EXPECT_TRUE(syncer_thread->Start());
- EXPECT_TRUE(syncer_thread->Stop(2000));
-
- // Do it again for good measure. I caught some bugs by adding this so
- // I would recommend keeping it.
- EXPECT_TRUE(syncer_thread->Start());
- EXPECT_TRUE(syncer_thread->Stop(2000));
-}
-
-TEST(SyncerThread, GetRecommendedDelay) {
- EXPECT_LE(0, SyncerThread::GetRecommendedDelaySeconds(0));
- EXPECT_LE(1, SyncerThread::GetRecommendedDelaySeconds(1));
- EXPECT_LE(50, SyncerThread::GetRecommendedDelaySeconds(50));
- EXPECT_LE(10, SyncerThread::GetRecommendedDelaySeconds(10));
- EXPECT_EQ(SyncerThread::kMaxBackoffSeconds,
- SyncerThread::GetRecommendedDelaySeconds(
- SyncerThread::kMaxBackoffSeconds));
- EXPECT_EQ(SyncerThread::kMaxBackoffSeconds,
- SyncerThread::GetRecommendedDelaySeconds(
- SyncerThread::kMaxBackoffSeconds+1));
-}
-
-TEST_F(SyncerThreadTest, CalculateSyncWaitTime) {
- SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL,
- std::vector<SyncEngineEventListener*>());
- scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context));
- syncer_thread->DisableIdleDetection();
-
- // Syncer_polling_interval_ is less than max poll interval.
- TimeDelta syncer_polling_interval = TimeDelta::FromSeconds(1);
-
- syncer_thread->SetSyncerPollingInterval(syncer_polling_interval);
-
- // user_idle_ms is less than 10 * (syncer_polling_interval*1000).
- ASSERT_EQ(syncer_polling_interval.InMilliseconds(),
- syncer_thread->CalculateSyncWaitTime(1000, 0));
- ASSERT_EQ(syncer_polling_interval.InMilliseconds(),
- syncer_thread->CalculateSyncWaitTime(1000, 1));
-
- // user_idle_ms is ge than 10 * (syncer_polling_interval*1000).
- int last_poll_time = 2000;
- ASSERT_TRUE(last_poll_time <=
- syncer_thread->CalculateSyncWaitTime(last_poll_time, 10000));
- ASSERT_TRUE(last_poll_time * 3 >=
- syncer_thread->CalculateSyncWaitTime(last_poll_time, 10000));
- ASSERT_TRUE(last_poll_time <=
- syncer_thread->CalculateSyncWaitTime(last_poll_time, 100000));
- ASSERT_TRUE(last_poll_time * 3 >=
- syncer_thread->CalculateSyncWaitTime(last_poll_time, 100000));
-
- // Maximum backoff time should be syncer_max_interval.
- int near_threshold = SyncerThread::kDefaultMaxPollIntervalMs / 2 - 1;
- int threshold = SyncerThread::kDefaultMaxPollIntervalMs;
- int over_threshold = SyncerThread::kDefaultMaxPollIntervalMs + 1;
- ASSERT_TRUE(near_threshold <=
- syncer_thread->CalculateSyncWaitTime(near_threshold, 10000));
- ASSERT_TRUE(SyncerThread::kDefaultMaxPollIntervalMs >=
- syncer_thread->CalculateSyncWaitTime(near_threshold, 10000));
- ASSERT_TRUE(SyncerThread::kDefaultMaxPollIntervalMs ==
- syncer_thread->CalculateSyncWaitTime(threshold, 10000));
- ASSERT_TRUE(SyncerThread::kDefaultMaxPollIntervalMs ==
- syncer_thread->CalculateSyncWaitTime(over_threshold, 10000));
-
- // Possible idle time must be capped by syncer_max_interval.
- int over_sync_max_interval =
- SyncerThread::kDefaultMaxPollIntervalMs + 1;
- syncer_polling_interval = TimeDelta::FromSeconds(
- over_sync_max_interval / 100); // so 1000* is right
- syncer_thread->SetSyncerPollingInterval(syncer_polling_interval);
- ASSERT_EQ(syncer_polling_interval.InSeconds() * 1000,
- syncer_thread->CalculateSyncWaitTime(1000, over_sync_max_interval));
- syncer_polling_interval = TimeDelta::FromSeconds(1);
- syncer_thread->SetSyncerPollingInterval(syncer_polling_interval);
- ASSERT_TRUE(last_poll_time <=
- syncer_thread->CalculateSyncWaitTime(last_poll_time,
- over_sync_max_interval));
- ASSERT_TRUE(last_poll_time * 3 >=
- syncer_thread->CalculateSyncWaitTime(last_poll_time,
- over_sync_max_interval));
-}
-
-TEST_F(SyncerThreadTest, CalculatePollingWaitTime) {
- // Set up the environment.
- int user_idle_milliseconds_param = 0;
- SyncSessionContext* context = new SyncSessionContext(NULL, NULL, NULL,
- std::vector<SyncEngineEventListener*>());
- scoped_refptr<SyncerThread> syncer_thread(new SyncerThread(context));
- syncer_thread->DisableIdleDetection();
- // Hold the lock to appease asserts in code.
- base::AutoLock lock(syncer_thread->lock_);
-
- // Notifications disabled should result in a polling interval of
- // kDefaultShortPollInterval.
- {
- context->set_notifications_enabled(false);
- bool continue_sync_cycle_param = false;
-
- // No work and no backoff.
- WaitInterval interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_EQ(SyncerThread::kDefaultShortPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
-
- // In this case the continue_sync_cycle is turned off.
- continue_sync_cycle_param = true;
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_EQ(SyncerThread::kDefaultShortPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
- }
-
- // Notifications enabled should result in a polling interval of
- // SyncerThread::kDefaultLongPollIntervalSeconds.
- {
- context->set_notifications_enabled(true);
- bool continue_sync_cycle_param = false;
-
- // No work and no backoff.
- WaitInterval interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
-
- // In this case the continue_sync_cycle is turned off.
- continue_sync_cycle_param = true;
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
- }
-
- // There are two states which can cause a continuation, either the updates
- // available do not match the updates received, or the unsynced count is
- // non-zero.
- {
- // More server changes remaining to download.
- context->set_last_snapshot(SessionSnapshotForTest(1, 0));
- bool continue_sync_cycle_param = false;
-
- WaitInterval interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_LE(0, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- continue_sync_cycle_param = false;
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_GE(3, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_LE(0, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
-
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_GE(2, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- // Now simulate no more server changes remaining.
- context->set_last_snapshot(SessionSnapshotForTest(0, 0));
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
- }
-
- {
- // Now try with unsynced local items.
- context->set_last_snapshot(SessionSnapshotForTest(0, 1));
- bool continue_sync_cycle_param = false;
-
- WaitInterval interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_LE(0, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- continue_sync_cycle_param = false;
- interval = syncer_thread->CalculatePollingWaitTime(
- 0,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_GE(2, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- context->set_last_snapshot(SessionSnapshotForTest(0, 0));
- interval = syncer_thread->CalculatePollingWaitTime(
- 4,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
- }
-
- // Regression for exponential backoff reset when the syncer is nudged.
- {
- context->set_last_snapshot(SessionSnapshotForTest(0, 1));
- bool continue_sync_cycle_param = false;
-
- // Expect move from default polling interval to exponential backoff due to
- // unsynced_count != 0.
- WaitInterval interval = syncer_thread->CalculatePollingWaitTime(
- 3600,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_LE(0, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- continue_sync_cycle_param = false;
- interval = syncer_thread->CalculatePollingWaitTime(
- 3600,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_GE(2, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- // Expect exponential backoff.
- interval = syncer_thread->CalculatePollingWaitTime(
- 2,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_LE(2, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- interval = syncer_thread->CalculatePollingWaitTime(
- 2,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_GE(6, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- syncer_thread->vault_.current_wait_interval_ = interval;
-
- interval = syncer_thread->CalculatePollingWaitTime(
- static_cast<int>(interval.poll_delta.InSeconds()),
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- true);
-
- // Don't change poll on a failed nudge during backoff.
- ASSERT_TRUE(syncer_thread->vault_.current_wait_interval_.poll_delta ==
- interval.poll_delta);
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_TRUE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- // If we got a nudge and we weren't in backoff mode, we see exponential
- // backoff.
- syncer_thread->vault_.current_wait_interval_.mode = WaitInterval::NORMAL;
- interval = syncer_thread->CalculatePollingWaitTime(
- 2,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- true);
-
- // 5 and 3 are bounds on the backoff randomization formula given input of 2.
- ASSERT_GE(5, interval.poll_delta.InSeconds());
- ASSERT_LE(3, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- // And if another interval expires, we get a bigger backoff.
- WaitInterval new_interval = syncer_thread->CalculatePollingWaitTime(
- static_cast<int>(interval.poll_delta.InSeconds()),
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- false);
-
- ASSERT_GE(12, new_interval.poll_delta.InSeconds());
- ASSERT_LE(5, new_interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::EXPONENTIAL_BACKOFF, interval.mode);
- ASSERT_FALSE(new_interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- // A nudge resets the continue_sync_cycle_param value, so our backoff
- // should return to the minimum.
- continue_sync_cycle_param = false;
- interval = syncer_thread->CalculatePollingWaitTime(
- 3600,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- true);
-
- ASSERT_LE(0, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- continue_sync_cycle_param = false;
- interval = syncer_thread->CalculatePollingWaitTime(
- 3600,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- true);
-
- ASSERT_GE(2, interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_TRUE(continue_sync_cycle_param);
-
- // Setting unsynced_count = 0 returns us to the default polling interval.
- context->set_last_snapshot(SessionSnapshotForTest(0, 0));
- interval = syncer_thread->CalculatePollingWaitTime(
- 4,
- &user_idle_milliseconds_param,
- &continue_sync_cycle_param,
- true);
-
- ASSERT_EQ(SyncerThread::kDefaultLongPollIntervalSeconds,
- interval.poll_delta.InSeconds());
- ASSERT_EQ(WaitInterval::NORMAL, interval.mode);
- ASSERT_FALSE(interval.had_nudge_during_backoff);
- ASSERT_FALSE(continue_sync_cycle_param);
- }
-}
-
-TEST_F(SyncerThreadWithSyncerTest, Polling) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
-
- const TimeDelta poll_interval = TimeDelta::FromSeconds(1);
- syncer_thread()->SetSyncerShortPollInterval(poll_interval);
- EXPECT_TRUE(syncer_thread()->Start());
-
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
-
- TimeDelta two_polls = poll_interval + poll_interval;
- // We could theoretically return immediately from the wait if the interceptor
- // was already signaled for a SyncShare (the first one comes quick).
- interceptor.WaitForSyncShare(1, two_polls);
- EXPECT_FALSE(interceptor.times_sync_occured().empty());
-
- // Wait for at least 2 more SyncShare operations.
- interceptor.WaitForSyncShare(2, two_polls);
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-
- // Now analyze the run.
- std::vector<TimeTicks> data = interceptor.times_sync_occured();
-
- EXPECT_GE(data.size(), static_cast<unsigned int>(3));
- for (unsigned int i = 0; i < data.size() - 1; i++) {
- TimeTicks optimal_next_sync = data[i] + poll_interval;
- EXPECT_TRUE(data[i + 1] >= optimal_next_sync);
- // This should be reliable, as there are no blocking or I/O operations
- // except the explicit 2 second wait, so if it takes longer than this
- // there is a problem.
- EXPECT_TRUE(data[i + 1] < optimal_next_sync + poll_interval);
- }
-}
-
-TEST_F(SyncerThreadWithSyncerTest, Nudge) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- // We don't want a poll to happen during this test (except the first one).
- PreventThreadFromPolling();
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- const TimeDelta poll_interval = TimeDelta::FromMinutes(5);
- interceptor.WaitForSyncShare(1, poll_interval + poll_interval);
-
- EXPECT_EQ(static_cast<unsigned int>(1),
- interceptor.times_sync_occured().size());
- // The SyncerThread should be waiting for the poll now. Nudge it to sync
- // immediately (5ms).
- syncer_thread()->NudgeSyncer(5, SyncerThread::kUnknown);
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1));
- EXPECT_EQ(static_cast<unsigned int>(2),
- interceptor.times_sync_occured().size());
-
- // SyncerThread should be waiting again. Signal it to stop.
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-TEST_F(SyncerThreadWithSyncerTest, NudgeWithDataTypes) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- // We don't want a poll to happen during this test (except the first one).
- PreventThreadFromPolling();
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- const TimeDelta poll_interval = TimeDelta::FromMinutes(5);
- interceptor.WaitForSyncShare(1, poll_interval + poll_interval);
- EXPECT_EQ(static_cast<unsigned int>(1),
- interceptor.times_sync_occured().size());
-
- // The SyncerThread should be waiting for the poll now. Nudge it to sync
- // immediately (5ms).
- syncable::ModelTypeBitSet model_types;
- model_types[syncable::BOOKMARKS] = true;
-
- // Paused so we can verify the nudge types safely.
- syncer_thread()->RequestPause();
- syncer_thread()->NudgeSyncerWithDataTypes(5,
- SyncerThread::kUnknown,
- model_types);
- EXPECT_TRUE(CompareNudgeTypesBitSetToVault(model_types));
- syncer_thread()->RequestResume();
-
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1));
- EXPECT_EQ(static_cast<unsigned int>(2),
- interceptor.times_sync_occured().size());
-
- // SyncerThread should be waiting again. Signal it to stop.
- EXPECT_TRUE(syncer_thread()->Stop(2000));
- EXPECT_TRUE(syncer_thread()->vault_.pending_nudge_types_.empty());
-}
-
-TEST_F(SyncerThreadWithSyncerTest, NudgeWithDataTypesCoalesced) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- // We don't want a poll to happen during this test (except the first one).
- PreventThreadFromPolling();
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- const TimeDelta poll_interval = TimeDelta::FromMinutes(5);
- interceptor.WaitForSyncShare(1, poll_interval + poll_interval);
- EXPECT_EQ(static_cast<unsigned int>(1),
- interceptor.times_sync_occured().size());
-
- // The SyncerThread should be waiting for the poll now. Nudge it to sync
- // immediately (5ms).
- syncable::ModelTypeBitSet model_types;
- model_types[syncable::BOOKMARKS] = true;
-
- // Paused so we can verify the nudge types safely.
- syncer_thread()->RequestPause();
- syncer_thread()->NudgeSyncerWithDataTypes(100,
- SyncerThread::kUnknown,
- model_types);
- EXPECT_TRUE(CompareNudgeTypesBitSetToVault(model_types));
-
- model_types[syncable::BOOKMARKS] = false;
- model_types[syncable::AUTOFILL] = true;
- syncer_thread()->NudgeSyncerWithDataTypes(0,
- SyncerThread::kUnknown,
- model_types);
-
- // Reset BOOKMARKS for expectations.
- model_types[syncable::BOOKMARKS] = true;
- EXPECT_TRUE(CompareNudgeTypesBitSetToVault(model_types));
-
- syncer_thread()->RequestResume();
-
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1));
- EXPECT_EQ(static_cast<unsigned int>(2),
- interceptor.times_sync_occured().size());
-
- // SyncerThread should be waiting again. Signal it to stop.
- EXPECT_TRUE(syncer_thread()->Stop(2000));
- EXPECT_TRUE(syncer_thread()->vault_.pending_nudge_types_.empty());
-}
-
-TEST_F(SyncerThreadWithSyncerTest, NudgeWithPayloads) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- // We don't want a poll to happen during this test (except the first one).
- PreventThreadFromPolling();
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- const TimeDelta poll_interval = TimeDelta::FromMinutes(5);
- interceptor.WaitForSyncShare(1, poll_interval + poll_interval);
- EXPECT_EQ(static_cast<unsigned int>(1),
- interceptor.times_sync_occured().size());
-
- // The SyncerThread should be waiting for the poll now. Nudge it to sync
- // immediately (5ms).
- sessions::TypePayloadMap nudge_types;
- nudge_types[syncable::BOOKMARKS] = "test";
- connection()->ExpectGetUpdatesRequestPayloads(nudge_types);
-
- // Paused so we can verify the nudge types safely.
- syncer_thread()->RequestPause();
- syncer_thread()->NudgeSyncerWithPayloads(5,
- SyncerThread::kUnknown,
- nudge_types);
- EXPECT_TRUE(CompareNudgeTypesToVault(nudge_types));
- syncer_thread()->RequestResume();
-
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1));
- EXPECT_EQ(static_cast<unsigned int>(2),
- interceptor.times_sync_occured().size());
-
- // SyncerThread should be waiting again. Signal it to stop.
- EXPECT_TRUE(syncer_thread()->Stop(2000));
- EXPECT_TRUE(syncer_thread()->vault_.pending_nudge_types_.empty());
-}
-
-TEST_F(SyncerThreadWithSyncerTest, NudgeWithPayloadsCoalesced) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- // We don't want a poll to happen during this test (except the first one).
- PreventThreadFromPolling();
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- const TimeDelta poll_interval = TimeDelta::FromMinutes(5);
- interceptor.WaitForSyncShare(1, poll_interval + poll_interval);
- EXPECT_EQ(static_cast<unsigned int>(1),
- interceptor.times_sync_occured().size());
-
- // The SyncerThread should be waiting for the poll now. Nudge it to sync
- // immediately (5ms).
- sessions::TypePayloadMap nudge_types;
- nudge_types[syncable::BOOKMARKS] = "books";
-
- // Paused so we can verify the nudge types safely.
- syncer_thread()->RequestPause();
- syncer_thread()->NudgeSyncerWithPayloads(100,
- SyncerThread::kUnknown,
- nudge_types);
- EXPECT_TRUE(CompareNudgeTypesToVault(nudge_types));
-
- nudge_types.erase(syncable::BOOKMARKS);
- nudge_types[syncable::AUTOFILL] = "auto";
- syncer_thread()->NudgeSyncerWithPayloads(0,
- SyncerThread::kUnknown,
- nudge_types);
-
- // Reset BOOKMARKS for expectations.
- nudge_types[syncable::BOOKMARKS] = "books";
- EXPECT_TRUE(CompareNudgeTypesToVault(nudge_types));
- connection()->ExpectGetUpdatesRequestPayloads(nudge_types);
-
- syncer_thread()->RequestResume();
-
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1));
- EXPECT_EQ(static_cast<unsigned int>(2),
- interceptor.times_sync_occured().size());
-
- // SyncerThread should be waiting again. Signal it to stop.
- EXPECT_TRUE(syncer_thread()->Stop(2000));
- EXPECT_TRUE(syncer_thread()->vault_.pending_nudge_types_.empty());
-}
-
-TEST_F(SyncerThreadWithSyncerTest, Throttling) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- const TimeDelta poll_interval = TimeDelta::FromMilliseconds(10);
- syncer_thread()->SetSyncerShortPollInterval(poll_interval);
-
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
-
- // Wait for some healthy syncing.
- interceptor.WaitForSyncShare(4, poll_interval + poll_interval);
-
- // Tell the server to throttle a single request, which should be all it takes
- // to silence our syncer (for 2 hours, so we shouldn't hit that in this test).
- // This will atomically visit the interceptor so it can switch to throttled
- // mode and fail on multiple requests.
- connection()->ThrottleNextRequest(&interceptor);
-
- // Try to trigger a sync (we have a really short poll interval already).
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
-
- // Wait until the syncer thread reports that it is throttled. Any further
- // sync share interceptions will result in failure. If things are broken,
- // we may never halt.
- ASSERT_TRUE(WaitForThrottle());
- EXPECT_TRUE(syncer_thread()->IsSyncingCurrentlySilenced());
-
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-TEST_F(SyncerThreadWithSyncerTest, StopSyncPermanently) {
- // The SyncerThread should request an exit from the Syncer and set
- // conditions for termination.
- const TimeDelta poll_interval = TimeDelta::FromMilliseconds(10);
- syncer_thread()->SetSyncerShortPollInterval(poll_interval);
-
- ListenerMock listener;
- WaitableEvent sync_cycle_ended_event(false, false);
- WaitableEvent syncer_thread_exiting_event(false, false);
- TestScopedSessionEventListener reg(context_, &listener);
-
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::STATUS_CHANGED))).
- Times(AnyNumber());
-
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- Times(AnyNumber()).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
-
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::STOP_SYNCING_PERMANENTLY)));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_EXITING))).
- WillOnce(SignalEvent(&syncer_thread_exiting_event));
-
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
- connection()->set_store_birthday("NotYourLuckyDay");
- ASSERT_TRUE(syncer_thread_exiting_event.TimedWait(max_wait_time_));
- EXPECT_TRUE(syncer_thread()->Stop(0));
-}
-
-TEST_F(SyncerThreadWithSyncerTest, AuthInvalid) {
- SyncShareIntercept interceptor;
- connection()->SetMidCommitObserver(&interceptor);
- const TimeDelta poll_interval = TimeDelta::FromMilliseconds(1);
-
- syncer_thread()->SetSyncerShortPollInterval(poll_interval);
- EXPECT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
-
- // Wait for some healthy syncing.
- interceptor.WaitForSyncShare(2, TimeDelta::FromSeconds(10));
- EXPECT_GE(interceptor.times_sync_occured().size(), 2U);
-
- // Atomically start returning auth invalid and set the interceptor to fail
- // on any sync.
- connection()->FailWithAuthInvalid(&interceptor);
- WaitForDisconnect();
-
- // Try to trigger a sync (the interceptor will assert if one occurs).
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
-
- // Wait several poll intervals but don't expect any syncing besides the cycle
- // that lost the connection.
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(1));
- EXPECT_EQ(1U, interceptor.times_sync_occured().size());
-
- // Simulate a valid re-authentication and expect resumption of syncing.
- interceptor.Reset();
- ASSERT_TRUE(interceptor.times_sync_occured().empty());
- connection()->StopFailingWithAuthInvalid(NULL);
- ServerConnectionEvent e = {ServerConnectionEvent::STATUS_CHANGED,
- HttpResponse::SERVER_CONNECTION_OK,
- true};
- connection()->channel()->NotifyListeners(e);
-
- interceptor.WaitForSyncShare(1, TimeDelta::FromSeconds(10));
- EXPECT_FALSE(interceptor.times_sync_occured().empty());
-
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-// TODO(zea): Disabled, along with PauseWhenNotConnected, due to stalling on
-// windows, preventing further sync unit tests from running. See crbug/39070.
-TEST_F(SyncerThreadWithSyncerTest, DISABLED_Pause) {
- WaitableEvent sync_cycle_ended_event(false, false);
- WaitableEvent paused_event(false, false);
- WaitableEvent resumed_event(false, false);
- PreventThreadFromPolling();
-
- ListenerMock listener;
- TestScopedSessionEventListener reg(context_, &listener);
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::STATUS_CHANGED))).
- Times(AnyNumber());
-
- // Wait for the initial sync to complete.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_EXITING)));
-
- ASSERT_TRUE(syncer_thread()->Start());
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
- // Request a pause.
- ASSERT_TRUE(Pause(&listener));
-
- // Resuming the pause.
- ASSERT_TRUE(Resume(&listener));
-
- // Not paused, should fail.
- EXPECT_FALSE(syncer_thread()->RequestResume());
-
- // Request a pause.
- ASSERT_TRUE(Pause(&listener));
-
- // Nudge the syncer, this should do nothing while we are paused.
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
-
- // Resuming will cause the nudge to be processed and a sync cycle to run.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- ASSERT_TRUE(Resume(&listener));
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-TEST_F(SyncerThreadWithSyncerTest, StartWhenNotConnected) {
- WaitableEvent sync_cycle_ended_event(false, false);
- WaitableEvent event(false, false);
- ListenerMock listener;
- TestScopedSessionEventListener reg(context_, &listener);
- PreventThreadFromPolling();
-
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::STATUS_CHANGED))).
- Times(AnyNumber());
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_EXITING)));
-
- connection()->SetServerNotReachable();
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
-
- // Syncer thread will always go through once cycle at the start,
- // then it will wait for a connection.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION))).
- WillOnce(SignalEvent(&event));
- ASSERT_TRUE(syncer_thread()->Start());
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
- ASSERT_TRUE(event.TimedWait(max_wait_time_));
-
- // Connect, will put the syncer thread into its usually poll wait.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_CONNECTED))).
- WillOnce(SignalEvent(&event));
- connection()->SetServerReachable();
- ASSERT_TRUE(event.TimedWait(max_wait_time_));
-
- // Nudge the syncer to complete a cycle.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-// See TODO comment on the "Pause" test above.
-TEST_F(SyncerThreadWithSyncerTest, DISABLED_PauseWhenNotConnected) {
- WaitableEvent sync_cycle_ended_event(false, false);
- WaitableEvent event(false, false);
- ListenerMock listener;
- TestScopedSessionEventListener reg(context_, &listener);
- PreventThreadFromPolling();
-
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::STATUS_CHANGED))).
- Times(AnyNumber());
-
- // Put the thread into a "waiting for connection" state.
- connection()->SetServerNotReachable();
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION))).
- WillOnce(SignalEvent(&event));
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
-
- ASSERT_TRUE(syncer_thread()->Start());
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
- ASSERT_TRUE(event.TimedWait(max_wait_time_));
-
- // Pause and resume the thread while waiting for a connection.
- ASSERT_TRUE(Pause(&listener));
- ASSERT_TRUE(Resume(&listener));
-
- // Make a connection and let the syncer cycle.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_CONNECTED))).
- WillOnce(SignalEvent(&event));
- connection()->SetServerReachable();
- ASSERT_TRUE(event.TimedWait(max_wait_time_));
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
- // Disconnect and get into the waiting for a connection state.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_WAITING_FOR_CONNECTION))).
- WillOnce(SignalEvent(&event));
- connection()->SetServerNotReachable();
- ASSERT_TRUE(event.TimedWait(max_wait_time_));
-
- // Pause so we can test getting a connection while paused.
- ASSERT_TRUE(Pause(&listener));
-
- // Get a connection then resume.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_CONNECTED))).
- WillOnce(SignalEvent(&event));
- connection()->SetServerReachable();
- ASSERT_TRUE(event.TimedWait(max_wait_time_));
-
- ASSERT_TRUE(Resume(&listener));
-
- // Cycle the syncer to show we are not longer paused.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_EXITING)));
-
- syncer_thread()->NudgeSyncer(0, SyncerThread::kUnknown);
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
-
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-TEST_F(SyncerThreadWithSyncerTest, PauseResumeWhenNotRunning) {
- WaitableEvent sync_cycle_ended_event(false, false);
- WaitableEvent event(false, false);
- ListenerMock listener;
- TestScopedSessionEventListener reg(context_, &listener);
- PreventThreadFromPolling();
-
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::STATUS_CHANGED))).
- Times(AnyNumber());
-
- // Pause and resume the syncer while not running
- ASSERT_TRUE(Pause(&listener));
- ASSERT_TRUE(Resume(&listener));
-
- // Pause the thread then start the syncer.
- ASSERT_TRUE(Pause(&listener));
- metadb()->Open();
- syncer_thread()->CreateSyncer(metadb()->name());
- ASSERT_TRUE(syncer_thread()->Start());
-
- // Resume and let the syncer cycle.
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNC_CYCLE_ENDED))).
- WillOnce(SignalEvent(&sync_cycle_ended_event));
- EXPECT_CALL(listener, OnSyncEngineEvent(
- Field(&SyncEngineEvent::what_happened,
- SyncEngineEvent::SYNCER_THREAD_EXITING)));
-
- ASSERT_TRUE(Resume(&listener));
- ASSERT_TRUE(sync_cycle_ended_event.TimedWait(max_wait_time_));
- EXPECT_TRUE(syncer_thread()->Stop(2000));
-}
-
-} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/syncer_types.cc b/chrome/browser/sync/engine/syncer_types.cc
new file mode 100644
index 0000000..8bc5524
--- /dev/null
+++ b/chrome/browser/sync/engine/syncer_types.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/engine/syncer_types.h"
+
+namespace browser_sync {
+
+SyncEngineEvent::SyncEngineEvent(EventCause cause) : what_happened(cause),
+ snapshot(NULL) {
+}
+
+SyncEngineEvent::~SyncEngineEvent() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/engine/syncer_types.h b/chrome/browser/sync/engine/syncer_types.h
index 0e05e0e..57e8855 100644
--- a/chrome/browser/sync/engine/syncer_types.h
+++ b/chrome/browser/sync/engine/syncer_types.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,7 +10,7 @@
#include <vector>
#include "base/observer_list.h"
-#include "chrome/browser/sync/util/channel.h"
+#include "chrome/browser/sync/syncable/model_type.h"
namespace syncable {
class BaseTransaction;
@@ -83,30 +83,6 @@ struct SyncEngineEvent {
SYNC_CYCLE_ENDED,
////////////////////////////////////////////////////////////////
- // SyncerThread generated events.
-
- // This event is sent when the thread is paused in response to a
- // pause request.
- // TODO(tim): Deprecated.
- SYNCER_THREAD_PAUSED,
-
- // This event is sent when the thread is resumed in response to a
- // resume request.
- // TODO(tim): Deprecated.
- SYNCER_THREAD_RESUMED,
-
- // This event is sent when the thread is waiting for a connection
- // to be established.
- SYNCER_THREAD_WAITING_FOR_CONNECTION,
-
- // This event is sent when a connection has been established and
- // the thread continues.
- SYNCER_THREAD_CONNECTED,
-
- // Sent when the main syncer loop finishes.
- SYNCER_THREAD_EXITING,
-
- ////////////////////////////////////////////////////////////////
// Generated in response to specific protocol actions or events.
// New token in updated_token.
@@ -124,9 +100,8 @@ struct SyncEngineEvent {
CLEAR_SERVER_DATA_FAILED,
};
- explicit SyncEngineEvent(EventCause cause) : what_happened(cause),
- snapshot(NULL) {
-}
+ explicit SyncEngineEvent(EventCause cause);
+ ~SyncEngineEvent();
EventCause what_happened;
diff --git a/chrome/browser/sync/engine/syncer_unittest.cc b/chrome/browser/sync/engine/syncer_unittest.cc
index 0aa7e35..243756a 100644
--- a/chrome/browser/sync/engine/syncer_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -13,7 +13,7 @@
#include <string>
#include "base/callback.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/string_number_conversions.h"
#include "build/build_config.h"
#include "chrome/browser/sync/engine/conflict_resolver.h"
@@ -154,8 +154,8 @@ class SyncerTest : public testing::Test,
std::vector<ModelSafeWorker*> workers;
GetModelSafeRoutingInfo(&info);
GetWorkers(&workers);
- sessions::TypePayloadMap types =
- sessions::MakeTypePayloadMapFromRoutingInfo(info, std::string());
+ syncable::ModelTypePayloadMap types =
+ syncable::ModelTypePayloadMapFromRoutingInfo(info, std::string());
return new SyncSession(context_.get(), this,
sessions::SyncSourceInfo(sync_pb::GetUpdatesCallerInfo::UNKNOWN, types),
info, workers);
diff --git a/chrome/browser/sync/engine/syncer_util.cc b/chrome/browser/sync/engine/syncer_util.cc
index c1bed35..c20e206 100644
--- a/chrome/browser/sync/engine/syncer_util.cc
+++ b/chrome/browser/sync/engine/syncer_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -639,28 +639,6 @@ void SyncerUtil::AddPredecessorsThenItem(
std::reverse(commit_ids->begin() + initial_size, commit_ids->end());
}
-// TODO(ncarter): This is redundant to some code in GetCommitIdsCommand. Unify
-// them.
-// static
-void SyncerUtil::AddUncommittedParentsAndTheirPredecessors(
- syncable::BaseTransaction* trans,
- syncable::MetahandleSet* inserted_items,
- std::vector<syncable::Id>* commit_ids,
- syncable::Id parent_id) {
- size_t intial_commit_ids_size = commit_ids->size();
- // Climb the tree adding entries leaf -> root.
- while (!parent_id.ServerKnows()) {
- Entry parent(trans, GET_BY_ID, parent_id);
- CHECK(parent.good()) << "Bad user-only parent in item path.";
- if (!AddItemThenPredecessors(trans, &parent, IS_UNSYNCED, inserted_items,
- commit_ids))
- break; // Parent was already present in |inserted_items|.
- parent_id = parent.Get(PARENT_ID);
- }
- // Reverse what we added to get the correct order.
- std::reverse(commit_ids->begin() + intial_commit_ids_size, commit_ids->end());
-}
-
// static
void SyncerUtil::MarkDeletedChildrenSynced(
const syncable::ScopedDirLookup &dir,
diff --git a/chrome/browser/sync/engine/syncer_util.h b/chrome/browser/sync/engine/syncer_util.h
index b1d5e50..90de980 100644
--- a/chrome/browser/sync/engine/syncer_util.h
+++ b/chrome/browser/sync/engine/syncer_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -127,12 +127,6 @@ class SyncerUtil {
syncable::MetahandleSet* inserted_items,
std::vector<syncable::Id>* commit_ids);
- static void AddUncommittedParentsAndTheirPredecessors(
- syncable::BaseTransaction* trans,
- syncable::MetahandleSet* inserted_items,
- std::vector<syncable::Id>* commit_ids,
- syncable::Id parent_id);
-
static void MarkDeletedChildrenSynced(
const syncable::ScopedDirLookup &dir,
std::set<syncable::Id>* deleted_folders);
diff --git a/chrome/browser/sync/engine/update_applicator.cc b/chrome/browser/sync/engine/update_applicator.cc
index 6922c0f..04eca43 100644
--- a/chrome/browser/sync/engine/update_applicator.cc
+++ b/chrome/browser/sync/engine/update_applicator.cc
@@ -91,10 +91,21 @@ void UpdateApplicator::Advance() {
}
bool UpdateApplicator::SkipUpdate(const syncable::Entry& entry) {
- ModelSafeGroup g =
- GetGroupForModelType(entry.GetServerModelType(), routing_info_);
+ syncable::ModelType type = entry.GetServerModelType();
+ ModelSafeGroup g = GetGroupForModelType(type, routing_info_);
+ // The extra routing_info count check here is to support GetUpdateses for
+ // a subset of the globally enabled types, and not attempt to update items
+ // if their type isn't permitted in the current run. These would typically
+ // be unapplied items from a previous sync.
if (g != group_filter_)
return true;
+ if (g == GROUP_PASSIVE &&
+ !routing_info_.count(type) &&
+ type != syncable::UNSPECIFIED &&
+ type != syncable::TOP_LEVEL_FOLDER) {
+ VLOG(1) << "Skipping update application, type not permitted.";
+ return true;
+ }
return false;
}
diff --git a/chrome/browser/sync/glue/app_data_type_controller.cc b/chrome/browser/sync/glue/app_data_type_controller.cc
index 453d5e2..cf025f6 100644
--- a/chrome/browser/sync/glue/app_data_type_controller.cc
+++ b/chrome/browser/sync/glue/app_data_type_controller.cc
@@ -1,17 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/app_data_type_controller.h"
#include "base/metrics/histogram.h"
-#include "base/logging.h"
-#include "base/time.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/browser/sync/unrecoverable_error_handler.h"
-#include "content/browser/browser_thread.h"
namespace browser_sync {
@@ -19,110 +14,42 @@ AppDataTypeController::AppDataTypeController(
ProfileSyncFactory* profile_sync_factory,
Profile* profile,
ProfileSyncService* sync_service)
- : profile_sync_factory_(profile_sync_factory),
- profile_(profile),
- sync_service_(sync_service),
- state_(NOT_RUNNING) {
- DCHECK(profile_sync_factory);
- DCHECK(sync_service);
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
}
AppDataTypeController::~AppDataTypeController() {
}
-void AppDataTypeController::Start(StartCallback* start_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(start_callback);
- if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
- delete start_callback;
- return;
- }
+syncable::ModelType AppDataTypeController::type() const {
+ return syncable::APPS;
+}
- start_callback_.reset(start_callback);
+bool AppDataTypeController::StartModels() {
+ profile_->InitExtensions(true);
+ return true;
+}
- profile_->InitExtensions();
+void AppDataTypeController::CreateSyncComponents() {
ProfileSyncFactory::SyncComponents sync_components =
profile_sync_factory_->CreateAppSyncComponents(sync_service_,
this);
model_associator_.reset(sync_components.model_associator);
change_processor_.reset(sync_components.change_processor);
-
- bool sync_has_nodes = false;
- if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
- StartFailed(UNRECOVERABLE_ERROR);
- return;
- }
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- bool merge_success = model_associator_->AssociateModels();
- UMA_HISTOGRAM_TIMES("Sync.AppAssociationTime",
- base::TimeTicks::Now() - start_time);
- if (!merge_success) {
- StartFailed(ASSOCIATION_FAILED);
- return;
- }
-
- sync_service_->ActivateDataType(this, change_processor_.get());
- state_ = RUNNING;
- FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK);
-}
-
-void AppDataTypeController::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (change_processor_ != NULL)
- sync_service_->DeactivateDataType(this, change_processor_.get());
-
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
- change_processor_.reset();
- model_associator_.reset();
- start_callback_.reset();
-
- state_ = NOT_RUNNING;
-}
-
-bool AppDataTypeController::enabled() {
- return true;
-}
-
-syncable::ModelType AppDataTypeController::type() {
- return syncable::APPS;
-}
-
-browser_sync::ModelSafeGroup AppDataTypeController::model_safe_group() {
- return browser_sync::GROUP_UI;
-}
-
-const char* AppDataTypeController::name() const {
- // For logging only.
- return "app";
-}
-
-DataTypeController::State AppDataTypeController::state() {
- return state_;
}
-void AppDataTypeController::OnUnrecoverableError(
+void AppDataTypeController::RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
UMA_HISTOGRAM_COUNTS("Sync.AppRunFailures", 1);
- sync_service_->OnUnrecoverableError(from_here, message);
}
-void AppDataTypeController::FinishStart(StartResult result) {
- start_callback_->Run(result);
- start_callback_.reset();
+void AppDataTypeController::RecordAssociationTime(base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.AppAssociationTime", time);
}
-void AppDataTypeController::StartFailed(StartResult result) {
- model_associator_.reset();
- change_processor_.reset();
- start_callback_->Run(result);
- start_callback_.reset();
+void AppDataTypeController::RecordStartFailure(StartResult result) {
UMA_HISTOGRAM_ENUMERATION("Sync.AppStartFailures",
result,
MAX_START_RESULT);
diff --git a/chrome/browser/sync/glue/app_data_type_controller.h b/chrome/browser/sync/glue/app_data_type_controller.h
index 4bd2750..93bd517 100644
--- a/chrome/browser/sync/glue/app_data_type_controller.h
+++ b/chrome/browser/sync/glue/app_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,20 +8,11 @@
#include <string>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-
-class Profile;
-class ProfileSyncFactory;
-class ProfileSyncService;
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
namespace browser_sync {
-class AssociatorInterface;
-class ChangeProcessor;
-
-class AppDataTypeController : public DataTypeController {
+class AppDataTypeController : public FrontendDataTypeController {
public:
AppDataTypeController(
ProfileSyncFactory* profile_sync_factory,
@@ -30,41 +21,17 @@ class AppDataTypeController : public DataTypeController {
virtual ~AppDataTypeController();
// DataTypeController implementation.
- virtual void Start(StartCallback* start_callback);
-
- virtual void Stop();
-
- virtual bool enabled();
-
- virtual syncable::ModelType type();
-
- virtual browser_sync::ModelSafeGroup model_safe_group();
-
- virtual const char* name() const;
-
- virtual State state();
+ virtual syncable::ModelType type() const;
- // UnrecoverableErrorHandler interface.
- virtual void OnUnrecoverableError(
+ private:
+ // DataTypeController implementations.
+ virtual bool StartModels();
+ virtual void CreateSyncComponents();
+ virtual void RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message);
-
- private:
- // Helper method to run the stashed start callback with a given result.
- void FinishStart(StartResult result);
-
- // Cleans up state and calls callback when start fails.
- void StartFailed(StartResult result);
-
- ProfileSyncFactory* profile_sync_factory_;
- Profile* profile_;
- ProfileSyncService* sync_service_;
-
- State state_;
-
- scoped_ptr<StartCallback> start_callback_;
- scoped_ptr<AssociatorInterface> model_associator_;
- scoped_ptr<ChangeProcessor> change_processor_;
+ virtual void RecordAssociationTime(base::TimeDelta time);
+ virtual void RecordStartFailure(StartResult result);
DISALLOW_COPY_AND_ASSIGN(AppDataTypeController);
};
diff --git a/chrome/browser/sync/glue/autofill_change_processor.cc b/chrome/browser/sync/glue/autofill_change_processor.cc
index aef3b99..00be75c 100644
--- a/chrome/browser/sync/glue/autofill_change_processor.cc
+++ b/chrome/browser/sync/glue/autofill_change_processor.cc
@@ -19,7 +19,7 @@
#include "chrome/browser/webdata/web_data_service.h"
#include "chrome/browser/webdata/web_database.h"
#include "chrome/common/guid.h"
-#include "chrome/common/notification_service.h"
+#include "content/common/notification_service.h"
namespace browser_sync {
@@ -108,7 +108,7 @@ void AutofillChangeProcessor::ObserveAutofillEntriesChanged(
}
std::vector<base::Time> timestamps;
- if (!web_database_->GetAutofillTimestamps(
+ if (!web_database_->GetAutofillTable()->GetAutofillTimestamps(
change->key().name(),
change->key().value(),
&timestamps)) {
@@ -145,7 +145,7 @@ void AutofillChangeProcessor::ObserveAutofillEntriesChanged(
}
std::vector<base::Time> timestamps;
- if (!web_database_->GetAutofillTimestamps(
+ if (!web_database_->GetAutofillTable()->GetAutofillTimestamps(
change->key().name(),
change->key().value(),
&timestamps)) {
@@ -173,8 +173,10 @@ void AutofillChangeProcessor::RemoveSyncNode(const std::string& tag,
sync_api::WriteNode sync_node(trans);
int64 sync_id = model_associator_->GetSyncIdFromChromeId(tag);
if (sync_api::kInvalidId == sync_id) {
- std::string err = "Unexpected notification for: " + tag;
- error_handler()->OnUnrecoverableError(FROM_HERE, err);
+ // This could happen because web db might have duplicates and when an entry
+ // and its duplicate is deleted.
+ LOG(WARNING) <<
+ "Bogus delete notification generate for autofill entry " + tag;
return;
} else {
if (!sync_node.InitByIdLookup(sync_id)) {
@@ -288,7 +290,7 @@ void AutofillChangeProcessor::CommitChangesFromSyncModel() {
autofill_changes_.clear();
// Make changes
- if (!web_database_->UpdateAutofillEntries(new_entries)) {
+ if (!web_database_->GetAutofillTable()->UpdateAutofillEntries(new_entries)) {
error_handler()->OnUnrecoverableError(FROM_HERE,
"Could not update autofill entries.");
return;
@@ -301,7 +303,7 @@ void AutofillChangeProcessor::CommitChangesFromSyncModel() {
void AutofillChangeProcessor::ApplySyncAutofillEntryDelete(
const sync_pb::AutofillSpecifics& autofill) {
- if (!web_database_->RemoveFormElement(
+ if (!web_database_->GetAutofillTable()->RemoveFormElement(
UTF8ToUTF16(autofill.name()), UTF8ToUTF16(autofill.value()))) {
error_handler()->OnUnrecoverableError(FROM_HERE,
"Could not remove autofill node.");
@@ -344,11 +346,11 @@ void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
DCHECK(false) << "Guid generated is invalid " << guid;
return;
}
- scoped_ptr<AutoFillProfile> p(new AutoFillProfile);
+ scoped_ptr<AutofillProfile> p(new AutofillProfile);
p->set_guid(guid);
AutofillModelAssociator::FillProfileWithServerData(p.get(),
profile);
- if (!web_database_->AddAutoFillProfile(*p.get())) {
+ if (!web_database_->GetAutofillTable()->AddAutofillProfile(*p.get())) {
NOTREACHED() << "Couldn't add autofill profile: " << guid;
return;
}
@@ -364,16 +366,18 @@ void AutofillChangeProcessor::ApplySyncAutofillProfileChange(
"model association has not happened");
return;
}
- AutoFillProfile *temp_ptr;
- if (!web_database_->GetAutoFillProfile(*guid, &temp_ptr)) {
+ AutofillProfile *temp_ptr;
+ if (!web_database_->GetAutofillTable()->GetAutofillProfile(
+ *guid, &temp_ptr)) {
LOG(ERROR) << "Autofill profile not found for " << *guid;
return;
}
- scoped_ptr<AutoFillProfile> p(temp_ptr);
+ scoped_ptr<AutofillProfile> p(temp_ptr);
AutofillModelAssociator::FillProfileWithServerData(p.get(), profile);
- if (!web_database_->UpdateAutoFillProfile(*(p.get()))) {
+ if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
+ *(p.get()))) {
LOG(ERROR) << "Couldn't update autofill profile: " << guid;
return;
}
@@ -393,7 +397,7 @@ void AutofillChangeProcessor::ApplySyncAutofillProfileDelete(
return;
}
- if (!web_database_->RemoveAutoFillProfile(*guid)) {
+ if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(*guid)) {
LOG(ERROR) << "Could not remove the profile";
return;
}
diff --git a/chrome/browser/sync/glue/autofill_change_processor.h b/chrome/browser/sync/glue/autofill_change_processor.h
index 3b5ff35..914feca 100644
--- a/chrome/browser/sync/glue/autofill_change_processor.h
+++ b/chrome/browser/sync/glue/autofill_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -16,8 +16,8 @@
#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/webdata/web_data_service.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
class AutofillCreditCardChange;
class AutofillEntry;
diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.cc b/chrome/browser/sync/glue/autofill_data_type_controller.cc
index 37582fe..fa5dde4 100644
--- a/chrome/browser/sync/glue/autofill_data_type_controller.cc
+++ b/chrome/browser/sync/glue/autofill_data_type_controller.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -14,8 +14,8 @@
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/webdata/web_data_service.h"
-#include "chrome/common/notification_service.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
namespace browser_sync {
@@ -39,6 +39,12 @@ AutofillDataTypeController::AutofillDataTypeController(
AutofillDataTypeController::~AutofillDataTypeController() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // TODO(zea): remove once crbug.com/61804 is resolved.
+ CHECK_EQ(state_, NOT_RUNNING) << "AutofillDataTypeController destroyed "
+ << "without being stopped.";
+ CHECK(!change_processor_.get()) << "AutofillDataTypeController destroyed "
+ << "while holding a change processor.";
}
void AutofillDataTypeController::Start(StartCallback* start_callback) {
@@ -46,7 +52,7 @@ void AutofillDataTypeController::Start(StartCallback* start_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(start_callback);
if (state() != NOT_RUNNING) {
- start_callback->Run(BUSY);
+ start_callback->Run(BUSY, FROM_HERE);
delete start_callback;
return;
}
@@ -100,6 +106,11 @@ void AutofillDataTypeController::Observe(NotificationType type,
&AutofillDataTypeController::StartImpl));
}
+// TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
+// distinguishing chrome shutdown from sync shutdown, we should be able to avoid
+// this (http://crbug.com/55662). Further, all this functionality should be
+// abstracted to a higher layer, where we could ensure all datatypes are doing
+// the same thing (http://crbug.com/76232).
void AutofillDataTypeController::Stop() {
VLOG(1) << "Stopping autofill data type controller.";
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
@@ -116,13 +127,15 @@ void AutofillDataTypeController::Stop() {
}
// Wait for the model association to abort.
abort_association_complete_.Wait();
- StartDoneImpl(ABORTED, STOPPING);
+ StartDoneImpl(ABORTED, STOPPING, FROM_HERE);
}
// If Stop() is called while Start() is waiting for the personal
// data manager or web data service to load, abort the start.
if (state_ == MODEL_STARTING)
- StartDoneImpl(ABORTED, STOPPING);
+ StartDoneImpl(ABORTED, STOPPING, FROM_HERE);
+
+ DCHECK(!start_callback_.get());
// Deactivate the change processor on the UI thread. We dont want to listen
// for any more changes or process them from server.
@@ -140,27 +153,33 @@ void AutofillDataTypeController::Stop() {
// particular, during shutdown we may attempt to destroy the
// profile_sync_service before we've removed its observers (BUG 61804).
datatype_stopped_.Wait();
+ } else if (change_processor_.get()) {
+ // TODO(zea): remove once crbug.com/61804 is resolved.
+ LOG(FATAL) << "AutofillDataTypeController::Stop() called after DB thread"
+ << " killed.";
}
+ CHECK(!change_processor_.get()) << "AutofillChangeProcessor not released.";
}
bool AutofillDataTypeController::enabled() {
return true;
}
-syncable::ModelType AutofillDataTypeController::type() {
+syncable::ModelType AutofillDataTypeController::type() const {
return syncable::AUTOFILL;
}
-browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group() {
+browser_sync::ModelSafeGroup AutofillDataTypeController::model_safe_group()
+ const {
return browser_sync::GROUP_DB;
}
-const char* AutofillDataTypeController::name() const {
+std::string AutofillDataTypeController::name() const {
// For logging only.
return "autofill";
}
-DataTypeController::State AutofillDataTypeController::state() {
+DataTypeController::State AutofillDataTypeController::state() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return state_;
}
@@ -199,6 +218,11 @@ void AutofillDataTypeController::StartImpl() {
change_processor_.reset(sync_components.change_processor);
}
+ if (!model_associator_->CryptoReadyIfNecessary()) {
+ StartFailed(NEEDS_CRYPTO);
+ return;
+ }
+
bool sync_has_nodes = false;
if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
StartFailed(UNRECOVERABLE_ERROR);
@@ -234,18 +258,20 @@ void AutofillDataTypeController::StartDone(
this,
&AutofillDataTypeController::StartDoneImpl,
result,
- new_state));
+ new_state,
+ FROM_HERE));
}
}
void AutofillDataTypeController::StartDoneImpl(
DataTypeController::StartResult result,
- DataTypeController::State new_state) {
+ DataTypeController::State new_state,
+ const tracked_objects::Location& location) {
VLOG(1) << "Autofill data type controller StartDoneImpl called.";
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
set_state(new_state);
- start_callback_->Run(result);
+ start_callback_->Run(result, location);
start_callback_.reset();
if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) {
diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.h b/chrome/browser/sync/glue/autofill_data_type_controller.h
index 05b964c..8df5fb8 100644
--- a/chrome/browser/sync/glue/autofill_data_type_controller.h
+++ b/chrome/browser/sync/glue/autofill_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,7 +9,7 @@
#include <string>
#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/profile_sync_factory.h"
@@ -43,13 +43,13 @@ class AutofillDataTypeController : public DataTypeController,
virtual bool enabled();
- virtual syncable::ModelType type();
+ virtual syncable::ModelType type() const;
- virtual browser_sync::ModelSafeGroup model_safe_group();
+ virtual browser_sync::ModelSafeGroup model_safe_group() const;
- virtual const char* name() const;
+ virtual std::string name() const;
- virtual State state();
+ virtual State state() const;
// UnrecoverableHandler implementation
virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
@@ -74,7 +74,8 @@ class AutofillDataTypeController : public DataTypeController,
private:
void StartImpl();
void StartDone(StartResult result, State state);
- void StartDoneImpl(StartResult result, State state);
+ void StartDoneImpl(StartResult result, State state,
+ const tracked_objects::Location& location);
void StopImpl();
void StartFailed(StartResult result);
void OnUnrecoverableErrorImpl(const tracked_objects::Location& from_here,
diff --git a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc
index 6d36e39..01deb74 100644
--- a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc
@@ -1,13 +1,13 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/glue/autofill_data_type_controller.h"
@@ -17,10 +17,10 @@
#include "chrome/browser/sync/profile_sync_service_mock.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/webdata/web_data_service.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
#include "chrome/test/profile_mock.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::WaitableEvent;
@@ -46,7 +46,8 @@ ACTION_P(SignalEvent, event) {
class StartCallback {
public:
- MOCK_METHOD1(Run, void(DataTypeController::StartResult result));
+ MOCK_METHOD2(Run, void(DataTypeController::StartResult result,
+ const tracked_objects::Location& location));
};
class PersonalDataManagerMock : public PersonalDataManager {
@@ -118,7 +119,8 @@ class AutofillDataTypeControllerTest : public testing::Test {
WillOnce(Return(
ProfileSyncFactory::SyncComponents(model_associator_,
change_processor_)));
-
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
EXPECT_CALL(*model_associator_, AssociateModels()).
@@ -160,7 +162,7 @@ TEST_F(AutofillDataTypeControllerTest, StartPDMAndWDSReady) {
SetStartExpectations();
SetAssociateExpectations();
EXPECT_EQ(DataTypeController::NOT_RUNNING, autofill_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK)).
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _)).
WillOnce(QuitUIMessageLoop());
autofill_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
MessageLoop::current()->Run();
@@ -179,7 +181,7 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhilePDMStarting) {
EXPECT_EQ(DataTypeController::MODEL_STARTING, autofill_dtc_->state());
EXPECT_CALL(service_, DeactivateDataType(_, _)).Times(0);
- EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _));
autofill_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, autofill_dtc_->state());
}
@@ -197,7 +199,7 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhileWDSStarting) {
EXPECT_EQ(DataTypeController::MODEL_STARTING, autofill_dtc_->state());
EXPECT_CALL(service_, DeactivateDataType(_, _)).Times(0);
- EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _));
autofill_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, autofill_dtc_->state());
}
@@ -221,6 +223,8 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhileAssociatingNotActivated) {
// that signals the event and lets the DB thread continue.
WaitableEvent pause_db_thread(false, false);
WaitableEvent wait_for_db_thread_pause(false, false);
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillOnce(DoAll(
SignalEvent(&wait_for_db_thread_pause),
@@ -235,7 +239,7 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhileAssociatingNotActivated) {
EXPECT_EQ(DataTypeController::ASSOCIATING, autofill_dtc_->state());
EXPECT_CALL(service_, DeactivateDataType(_, _)).Times(0);
- EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _));
autofill_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, autofill_dtc_->state());
}
@@ -250,6 +254,8 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhileAssociatingActivated) {
WillOnce(Return(
ProfileSyncFactory::SyncComponents(model_associator_,
change_processor_)));
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
EXPECT_CALL(*model_associator_, AssociateModels()).
@@ -273,7 +279,7 @@ TEST_F(AutofillDataTypeControllerTest, AbortWhileAssociatingActivated) {
EXPECT_EQ(DataTypeController::ASSOCIATING, autofill_dtc_->state());
SetStopExpectations();
- EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _));
autofill_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, autofill_dtc_->state());
}
diff --git a/chrome/browser/sync/glue/autofill_model_associator.cc b/chrome/browser/sync/glue/autofill_model_associator.cc
index 5ecefc6..493e372 100644
--- a/chrome/browser/sync/glue/autofill_model_associator.cc
+++ b/chrome/browser/sync/glue/autofill_model_associator.cc
@@ -35,8 +35,8 @@ struct AutofillModelAssociator::DataBundle {
std::set<AutofillKey> current_entries;
std::vector<AutofillEntry> new_entries;
std::set<string16> current_profiles;
- std::vector<AutoFillProfile*> updated_profiles;
- std::vector<AutoFillProfile*> new_profiles; // We own these pointers.
+ std::vector<AutofillProfile*> updated_profiles;
+ std::vector<AutofillProfile*> new_profiles; // We own these pointers.
~DataBundle() { STLDeleteElements(&new_profiles); }
};
@@ -118,15 +118,15 @@ bool AutofillModelAssociator::TraverseAndAssociateChromeAutofillEntries(
bool AutofillModelAssociator::LoadAutofillData(
std::vector<AutofillEntry>* entries,
- std::vector<AutoFillProfile*>* profiles) {
+ std::vector<AutofillProfile*>* profiles) {
if (IsAbortPending())
return false;
- if (!web_database_->GetAllAutofillEntries(entries))
+ if (!web_database_->GetAutofillTable()->GetAllAutofillEntries(entries))
return false;
if (IsAbortPending())
return false;
- if (!web_database_->GetAutoFillProfiles(profiles))
+ if (!web_database_->GetAutofillTable()->GetAutofillProfiles(profiles))
return false;
return true;
@@ -142,7 +142,7 @@ bool AutofillModelAssociator::AssociateModels() {
// TODO(zork): Attempt to load the model association from storage.
std::vector<AutofillEntry> entries;
- ScopedVector<AutoFillProfile> profiles;
+ ScopedVector<AutofillProfile> profiles;
if (!LoadAutofillData(&entries, &profiles.get())) {
LOG(ERROR) << "Could not get the autofill data from WebDatabase.";
@@ -178,7 +178,7 @@ bool AutofillModelAssociator::AssociateModels() {
// the autofill database after closing the write transaction, since
// this is the only thread that writes to the database. We also don't have
// to worry about the sync model getting out of sync, because changes are
- // propogated to the ChangeProcessor on this thread.
+ // propagated to the ChangeProcessor on this thread.
if (!SaveChangesToWebData(bundle)) {
LOG(ERROR) << "Failed to update autofill entries.";
return false;
@@ -206,21 +206,24 @@ bool AutofillModelAssociator::SaveChangesToWebData(const DataBundle& bundle) {
return false;
if (bundle.new_entries.size() &&
- !web_database_->UpdateAutofillEntries(bundle.new_entries)) {
+ !web_database_->GetAutofillTable()->UpdateAutofillEntries(
+ bundle.new_entries)) {
return false;
}
for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
if (IsAbortPending())
return false;
- if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i]))
+ if (!web_database_->GetAutofillTable()->AddAutofillProfile(
+ *bundle.new_profiles[i]))
return false;
}
for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
if (IsAbortPending())
return false;
- if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i]))
+ if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
+ *bundle.updated_profiles[i]))
return false;
}
return true;
@@ -230,7 +233,7 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
DataBundle* bundle,
- const std::vector<AutoFillProfile*>& all_profiles_from_db) {
+ const std::vector<AutofillProfile*>& all_profiles_from_db) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
bool autofill_profile_not_migrated = HasNotMigratedYet(write_trans);
@@ -239,12 +242,12 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
VLOG(2) << "[AUTOFILL MIGRATION]"
<< "Printing profiles from web db";
- for (std::vector<AutoFillProfile*>::const_iterator ix =
+ for (std::vector<AutofillProfile*>::const_iterator ix =
all_profiles_from_db.begin(); ix != all_profiles_from_db.end(); ++ix) {
- AutoFillProfile* p = *ix;
+ AutofillProfile* p = *ix;
VLOG(2) << "[AUTOFILL MIGRATION] "
- << p->GetFieldText(AutofillType(NAME_FIRST))
- << p->GetFieldText(AutofillType(NAME_LAST));
+ << p->GetInfo(NAME_FIRST)
+ << p->GetInfo(NAME_LAST);
}
}
@@ -288,8 +291,8 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
// Define the functor to be used as the predicate in find_if call.
struct CompareProfiles
- : public std::binary_function<AutoFillProfile*, AutoFillProfile*, bool> {
- bool operator() (AutoFillProfile* p1, AutoFillProfile* p2) const {
+ : public std::binary_function<AutofillProfile*, AutofillProfile*, bool> {
+ bool operator() (AutofillProfile* p1, AutofillProfile* p2) const {
if (p1->Compare(*p2) == 0)
return true;
else
@@ -297,11 +300,11 @@ struct CompareProfiles
}
};
-AutoFillProfile* AutofillModelAssociator::FindCorrespondingNodeFromWebDB(
+AutofillProfile* AutofillModelAssociator::FindCorrespondingNodeFromWebDB(
const sync_pb::AutofillProfileSpecifics& profile,
- const std::vector<AutoFillProfile*>& all_profiles_from_db) {
+ const std::vector<AutofillProfile*>& all_profiles_from_db) {
static std::string guid(guid::GenerateGUID());
- AutoFillProfile p;
+ AutofillProfile p;
p.set_guid(guid);
if (!FillProfileWithServerData(&p, profile)) {
// Not a big deal. We encountered an error. Just say this profile does not
@@ -311,7 +314,7 @@ AutoFillProfile* AutofillModelAssociator::FindCorrespondingNodeFromWebDB(
}
// Now instantiate the functor and call find_if.
- std::vector<AutoFillProfile*>::const_iterator ix =
+ std::vector<AutofillProfile*>::const_iterator ix =
std::find_if(all_profiles_from_db.begin(),
all_profiles_from_db.end(),
std::bind2nd(CompareProfiles(), &p));
@@ -342,11 +345,11 @@ void AutofillModelAssociator::AddNativeProfileIfNeeded(
const sync_pb::AutofillProfileSpecifics& profile,
DataBundle* bundle,
const sync_api::ReadNode& node,
- const std::vector<AutoFillProfile*>& all_profiles_from_db) {
+ const std::vector<AutofillProfile*>& all_profiles_from_db) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- AutoFillProfile* profile_in_web_db = FindCorrespondingNodeFromWebDB(
+ AutofillProfile* profile_in_web_db = FindCorrespondingNodeFromWebDB(
profile, all_profiles_from_db);
if (profile_in_web_db != NULL) {
@@ -365,7 +368,7 @@ void AutofillModelAssociator::AddNativeProfileIfNeeded(
return;
}
Associate(&guid, node.GetId());
- AutoFillProfile* p = new AutoFillProfile(guid);
+ AutofillProfile* p = new AutofillProfile(guid);
FillProfileWithServerData(p, profile);
bundle->new_profiles.push_back(p);
}
@@ -498,18 +501,18 @@ bool AutofillModelAssociator::MergeTimestamps(
// the local value if they differ, and return whether the merge happened.
bool MergeField(FormGroup* f, AutofillFieldType t,
const std::string& specifics_field) {
- if (UTF16ToUTF8(f->GetFieldText(AutofillType(t))) == specifics_field)
+ if (UTF16ToUTF8(f->GetInfo(t)) == specifics_field)
return false;
- f->SetInfo(AutofillType(t), UTF8ToUTF16(specifics_field));
+ f->SetInfo(t, UTF8ToUTF16(specifics_field));
return true;
}
// static
bool AutofillModelAssociator::FillProfileWithServerData(
- AutoFillProfile* merge_into,
+ AutofillProfile* merge_into,
const sync_pb::AutofillProfileSpecifics& specifics) {
bool diff = false;
- AutoFillProfile* p = merge_into;
+ AutofillProfile* p = merge_into;
const sync_pb::AutofillProfileSpecifics& s(specifics);
diff = MergeField(p, NAME_FIRST, s.name_first()) || diff;
diff = MergeField(p, NAME_LAST, s.name_last()) || diff;
@@ -575,4 +578,13 @@ bool AutofillModelAssociator::HasNotMigratedYet(
return false;
}
+bool AutofillModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ syncable::ModelTypeSet encrypted_types;
+ sync_service_->GetEncryptedDataTypes(&encrypted_types);
+ return encrypted_types.count(syncable::AUTOFILL) == 0 ||
+ sync_service_->IsCryptographerReady(&trans);
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_model_associator.h b/chrome/browser/sync/glue/autofill_model_associator.h
index 09cfcdc..0709c89 100644
--- a/chrome/browser/sync/glue/autofill_model_associator.h
+++ b/chrome/browser/sync/glue/autofill_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,7 +12,7 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/engine/syncapi.h"
@@ -20,7 +20,7 @@
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/webdata/autofill_entry.h"
-class AutoFillProfile;
+class AutofillProfile;
class ProfileSyncService;
class WebDatabase;
@@ -66,6 +66,9 @@ class AutofillModelAssociator
// See ModelAssociator interface.
virtual void AbortAssociation();
+ // See ModelAssociator interface.
+ virtual bool CryptoReadyIfNecessary();
+
// Not implemented.
virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id);
@@ -93,7 +96,7 @@ class AutofillModelAssociator
const std::vector<base::Time>& timestamps,
std::vector<base::Time>* new_timestamps);
static bool FillProfileWithServerData(
- AutoFillProfile* merge_into,
+ AutofillProfile* merge_into,
const sync_pb::AutofillProfileSpecifics& specifics);
// TODO(georgey) : add the same processing for CC info (already in protocol
@@ -108,9 +111,9 @@ class AutofillModelAssociator
protected:
// Given a profile from sync db it tries to match the profile against
// one in web db. it ignores the guid and compares the actual data.
- AutoFillProfile* FindCorrespondingNodeFromWebDB(
+ AutofillProfile* FindCorrespondingNodeFromWebDB(
const sync_pb::AutofillProfileSpecifics& profile,
- const std::vector<AutoFillProfile*>& all_profiles_from_db);
+ const std::vector<AutofillProfile*>& all_profiles_from_db);
private:
typedef std::map<std::string, int64> AutofillToSyncIdMap;
@@ -122,7 +125,7 @@ class AutofillModelAssociator
// Helper to query WebDatabase for the current autofill state.
bool LoadAutofillData(std::vector<AutofillEntry>* entries,
- std::vector<AutoFillProfile*>* profiles);
+ std::vector<AutofillProfile*>* profiles);
// We split up model association first by autofill sub-type (entries, and
// profiles. There is a Traverse* method for each of these.
@@ -132,12 +135,12 @@ class AutofillModelAssociator
const std::vector<AutofillEntry>& all_entries_from_db,
std::set<AutofillKey>* current_entries,
std::vector<AutofillEntry>* new_entries);
- bool TraverseAndAssociateChromeAutoFillProfiles(
+ bool TraverseAndAssociateChromeAutofillProfiles(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
- const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ const std::vector<AutofillProfile*>& all_profiles_from_db,
std::set<string16>* current_profiles,
- std::vector<AutoFillProfile*>* updated_profiles);
+ std::vector<AutofillProfile*>* updated_profiles);
// Once the above traversals are complete, we traverse the sync model to
// associate all remaining nodes.
@@ -145,7 +148,7 @@ class AutofillModelAssociator
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
DataBundle* bundle,
- const std::vector<AutoFillProfile*>& all_profiles_from_db);
+ const std::vector<AutofillProfile*>& all_profiles_from_db);
// Helper to persist any changes that occured during model association to
// the WebDatabase.
@@ -157,13 +160,13 @@ class AutofillModelAssociator
DataBundle* bundle,
const sync_api::ReadNode& node);
- // Helper to insert an AutoFillProfile into the WebDatabase (e.g. in response
+ // Helper to insert an AutofillProfile into the WebDatabase (e.g. in response
// to encountering a sync node that doesn't exist yet locally).
void AddNativeProfileIfNeeded(
const sync_pb::AutofillProfileSpecifics& profile,
DataBundle* bundle,
const sync_api::ReadNode& node,
- const std::vector<AutoFillProfile*>& all_profiles_from_db);
+ const std::vector<AutofillProfile*>& all_profiles_from_db);
// Called at various points in model association to determine if the
// user requested an abort.
diff --git a/chrome/browser/sync/glue/autofill_profile_change_processor.cc b/chrome/browser/sync/glue/autofill_profile_change_processor.cc
index ccf9c5a..833e58e 100644
--- a/chrome/browser/sync/glue/autofill_profile_change_processor.cc
+++ b/chrome/browser/sync/glue/autofill_profile_change_processor.cc
@@ -19,9 +19,9 @@
#include "chrome/browser/webdata/autofill_change.h"
#include "chrome/browser/webdata/web_database.h"
#include "chrome/common/guid.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_type.h"
namespace browser_sync {
@@ -179,7 +179,7 @@ void AutofillProfileChangeProcessor::CommitChangesFromSyncModel() {
for (unsigned int i = 0;i < autofill_changes_.size(); ++i) {
if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
autofill_changes_[i].action_) {
- if (!web_database_->RemoveAutoFillProfile(
+ if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(
autofill_changes_[i].profile_specifics_.guid())) {
LOG(ERROR) << "could not delete the profile " <<
autofill_changes_[i].profile_specifics_.guid();
@@ -218,10 +218,10 @@ void AutofillProfileChangeProcessor::ApplyAutofillProfileChange(
profile_specifics.guid();
return;
}
- AutoFillProfile p(profile_specifics.guid());
+ AutofillProfile p(profile_specifics.guid());
AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
profile_specifics);
- if (!web_database_->AddAutoFillProfile(p)) {
+ if (!web_database_->GetAutofillTable()->AddAutofillProfile(p)) {
LOG(ERROR) << "could not add autofill profile for guid " << p.guid();
break;
}
@@ -232,18 +232,20 @@ void AutofillProfileChangeProcessor::ApplyAutofillProfileChange(
break;
}
case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
- AutoFillProfile *p;
- if (!web_database_->GetAutoFillProfile(profile_specifics.guid(), &p)) {
+ AutofillProfile *p;
+ if (!web_database_->GetAutofillTable()->GetAutofillProfile(
+ profile_specifics.guid(), &p)) {
LOG(ERROR) << "Could not find the autofill profile to update for " <<
profile_specifics.guid();
break;
}
- scoped_ptr<AutoFillProfile> autofill_pointer(p);
+ scoped_ptr<AutofillProfile> autofill_pointer(p);
AutofillProfileModelAssociator::OverwriteProfileWithServerData(
autofill_pointer.get(),
profile_specifics);
- if (!web_database_->UpdateAutoFillProfile(*(autofill_pointer.get()))) {
+ if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
+ *(autofill_pointer.get()))) {
LOG(ERROR) << "Could not update autofill profile for " <<
profile_specifics.guid();
break;
@@ -278,7 +280,7 @@ void AutofillProfileChangeProcessor::RemoveSyncNode(const std::string& guid,
void AutofillProfileChangeProcessor::AddAutofillProfileSyncNode(
sync_api::WriteTransaction* trans,
sync_api::BaseNode& autofill_profile_root,
- const AutoFillProfile& profile) {
+ const AutofillProfile& profile) {
std::string guid = profile.guid();
@@ -315,7 +317,7 @@ void AutofillProfileChangeProcessor::StopObserving() {
}
void AutofillProfileChangeProcessor::WriteAutofillProfile(
- const AutoFillProfile& profile,
+ const AutofillProfile& profile,
sync_api::WriteNode* node) {
sync_pb::AutofillProfileSpecifics specifics;
@@ -326,32 +328,27 @@ void AutofillProfileChangeProcessor::WriteAutofillProfile(
DCHECK(guid::IsValidGUID(profile.guid()));
specifics.set_guid(profile.guid());
- specifics.set_name_first(UTF16ToUTF8(
- profile.GetFieldText(AutofillType(NAME_FIRST))));
- specifics.set_name_middle(UTF16ToUTF8(
- profile.GetFieldText(AutofillType(NAME_MIDDLE))));
- specifics.set_name_last(
- UTF16ToUTF8(profile.GetFieldText(AutofillType(NAME_LAST))));
+ specifics.set_name_first(UTF16ToUTF8(profile.GetInfo(NAME_FIRST)));
+ specifics.set_name_middle(UTF16ToUTF8(profile.GetInfo(NAME_MIDDLE)));
+ specifics.set_name_last(UTF16ToUTF8(profile.GetInfo(NAME_LAST)));
specifics.set_address_home_line1(
- UTF16ToUTF8(profile.GetFieldText(AutofillType(ADDRESS_HOME_LINE1))));
+ UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE1)));
specifics.set_address_home_line2(
- UTF16ToUTF8(profile.GetFieldText(AutofillType(ADDRESS_HOME_LINE2))));
- specifics.set_address_home_city(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(ADDRESS_HOME_CITY))));
- specifics.set_address_home_state(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(ADDRESS_HOME_STATE))));
- specifics.set_address_home_country(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(ADDRESS_HOME_COUNTRY))));
- specifics.set_address_home_zip(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(ADDRESS_HOME_ZIP))));
- specifics.set_email_address(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(EMAIL_ADDRESS))));
- specifics.set_company_name(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(COMPANY_NAME))));
- specifics.set_phone_fax_whole_number(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(PHONE_FAX_WHOLE_NUMBER))));
- specifics.set_phone_home_whole_number(UTF16ToUTF8(profile.GetFieldText(
- AutofillType(PHONE_HOME_WHOLE_NUMBER))));
+ UTF16ToUTF8(profile.GetInfo(ADDRESS_HOME_LINE2)));
+ specifics.set_address_home_city(UTF16ToUTF8(profile.GetInfo(
+ ADDRESS_HOME_CITY)));
+ specifics.set_address_home_state(UTF16ToUTF8(profile.GetInfo(
+ ADDRESS_HOME_STATE)));
+ specifics.set_address_home_country(UTF16ToUTF8(profile.GetInfo(
+ ADDRESS_HOME_COUNTRY)));
+ specifics.set_address_home_zip(UTF16ToUTF8(profile.GetInfo(
+ ADDRESS_HOME_ZIP)));
+ specifics.set_email_address(UTF16ToUTF8(profile.GetInfo(EMAIL_ADDRESS)));
+ specifics.set_company_name(UTF16ToUTF8(profile.GetInfo(COMPANY_NAME)));
+ specifics.set_phone_fax_whole_number(UTF16ToUTF8(profile.GetInfo(
+ PHONE_FAX_WHOLE_NUMBER)));
+ specifics.set_phone_home_whole_number(UTF16ToUTF8(profile.GetInfo(
+ PHONE_HOME_WHOLE_NUMBER)));
node->SetAutofillProfileSpecifics(specifics);
}
diff --git a/chrome/browser/sync/glue/autofill_profile_change_processor.h b/chrome/browser/sync/glue/autofill_profile_change_processor.h
index a1d44a3..9b08d3d 100644
--- a/chrome/browser/sync/glue/autofill_profile_change_processor.h
+++ b/chrome/browser/sync/glue/autofill_profile_change_processor.h
@@ -16,10 +16,10 @@
#include "chrome/browser/sync/unrecoverable_error_handler.h"
#include "chrome/browser/webdata/autofill_change.h"
#include "chrome/browser/webdata/web_database.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_type.h"
namespace browser_sync {
@@ -48,7 +48,7 @@ class AutofillProfileChangeProcessor : public ChangeProcessor,
const NotificationDetails& details);
- static void WriteAutofillProfile(const AutoFillProfile& profile,
+ static void WriteAutofillProfile(const AutofillProfile& profile,
sync_api::WriteNode* node);
protected:
@@ -63,7 +63,7 @@ class AutofillProfileChangeProcessor : public ChangeProcessor,
AutofillProfileChangeRecord(
sync_api::SyncManager::ChangeRecord::Action action,
int64 id,
- const sync_pb::AutofillProfileSpecifics profile_specifics)
+ const sync_pb::AutofillProfileSpecifics& profile_specifics)
: action_(action),
id_(id),
profile_specifics_(profile_specifics) {}
@@ -73,7 +73,7 @@ class AutofillProfileChangeProcessor : public ChangeProcessor,
virtual void AddAutofillProfileSyncNode(sync_api::WriteTransaction* trans,
sync_api::BaseNode& autofill_profile_root,
- const AutoFillProfile& profile);
+ const AutofillProfile& profile);
void ActOnChange(AutofillProfileChange* change,
sync_api::WriteTransaction* trans,
diff --git a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc
index 1b5214b..2203197 100644
--- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc
+++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.cc
@@ -19,11 +19,11 @@ AutofillProfileDataTypeController::AutofillProfileDataTypeController(
AutofillProfileDataTypeController::~AutofillProfileDataTypeController() {}
-syncable::ModelType AutofillProfileDataTypeController::type() {
+syncable::ModelType AutofillProfileDataTypeController::type() const {
return syncable::AUTOFILL_PROFILE;
}
-const char* AutofillProfileDataTypeController::name() const {
+std::string AutofillProfileDataTypeController::name() const {
// For logging only.
return "autofill_profile";
}
diff --git a/chrome/browser/sync/glue/autofill_profile_data_type_controller.h b/chrome/browser/sync/glue/autofill_profile_data_type_controller.h
index 83cafc3..d97a904 100644
--- a/chrome/browser/sync/glue/autofill_profile_data_type_controller.h
+++ b/chrome/browser/sync/glue/autofill_profile_data_type_controller.h
@@ -19,9 +19,9 @@ class AutofillProfileDataTypeController : public AutofillDataTypeController {
ProfileSyncService* sync_service);
virtual ~AutofillProfileDataTypeController();
- virtual syncable::ModelType type();
+ virtual syncable::ModelType type() const;
- virtual const char* name() const;
+ virtual std::string name() const;
protected:
virtual ProfileSyncFactory::SyncComponents CreateSyncComponents(
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator.cc b/chrome/browser/sync/glue/autofill_profile_model_associator.cc
index 1cadaf0..a982eba 100644
--- a/chrome/browser/sync/glue/autofill_profile_model_associator.cc
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.cc
@@ -45,25 +45,25 @@ AutofillProfileModelAssociator::AutofillProfileModelAssociator()
number_of_profiles_created_(0) {
}
-bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
+bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutofillProfiles(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
- const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ const std::vector<AutofillProfile*>& all_profiles_from_db,
std::set<std::string>* current_profiles,
- std::vector<AutoFillProfile*>* updated_profiles,
- std::vector<AutoFillProfile*>* new_profiles,
+ std::vector<AutofillProfile*>* updated_profiles,
+ std::vector<AutofillProfile*>* new_profiles,
std::vector<std::string>* profiles_to_delete) {
if (VLOG_IS_ON(2)) {
VLOG(2) << "[AUTOFILL MIGRATION]"
<< "Printing profiles from web db";
- for (std::vector<AutoFillProfile*>::const_iterator ix =
+ for (std::vector<AutofillProfile*>::const_iterator ix =
all_profiles_from_db.begin(); ix != all_profiles_from_db.end(); ++ix) {
- AutoFillProfile* p = *ix;
+ AutofillProfile* p = *ix;
VLOG(2) << "[AUTOFILL MIGRATION] "
- << p->GetFieldText(AutofillType(NAME_FIRST))
- << p->GetFieldText(AutofillType(NAME_LAST))
+ << p->GetInfo(NAME_FIRST)
+ << p->GetInfo(NAME_LAST)
<< p->guid();
}
}
@@ -72,8 +72,8 @@ bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
<< "Looking for the above data in sync db..";
// Alias the all_profiles_from_db so we fit in 80 characters
- const std::vector<AutoFillProfile*>& profiles(all_profiles_from_db);
- for (std::vector<AutoFillProfile*>::const_iterator ix = profiles.begin();
+ const std::vector<AutofillProfile*>& profiles(all_profiles_from_db);
+ for (std::vector<AutofillProfile*>::const_iterator ix = profiles.begin();
ix != profiles.end();
++ix) {
std::string guid((*ix)->guid());
@@ -90,8 +90,8 @@ bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
current_profiles->find(guid) == current_profiles->end()) {
VLOG(2) << "[AUTOFILL MIGRATION]"
<< " Found in sync db: "
- << (*ix)->GetFieldText(AutofillType(NAME_FIRST))
- << (*ix)->GetFieldText(AutofillType(NAME_LAST))
+ << (*ix)->GetInfo(NAME_FIRST)
+ << (*ix)->GetInfo(NAME_LAST)
<< (*ix)->guid()
<< " so associating with node id " << node.GetId();
const sync_pb::AutofillProfileSpecifics& autofill(
@@ -125,11 +125,11 @@ bool AutofillProfileModelAssociator::GetSyncIdForTaggedNode(
}
bool AutofillProfileModelAssociator::LoadAutofillData(
- std::vector<AutoFillProfile*>* profiles) {
+ std::vector<AutofillProfile*>* profiles) {
if (IsAbortPending())
return false;
- if (!web_database_->GetAutoFillProfiles(profiles))
+ if (!web_database_->GetAutofillTable()->GetAutofillProfiles(profiles))
return false;
return true;
@@ -143,7 +143,7 @@ bool AutofillProfileModelAssociator::AssociateModels() {
abort_association_pending_ = false;
}
- ScopedVector<AutoFillProfile> profiles;
+ ScopedVector<AutofillProfile> profiles;
if (!LoadAutofillData(&profiles.get())) {
LOG(ERROR) << "Could not get the autofill data from WebDatabase.";
@@ -166,7 +166,7 @@ bool AutofillProfileModelAssociator::AssociateModels() {
return false;
}
- if (!TraverseAndAssociateChromeAutoFillProfiles(&trans, autofill_root,
+ if (!TraverseAndAssociateChromeAutofillProfiles(&trans, autofill_root,
profiles.get(), &bundle.current_profiles,
&bundle.updated_profiles,
&bundle.new_profiles,
@@ -210,9 +210,9 @@ bool AutofillProfileModelAssociator::DisassociateModels() {
bool AutofillProfileModelAssociator::MergeField(FormGroup* f,
AutofillFieldType t,
const std::string& specifics_field) {
- if (UTF16ToUTF8(f->GetFieldText(AutofillType(t))) == specifics_field)
+ if (UTF16ToUTF8(f->GetInfo(t)) == specifics_field)
return false;
- f->SetInfo(AutofillType(t), UTF8ToUTF16(specifics_field));
+ f->SetInfo(t, UTF8ToUTF16(specifics_field));
return true;
}
bool AutofillProfileModelAssociator::SyncModelHasUserCreatedNodes(
@@ -233,10 +233,10 @@ bool AutofillProfileModelAssociator::SyncModelHasUserCreatedNodes(
}
// static
bool AutofillProfileModelAssociator::OverwriteProfileWithServerData(
- AutoFillProfile* merge_into,
+ AutofillProfile* merge_into,
const sync_pb::AutofillProfileSpecifics& specifics) {
bool diff = false;
- AutoFillProfile* p = merge_into;
+ AutofillProfile* p = merge_into;
const sync_pb::AutofillProfileSpecifics& s(specifics);
diff = MergeField(p, NAME_FIRST, s.name_first()) || diff;
diff = MergeField(p, NAME_LAST, s.name_last()) || diff;
@@ -260,12 +260,12 @@ bool AutofillProfileModelAssociator::OverwriteProfileWithServerData(
int64 AutofillProfileModelAssociator::FindSyncNodeWithProfile(
sync_api::WriteTransaction* trans,
const sync_api::BaseNode& autofill_root,
- const AutoFillProfile& profile_from_db,
+ const AutofillProfile& profile_from_db,
std::set<std::string>* current_profiles) {
int64 sync_child_id = autofill_root.GetFirstChildId();
while (sync_child_id != sync_api::kInvalidId) {
ReadNode read_node(trans);
- AutoFillProfile p;
+ AutofillProfile p;
if (!read_node.InitByIdLookup(sync_child_id)) {
LOG(ERROR) << "unable to find the id given by getfirst child " <<
sync_child_id;
@@ -290,8 +290,8 @@ int64 AutofillProfileModelAssociator::FindSyncNodeWithProfile(
bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
sync_api::WriteTransaction* trans,
const sync_api::BaseNode& autofill_root,
- const AutoFillProfile& profile,
- std::vector<AutoFillProfile*>* new_profiles,
+ const AutofillProfile& profile,
+ std::vector<AutofillProfile*>* new_profiles,
std::set<std::string>* current_profiles,
std::vector<std::string>* profiles_to_delete) {
@@ -315,7 +315,7 @@ bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
autofill_specifics.guid();
return false;
}
- AutoFillProfile* p = new AutoFillProfile(autofill_specifics.guid());
+ AutofillProfile* p = new AutofillProfile(autofill_specifics.guid());
OverwriteProfileWithServerData(p, autofill_specifics);
new_profiles->push_back(p);
std::string guid = autofill_specifics.guid();
@@ -323,8 +323,8 @@ bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
current_profiles->insert(autofill_specifics.guid());
VLOG(2) << "[AUTOFILL MIGRATION]"
<< "Found in sync db but with a different guid: "
- << UTF16ToUTF8(profile.GetFieldText(AutofillType(NAME_FIRST)))
- << UTF16ToUTF8(profile.GetFieldText(AutofillType(NAME_LAST)))
+ << UTF16ToUTF8(profile.GetInfo(NAME_FIRST))
+ << UTF16ToUTF8(profile.GetInfo(NAME_LAST))
<< "New guid " << autofill_specifics.guid() << " sync node id "
<< sync_node_id << " so associating. Profile to be deleted "
<< profile.guid();
@@ -342,8 +342,8 @@ bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
node.SetTitle(UTF8ToWide(profile.guid()));
VLOG(2) << "[AUTOFILL MIGRATION]"
<< "NOT Found in sync db "
- << UTF16ToUTF8(profile.GetFieldText(AutofillType(NAME_FIRST)))
- << UTF16ToUTF8(profile.GetFieldText(AutofillType(NAME_LAST)))
+ << UTF16ToUTF8(profile.GetInfo(NAME_FIRST))
+ << UTF16ToUTF8(profile.GetInfo(NAME_LAST))
<< profile.guid()
<< " so creating a new sync node. Sync node id "
<< node.GetId();
@@ -405,7 +405,7 @@ void AutofillProfileModelAssociator::AddNativeProfileIfNeeded(
bundle->current_profiles.end()) {
std::string guid(profile.guid());
Associate(&guid, node.GetId());
- AutoFillProfile* p = new AutoFillProfile(profile.guid());
+ AutofillProfile* p = new AutofillProfile(profile.guid());
OverwriteProfileWithServerData(p, profile);
bundle->new_profiles.push_back(p);
VLOG(2) << "[AUTOFILL MIGRATION] "
@@ -426,21 +426,24 @@ bool AutofillProfileModelAssociator::SaveChangesToWebData(
for (size_t i = 0; i < bundle.new_profiles.size(); i++) {
if (IsAbortPending())
return false;
- if (!web_database_->AddAutoFillProfile(*bundle.new_profiles[i]))
+ if (!web_database_->GetAutofillTable()->AddAutofillProfile(
+ *bundle.new_profiles[i]))
return false;
}
for (size_t i = 0; i < bundle.updated_profiles.size(); i++) {
if (IsAbortPending())
return false;
- if (!web_database_->UpdateAutoFillProfile(*bundle.updated_profiles[i]))
+ if (!web_database_->GetAutofillTable()->UpdateAutofillProfile(
+ *bundle.updated_profiles[i]))
return false;
}
for (size_t i = 0; i< bundle.profiles_to_delete.size(); ++i) {
if (IsAbortPending())
return false;
- if (!web_database_->RemoveAutoFillProfile(bundle.profiles_to_delete[i]))
+ if (!web_database_->GetAutofillTable()->RemoveAutofillProfile(
+ bundle.profiles_to_delete[i]))
return false;
}
return true;
@@ -501,6 +504,14 @@ AutofillProfileModelAssociator::DataBundle::~DataBundle() {
STLDeleteElements(&new_profiles);
}
+bool AutofillProfileModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ syncable::ModelTypeSet encrypted_types;
+ sync_service_->GetEncryptedDataTypes(&encrypted_types);
+ return encrypted_types.count(syncable::AUTOFILL_PROFILE) == 0 ||
+ sync_service_->IsCryptographerReady(&trans);
+}
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator.h b/chrome/browser/sync/glue/autofill_profile_model_associator.h
index 377d597..c8b99ac 100644
--- a/chrome/browser/sync/glue/autofill_profile_model_associator.h
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,7 +12,7 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/engine/syncapi.h"
@@ -20,7 +20,7 @@
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/webdata/autofill_entry.h"
-class AutoFillProfile;
+class AutofillProfile;
class ProfileSyncService;
class WebDatabase;
@@ -72,6 +72,9 @@ class AutofillProfileModelAssociator
// See ModelAssociator interface.
virtual void AbortAssociation();
+ // See ModelAssociator interface.
+ virtual bool CryptoReadyIfNecessary();
+
virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id);
virtual bool InitSyncNodeFromChromeId(const std::string& node_id,
@@ -94,35 +97,35 @@ class AutofillProfileModelAssociator
virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id);
static bool OverwriteProfileWithServerData(
- AutoFillProfile* merge_into,
+ AutofillProfile* merge_into,
const sync_pb::AutofillProfileSpecifics& specifics);
protected:
AutofillProfileModelAssociator();
- bool TraverseAndAssociateChromeAutoFillProfiles(
+ bool TraverseAndAssociateChromeAutofillProfiles(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
- const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ const std::vector<AutofillProfile*>& all_profiles_from_db,
std::set<std::string>* current_profiles,
- std::vector<AutoFillProfile*>* updated_profiles,
- std::vector<AutoFillProfile*>* new_profiles,
+ std::vector<AutofillProfile*>* updated_profiles,
+ std::vector<AutofillProfile*>* new_profiles,
std::vector<std::string>* profiles_to_delete);
- // Helper to insert an AutoFillProfile into the WebDatabase (e.g. in response
+ // Helper to insert an AutofillProfile into the WebDatabase (e.g. in response
// to encountering a sync node that doesn't exist yet locally).
virtual void AddNativeProfileIfNeeded(
const sync_pb::AutofillProfileSpecifics& profile,
DataBundle* bundle,
const sync_api::ReadNode& node);
- // Helper to insert a sync node for the given AutoFillProfile (e.g. in
+ // Helper to insert a sync node for the given AutofillProfile (e.g. in
// response to encountering a native profile that doesn't exist yet in the
// cloud).
virtual bool MakeNewAutofillProfileSyncNodeIfNeeded(
sync_api::WriteTransaction* trans,
const sync_api::BaseNode& autofill_root,
- const AutoFillProfile& profile,
- std::vector<AutoFillProfile*>* new_profiles,
+ const AutofillProfile& profile,
+ std::vector<AutofillProfile*>* new_profiles,
std::set<std::string>* current_profiles,
std::vector<std::string>* profiles_to_delete);
@@ -142,7 +145,7 @@ class AutofillProfileModelAssociator
// struct DataBundle;
// Helper to query WebDatabase for the current autofill state.
- bool LoadAutofillData(std::vector<AutoFillProfile*>* profiles);
+ bool LoadAutofillData(std::vector<AutofillProfile*>* profiles);
static bool MergeField(FormGroup* f,
AutofillFieldType t,
@@ -158,7 +161,7 @@ class AutofillProfileModelAssociator
int64 FindSyncNodeWithProfile(sync_api::WriteTransaction* trans,
const sync_api::BaseNode& autofill_root,
- const AutoFillProfile& profile,
+ const AutofillProfile& profile,
std::set<std::string>* current_profiles);
ProfileSyncService* sync_service_;
@@ -186,8 +189,8 @@ struct AutofillProfileModelAssociator::DataBundle {
std::set<std::string> current_profiles;
std::vector<std::string> profiles_to_delete;
- std::vector<AutoFillProfile*> updated_profiles;
- std::vector<AutoFillProfile*> new_profiles; // We own these pointers.
+ std::vector<AutofillProfile*> updated_profiles;
+ std::vector<AutofillProfile*> new_profiles; // We own these pointers.
};
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc b/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc
index 46287fd..e1792a6 100644
--- a/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator_unittest.cc
@@ -18,7 +18,7 @@ using ::testing::ReturnRef;
using ::testing::Pointee;
using ::testing::Ref;
using ::testing::Invoke;
-class AutoFillProfile;
+class AutofillProfile;
using browser_sync::AutofillProfileModelAssociator;
@@ -31,15 +31,15 @@ class MockAutofillProfileModelAssociator
MockAutofillProfileModelAssociator() {
}
virtual ~MockAutofillProfileModelAssociator() {}
- bool TraverseAndAssociateChromeAutoFillProfilesWrapper(
+ bool TraverseAndAssociateChromeAutofillProfilesWrapper(
sync_api::WriteTransaction* write_trans,
const sync_api::ReadNode& autofill_root,
- const std::vector<AutoFillProfile*>& all_profiles_from_db,
+ const std::vector<AutofillProfile*>& all_profiles_from_db,
std::set<std::string>* current_profiles,
- std::vector<AutoFillProfile*>* updated_profiles,
- std::vector<AutoFillProfile*>* new_profiles,
+ std::vector<AutofillProfile*>* updated_profiles,
+ std::vector<AutofillProfile*>* new_profiles,
std::vector<std::string>* profiles_to_delete) {
- return TraverseAndAssociateChromeAutoFillProfiles(write_trans,
+ return TraverseAndAssociateChromeAutofillProfiles(write_trans,
autofill_root,
all_profiles_from_db,
current_profiles,
@@ -52,13 +52,13 @@ class MockAutofillProfileModelAssociator
DataBundle*,
const sync_api::ReadNode&));
MOCK_METHOD2(OverwriteProfileWithServerData,
- bool(AutoFillProfile*,
+ bool(AutofillProfile*,
const sync_pb::AutofillProfileSpecifics&));
MOCK_METHOD6(MakeNewAutofillProfileSyncNodeIfNeeded,
bool(sync_api::WriteTransaction*,
const sync_api::BaseNode&,
- const AutoFillProfile&,
- std::vector<AutoFillProfile*>*,
+ const AutofillProfile&,
+ std::vector<AutofillProfile*>*,
std::set<std::string>*,
std::vector<std::string>*));
MOCK_METHOD2(Associate, void(const std::string*, int64));
@@ -94,7 +94,7 @@ class AutofillProfileModelAssociatorTest : public testing::Test {
TEST_F(AutofillProfileModelAssociatorTest,
TestAssociateProfileInWebDBWithSyncDB) {
- ScopedVector<AutoFillProfile> profiles_from_web_db;
+ ScopedVector<AutofillProfile> profiles_from_web_db;
std::string guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
sync_pb::EntitySpecifics specifics;
@@ -108,7 +108,7 @@ TEST_F(AutofillProfileModelAssociatorTest,
// This will be released inside the function
// TraverseAndAssociateChromeAutofillProfiles
- AutoFillProfile *profile = new AutoFillProfile(guid);
+ AutofillProfile *profile = new AutofillProfile(guid);
// Set up the entry kernel with what we want.
EntryKernel kernel;
@@ -125,7 +125,7 @@ TEST_F(AutofillProfileModelAssociatorTest,
profiles_from_web_db.push_back(profile);
- associator_.TraverseAndAssociateChromeAutoFillProfilesWrapper(&write_trans,
+ associator_.TraverseAndAssociateChromeAutofillProfilesWrapper(&write_trans,
read_node,
profiles_from_web_db.get(),
&current_profiles,
@@ -137,7 +137,7 @@ TEST_F(AutofillProfileModelAssociatorTest,
}
TEST_F(AutofillProfileModelAssociatorTest, TestAssociatingMissingWebDBProfile) {
- ScopedVector<AutoFillProfile> profiles_from_web_db;
+ ScopedVector<AutofillProfile> profiles_from_web_db;
MockDirectory mock_directory;
MockWriteTransaction write_trans(&mock_directory);
@@ -149,7 +149,7 @@ TEST_F(AutofillProfileModelAssociatorTest, TestAssociatingMissingWebDBProfile) {
std::string guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44A";
std::set<std::string> current_profiles;
- AutoFillProfile *profile = new AutoFillProfile(guid);
+ AutofillProfile *profile = new AutofillProfile(guid);
EXPECT_CALL(associator_,
MakeNewAutofillProfileSyncNodeIfNeeded(&write_trans,
@@ -162,7 +162,7 @@ TEST_F(AutofillProfileModelAssociatorTest, TestAssociatingMissingWebDBProfile) {
profiles_from_web_db.push_back(profile);
- associator_.TraverseAndAssociateChromeAutoFillProfilesWrapper(&write_trans,
+ associator_.TraverseAndAssociateChromeAutofillProfilesWrapper(&write_trans,
autofill_root,
profiles_from_web_db.get(),
&current_profiles,
@@ -254,8 +254,7 @@ TEST_F(AutofillProfileModelAssociatorTest, TestNeedToAddNativeProfile) {
read_node);
EXPECT_EQ(bundle.new_profiles.size(), (unsigned int)1);
- EXPECT_EQ(
- bundle.new_profiles.front()->GetFieldText(AutofillType(NAME_FIRST)),
- UTF8ToUTF16(first_name));
+ EXPECT_EQ(bundle.new_profiles.front()->GetInfo(NAME_FIRST),
+ UTF8ToUTF16(first_name));
}
diff --git a/chrome/browser/sync/glue/bookmark_change_processor.cc b/chrome/browser/sync/glue/bookmark_change_processor.cc
index ad673ef..0172cd2 100644
--- a/chrome/browser/sync/glue/bookmark_change_processor.cc
+++ b/chrome/browser/sync/glue/bookmark_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/bookmark_change_processor.h"
@@ -61,7 +61,7 @@ void BookmarkChangeProcessor::UpdateSyncNodeProperties(
void BookmarkChangeProcessor::EncodeFavicon(const BookmarkNode* src,
BookmarkModel* model,
std::vector<unsigned char>* dst) {
- const SkBitmap& favicon = model->GetFavIcon(src);
+ const SkBitmap& favicon = model->GetFavicon(src);
dst->clear();
@@ -95,7 +95,7 @@ void BookmarkChangeProcessor::RemoveSyncNodeHierarchy(
sync_api::WriteTransaction trans(share_handle());
// Later logic assumes that |topmost| has been unlinked.
- DCHECK(!topmost->GetParent());
+ DCHECK(!topmost->parent());
// A BookmarkModel deletion event means that |node| and all its children were
// deleted. Sync backend expects children to be deleted individually, so we do
@@ -107,19 +107,19 @@ void BookmarkChangeProcessor::RemoveSyncNodeHierarchy(
int index = 0;
while (node) {
// The top of |index_stack| should always be |node|'s index.
- DCHECK(!node->GetParent() || (node->GetParent()->IndexOfChild(node) ==
+ DCHECK(!node->parent() || (node->parent()->GetIndexOf(node) ==
index_stack.top()));
- if (index == node->GetChildCount()) {
+ if (index == node->child_count()) {
// If we've processed all of |node|'s children, delete |node| and move
// on to its successor.
RemoveOneSyncNode(&trans, node);
- node = node->GetParent();
+ node = node->parent();
index = index_stack.top() + 1; // (top() + 0) was what we removed.
index_stack.pop();
} else {
// If |node| has an unprocessed child, process it next after pushing the
// current state onto the stack.
- DCHECK_LT(index, node->GetChildCount());
+ DCHECK_LT(index, node->child_count());
index_stack.push(index);
node = node->GetChild(index);
index = 0;
@@ -163,8 +163,7 @@ int64 BookmarkChangeProcessor::CreateSyncNode(const BookmarkNode* parent,
sync_api::WriteNode sync_child(trans);
// Actually create the node with the appropriate initial position.
- if (!PlaceSyncNode(CREATE, parent, index, trans, &sync_child, associator,
- error_handler)) {
+ if (!PlaceSyncNode(CREATE, parent, index, trans, &sync_child, associator)) {
error_handler->OnUnrecoverableError(FROM_HERE,
"Sync node creation failed; recovery unlikely");
return sync_api::kInvalidId;
@@ -212,10 +211,10 @@ void BookmarkChangeProcessor::BookmarkNodeChanged(BookmarkModel* model,
DCHECK_EQ(sync_node.GetIsFolder(), node->is_folder());
DCHECK_EQ(model_associator_->GetChromeNodeFromSyncId(
sync_node.GetParentId()),
- node->GetParent());
+ node->parent());
// This node's index should be one more than the predecessor's index.
- DCHECK_EQ(node->GetParent()->IndexOfChild(node),
- CalculateBookmarkModelInsertionIndex(node->GetParent(),
+ DCHECK_EQ(node->parent()->GetIndexOf(node),
+ CalculateBookmarkModelInsertionIndex(node->parent(),
&sync_node));
}
@@ -242,13 +241,13 @@ void BookmarkChangeProcessor::BookmarkNodeMoved(BookmarkModel* model,
}
if (!PlaceSyncNode(MOVE, new_parent, new_index, &trans, &sync_node,
- model_associator_, error_handler())) {
+ model_associator_)) {
error_handler()->OnUnrecoverableError(FROM_HERE, std::string());
return;
}
}
-void BookmarkChangeProcessor::BookmarkNodeFavIconLoaded(BookmarkModel* model,
+void BookmarkChangeProcessor::BookmarkNodeFaviconLoaded(BookmarkModel* model,
const BookmarkNode* node) {
DCHECK(running());
BookmarkNodeChanged(model, node);
@@ -262,7 +261,7 @@ void BookmarkChangeProcessor::BookmarkNodeChildrenReordered(
// The given node's children got reordered. We need to reorder all the
// children of the corresponding sync node.
- for (int i = 0; i < node->GetChildCount(); ++i) {
+ for (int i = 0; i < node->child_count(); ++i) {
sync_api::WriteNode sync_child(&trans);
if (!model_associator_->InitSyncNodeFromChromeId(node->GetChild(i)->id(),
&sync_child)) {
@@ -273,7 +272,7 @@ void BookmarkChangeProcessor::BookmarkNodeChildrenReordered(
model_associator_->GetSyncIdFromChromeId(node->id()));
if (!PlaceSyncNode(MOVE, node, i, &trans, &sync_child,
- model_associator_, error_handler())) {
+ model_associator_)) {
error_handler()->OnUnrecoverableError(FROM_HERE, std::string());
return;
}
@@ -283,12 +282,10 @@ void BookmarkChangeProcessor::BookmarkNodeChildrenReordered(
// static
bool BookmarkChangeProcessor::PlaceSyncNode(MoveOrCreate operation,
const BookmarkNode* parent, int index, sync_api::WriteTransaction* trans,
- sync_api::WriteNode* dst, BookmarkModelAssociator* associator,
- UnrecoverableErrorHandler* error_handler) {
+ sync_api::WriteNode* dst, BookmarkModelAssociator* associator) {
sync_api::ReadNode sync_parent(trans);
if (!associator->InitSyncNodeFromChromeId(parent->id(), &sync_parent)) {
LOG(WARNING) << "Parent lookup failed";
- error_handler->OnUnrecoverableError(FROM_HERE, std::string());
return false;
}
@@ -342,8 +339,8 @@ int BookmarkChangeProcessor::CalculateBookmarkModelInsertionIndex(
const BookmarkNode* predecessor =
model_associator_->GetChromeNodeFromSyncId(predecessor_id);
DCHECK(predecessor);
- DCHECK_EQ(predecessor->GetParent(), parent);
- return parent->IndexOfChild(predecessor) + 1;
+ DCHECK_EQ(predecessor->parent(), parent);
+ return parent->GetIndexOf(predecessor) + 1;
}
// ApplyModelChanges is called by the sync backend after changes have been made
@@ -396,21 +393,25 @@ void BookmarkChangeProcessor::ApplyChangesFromSyncModel(
// Children of a deleted node should not be deleted; they may be
// reparented by a later change record. Move them to a temporary place.
DCHECK(dst) << "Could not find node to be deleted";
- const BookmarkNode* parent = dst->GetParent();
- if (dst->GetChildCount()) {
+ if (!dst) // Can't do anything if we can't find the chrome node.
+ continue;
+ const BookmarkNode* parent = dst->parent();
+ if (dst->child_count()) {
if (!foster_parent) {
- foster_parent = model->AddGroup(model->other_node(),
- model->other_node()->GetChildCount(),
- string16());
+ foster_parent = model->AddFolder(model->other_node(),
+ model->other_node()->child_count(),
+ string16());
}
- for (int i = dst->GetChildCount() - 1; i >= 0; --i) {
+ for (int i = dst->child_count() - 1; i >= 0; --i) {
model->Move(dst->GetChild(i), foster_parent,
- foster_parent->GetChildCount());
+ foster_parent->child_count());
}
}
- DCHECK_EQ(dst->GetChildCount(), 0) << "Node being deleted has children";
+ DCHECK_EQ(dst->child_count(), 0) << "Node being deleted has children";
model_associator_->Disassociate(changes[i].id);
- model->Remove(parent, parent->IndexOfChild(dst));
+ int index = parent->GetIndexOf(dst);
+ if (index > -1)
+ model->Remove(parent, index);
dst = NULL;
} else {
DCHECK_EQ((changes[i].action ==
@@ -430,9 +431,9 @@ void BookmarkChangeProcessor::ApplyChangesFromSyncModel(
// Clean up the temporary node.
if (foster_parent) {
// There should be no nodes left under the foster parent.
- DCHECK_EQ(foster_parent->GetChildCount(), 0);
- model->Remove(foster_parent->GetParent(),
- foster_parent->GetParent()->IndexOfChild(foster_parent));
+ DCHECK_EQ(foster_parent->child_count(), 0);
+ model->Remove(foster_parent->parent(),
+ foster_parent->parent()->GetIndexOf(foster_parent));
foster_parent = NULL;
}
@@ -472,7 +473,7 @@ const BookmarkNode* BookmarkChangeProcessor::CreateOrUpdateBookmarkNode(
model->SetURL(dst, src->GetURL());
model->SetTitle(dst, WideToUTF16Hack(src->GetTitle()));
- SetBookmarkFavicon(src, dst, model->profile());
+ SetBookmarkFavicon(src, dst, model);
}
return dst;
@@ -487,17 +488,17 @@ const BookmarkNode* BookmarkChangeProcessor::CreateBookmarkNode(
BookmarkModel* model,
int index) {
DCHECK(parent);
- DCHECK(index >= 0 && index <= parent->GetChildCount());
+ DCHECK(index >= 0 && index <= parent->child_count());
const BookmarkNode* node;
if (sync_node->GetIsFolder()) {
- node = model->AddGroup(parent, index,
- WideToUTF16Hack(sync_node->GetTitle()));
+ node = model->AddFolder(parent, index,
+ WideToUTF16Hack(sync_node->GetTitle()));
} else {
node = model->AddURL(parent, index,
WideToUTF16Hack(sync_node->GetTitle()),
sync_node->GetURL());
- SetBookmarkFavicon(sync_node, node, model->profile());
+ SetBookmarkFavicon(sync_node, node, model);
}
return node;
}
@@ -507,13 +508,14 @@ const BookmarkNode* BookmarkChangeProcessor::CreateBookmarkNode(
bool BookmarkChangeProcessor::SetBookmarkFavicon(
sync_api::BaseNode* sync_node,
const BookmarkNode* bookmark_node,
- Profile* profile) {
+ BookmarkModel* bookmark_model) {
std::vector<unsigned char> icon_bytes_vector;
sync_node->GetFaviconBytes(&icon_bytes_vector);
if (icon_bytes_vector.empty())
return false;
- ApplyBookmarkFavicon(bookmark_node, profile, icon_bytes_vector);
+ ApplyBookmarkFavicon(bookmark_node, bookmark_model->profile(),
+ icon_bytes_vector);
return true;
}
@@ -536,10 +538,11 @@ void BookmarkChangeProcessor::ApplyBookmarkFavicon(
FaviconService* favicon_service =
profile->GetFaviconService(Profile::EXPLICIT_ACCESS);
- history->AddPage(bookmark_node->GetURL(), history::SOURCE_SYNCED);
+ history->AddPageNoVisitForBookmark(bookmark_node->GetURL());
favicon_service->SetFavicon(bookmark_node->GetURL(),
fake_icon_url,
- icon_bytes_vector);
+ icon_bytes_vector,
+ history::FAVICON);
}
// static
diff --git a/chrome/browser/sync/glue/bookmark_change_processor.h b/chrome/browser/sync/glue/bookmark_change_processor.h
index 461cbb0..debc776 100644
--- a/chrome/browser/sync/glue/bookmark_change_processor.h
+++ b/chrome/browser/sync/glue/bookmark_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -14,8 +14,6 @@
#include "chrome/browser/sync/glue/bookmark_model_associator.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-class ProfileSyncService;
-
namespace browser_sync {
// This class is responsible for taking changes from the BookmarkModel
@@ -47,7 +45,7 @@ class BookmarkChangeProcessor : public BookmarkModelObserver,
const BookmarkNode* node);
virtual void BookmarkNodeChanged(BookmarkModel* model,
const BookmarkNode* node);
- virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ virtual void BookmarkNodeFaviconLoaded(BookmarkModel* model,
const BookmarkNode* node);
virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
const BookmarkNode* node);
@@ -75,7 +73,7 @@ class BookmarkChangeProcessor : public BookmarkModelObserver,
// for the bookmark in question.
static bool SetBookmarkFavicon(sync_api::BaseNode* sync_node,
const BookmarkNode* bookmark_node,
- Profile* profile);
+ BookmarkModel* model);
// Applies the favicon data in |icon_bytes_vector| to |bookmark_node|.
// |profile| is the profile that contains the HistoryService and BookmarkModel
@@ -140,8 +138,7 @@ class BookmarkChangeProcessor : public BookmarkModelObserver,
int index,
sync_api::WriteTransaction* trans,
sync_api::WriteNode* dst,
- BookmarkModelAssociator* associator,
- UnrecoverableErrorHandler* error_handler);
+ BookmarkModelAssociator* associator);
// Copy properties (but not position) from |src| to |dst|.
static void UpdateSyncNodeProperties(const BookmarkNode* src,
diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller.cc b/chrome/browser/sync/glue/bookmark_data_type_controller.cc
index 93d1430..8cd3d5b 100644
--- a/chrome/browser/sync/glue/bookmark_data_type_controller.cc
+++ b/chrome/browser/sync/glue/bookmark_data_type_controller.cc
@@ -1,22 +1,18 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/bookmark_data_type_controller.h"
-#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/time.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/glue/bookmark_change_processor.h"
-#include "chrome/browser/sync/glue/bookmark_model_associator.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
+#include "chrome/browser/sync/profile_sync_service.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
namespace browser_sync {
@@ -24,96 +20,31 @@ BookmarkDataTypeController::BookmarkDataTypeController(
ProfileSyncFactory* profile_sync_factory,
Profile* profile,
ProfileSyncService* sync_service)
- : profile_sync_factory_(profile_sync_factory),
- profile_(profile),
- sync_service_(sync_service),
- state_(NOT_RUNNING) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(profile_sync_factory);
- DCHECK(profile);
- DCHECK(sync_service);
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
}
BookmarkDataTypeController::~BookmarkDataTypeController() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
-void BookmarkDataTypeController::Start(StartCallback* start_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(start_callback);
- if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
- delete start_callback;
- return;
- }
-
- start_callback_.reset(start_callback);
-
- if (!enabled()) {
- FinishStart(NOT_ENABLED);
- return;
- }
-
- state_ = MODEL_STARTING;
-
+// We want to start the bookmark model before we begin associating.
+bool BookmarkDataTypeController::StartModels() {
// If the bookmarks model is loaded, continue with association.
BookmarkModel* bookmark_model = profile_->GetBookmarkModel();
if (bookmark_model && bookmark_model->IsLoaded()) {
- Associate();
- return;
+ return true; // Continue to Associate().
}
// Add an observer and continue when the bookmarks model is loaded.
registrar_.Add(this, NotificationType::BOOKMARK_MODEL_LOADED,
Source<Profile>(sync_service_->profile()));
+ return false; // Don't continue Start.
}
-void BookmarkDataTypeController::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- // If Stop() is called while Start() is waiting for the bookmark
- // model to load, abort the start.
- if (state_ == MODEL_STARTING)
- FinishStart(ABORTED);
-
+// Cleanup for our extra registrar usage.
+void BookmarkDataTypeController::CleanupState() {
registrar_.RemoveAll();
- if (change_processor_ != NULL)
- sync_service_->DeactivateDataType(this, change_processor_.get());
-
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
- change_processor_.reset();
- model_associator_.reset();
-
- state_ = NOT_RUNNING;
-}
-
-bool BookmarkDataTypeController::enabled() {
- return true;
-}
-
-syncable::ModelType BookmarkDataTypeController::type() {
- return syncable::BOOKMARKS;
-}
-
-browser_sync::ModelSafeGroup BookmarkDataTypeController::model_safe_group() {
- return browser_sync::GROUP_UI;
-}
-
-const char* BookmarkDataTypeController::name() const {
- // For logging only.
- return "bookmark";
-}
-
-DataTypeController::State BookmarkDataTypeController::state() {
- return state_;
-}
-
-void BookmarkDataTypeController::OnUnrecoverableError(
- const tracked_objects::Location& from_here, const std::string& message) {
- // The ProfileSyncService will invoke our Stop() method in response to this.
- UMA_HISTOGRAM_COUNTS("Sync.BookmarkRunFailures", 1);
- sync_service_->OnUnrecoverableError(from_here, message);
}
void BookmarkDataTypeController::Observe(NotificationType type,
@@ -122,52 +53,33 @@ void BookmarkDataTypeController::Observe(NotificationType type,
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK_EQ(NotificationType::BOOKMARK_MODEL_LOADED, type.value);
registrar_.RemoveAll();
+ DCHECK_EQ(state_, MODEL_STARTING);
+ state_ = ASSOCIATING;
Associate();
}
-void BookmarkDataTypeController::Associate() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK_EQ(state_, MODEL_STARTING);
- state_ = ASSOCIATING;
+syncable::ModelType BookmarkDataTypeController::type() const {
+ return syncable::BOOKMARKS;
+}
- ProfileSyncFactory::SyncComponents sync_components =
- profile_sync_factory_->CreateBookmarkSyncComponents(sync_service_, this);
+void BookmarkDataTypeController::CreateSyncComponents() {
+ ProfileSyncFactory::SyncComponents sync_components = profile_sync_factory_->
+ CreateBookmarkSyncComponents(sync_service_, this);
model_associator_.reset(sync_components.model_associator);
change_processor_.reset(sync_components.change_processor);
+}
- bool sync_has_nodes = false;
- if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
- StartFailed(UNRECOVERABLE_ERROR);
- return;
- }
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- bool merge_success = model_associator_->AssociateModels();
- UMA_HISTOGRAM_TIMES("Sync.BookmarkAssociationTime",
- base::TimeTicks::Now() - start_time);
- if (!merge_success) {
- StartFailed(ASSOCIATION_FAILED);
- return;
- }
-
- sync_service_->ActivateDataType(this, change_processor_.get());
- state_ = RUNNING;
- FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK);
+void BookmarkDataTypeController::RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) {
+ UMA_HISTOGRAM_COUNTS("Sync.BookmarkRunFailures", 1);
}
-void BookmarkDataTypeController::FinishStart(StartResult result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- start_callback_->Run(result);
- start_callback_.reset();
+void BookmarkDataTypeController::RecordAssociationTime(base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.BookmarkAssociationTime", time);
}
-void BookmarkDataTypeController::StartFailed(StartResult result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- model_associator_.reset();
- change_processor_.reset();
- state_ = NOT_RUNNING;
- start_callback_->Run(result);
- start_callback_.reset();
+void BookmarkDataTypeController::RecordStartFailure(StartResult result) {
UMA_HISTOGRAM_ENUMERATION("Sync.BookmarkStartFailures",
result,
MAX_START_RESULT);
diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller.h b/chrome/browser/sync/glue/bookmark_data_type_controller.h
index 0716b44..8741e84 100644
--- a/chrome/browser/sync/glue/bookmark_data_type_controller.h
+++ b/chrome/browser/sync/glue/bookmark_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,26 +8,18 @@
#include <string>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_observer.h"
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
class NotificationDetails;
class NotificationType;
class NotificationSource;
-class Profile;
-class ProfileSyncService;
-class ProfileSyncFactory;
namespace browser_sync {
-class AssociatorInterface;
-class ChangeProcessor;
-
// A class that manages the startup and shutdown of bookmark sync.
-class BookmarkDataTypeController : public DataTypeController,
+class BookmarkDataTypeController : public FrontendDataTypeController,
public NotificationObserver {
public:
BookmarkDataTypeController(
@@ -36,24 +28,8 @@ class BookmarkDataTypeController : public DataTypeController,
ProfileSyncService* sync_service);
virtual ~BookmarkDataTypeController();
- // DataTypeController interface.
- virtual void Start(StartCallback* start_callback);
-
- virtual void Stop();
-
- virtual bool enabled();
-
- virtual syncable::ModelType type();
-
- virtual browser_sync::ModelSafeGroup model_safe_group();
-
- virtual const char* name() const;
-
- virtual State state();
-
- // UnrecoverableErrorHandler interface.
- virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
- const std::string& message);
+ // FrontendDataTypeController interface.
+ virtual syncable::ModelType type() const;
// NotificationObserver interface.
virtual void Observe(NotificationType type,
@@ -61,24 +37,16 @@ class BookmarkDataTypeController : public DataTypeController,
const NotificationDetails& details);
private:
- // Runs model association and change processor registration.
- void Associate();
-
- // Helper method to run the stashed start callback with a given result.
- void FinishStart(StartResult result);
-
- // Cleans up state and calls callback when star fails.
- void StartFailed(StartResult result);
-
- ProfileSyncFactory* profile_sync_factory_;
- Profile* profile_;
- ProfileSyncService* sync_service_;
-
- State state_;
+ // FrontendDataTypeController interface.
+ virtual bool StartModels();
+ virtual void CleanupState();
+ virtual void CreateSyncComponents();
+ virtual void RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message);
+ virtual void RecordAssociationTime(base::TimeDelta time);
+ virtual void RecordStartFailure(StartResult result);
- scoped_ptr<StartCallback> start_callback_;
- scoped_ptr<AssociatorInterface> model_associator_;
- scoped_ptr<ChangeProcessor> change_processor_;
NotificationRegistrar registrar_;
DISALLOW_COPY_AND_ASSIGN(BookmarkDataTypeController);
diff --git a/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc b/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc
index 40edc78..af20af5 100644
--- a/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/bookmark_data_type_controller_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profiles/profile.h"
@@ -15,11 +15,11 @@
#include "chrome/browser/sync/glue/model_associator_mock.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
#include "chrome/browser/sync/profile_sync_service_mock.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
#include "chrome/test/profile_mock.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
using browser_sync::BookmarkDataTypeController;
@@ -34,7 +34,8 @@ using testing::SetArgumentPointee;
class StartCallback {
public:
- MOCK_METHOD1(Run, void(DataTypeController::StartResult result));
+ MOCK_METHOD2(Run, void(DataTypeController::StartResult result,
+ const tracked_objects::Location& location));
};
class BookmarkModelMock : public BookmarkModel {
@@ -68,6 +69,8 @@ class BookmarkDataTypeControllerTest : public testing::Test {
}
void SetAssociateExpectations() {
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
@@ -99,7 +102,7 @@ TEST_F(BookmarkDataTypeControllerTest, StartBookmarkModelReady) {
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, bookmark_dtc_->state());
}
@@ -109,7 +112,7 @@ TEST_F(BookmarkDataTypeControllerTest, StartBookmarkModelNotReady) {
EXPECT_CALL(bookmark_model_, IsLoaded()).WillRepeatedly(Return(false));
SetAssociateExpectations();
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::MODEL_STARTING, bookmark_dtc_->state());
@@ -126,7 +129,7 @@ TEST_F(BookmarkDataTypeControllerTest, StartFirstRun) {
SetAssociateExpectations();
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -134,7 +137,7 @@ TEST_F(BookmarkDataTypeControllerTest, StartBusy) {
SetStartExpectations();
EXPECT_CALL(bookmark_model_, IsLoaded()).WillRepeatedly(Return(false));
- EXPECT_CALL(start_callback_, Run(DataTypeController::BUSY));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::BUSY, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -145,7 +148,7 @@ TEST_F(BookmarkDataTypeControllerTest, StartOk) {
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -153,12 +156,14 @@ TEST_F(BookmarkDataTypeControllerTest, StartAssociationFailed) {
SetStartExpectations();
// Set up association to fail.
EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _));
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
EXPECT_CALL(*model_associator_, AssociateModels()).
WillRepeatedly(Return(false));
- EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
}
@@ -168,9 +173,11 @@ TEST_F(BookmarkDataTypeControllerTest,
SetStartExpectations();
// Set up association to fail with an unrecoverable error.
EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _));
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
}
@@ -179,7 +186,7 @@ TEST_F(BookmarkDataTypeControllerTest, StartAborted) {
SetStartExpectations();
EXPECT_CALL(bookmark_model_, IsLoaded()).WillRepeatedly(Return(false));
- EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
bookmark_dtc_->Stop();
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
@@ -192,7 +199,7 @@ TEST_F(BookmarkDataTypeControllerTest, Stop) {
EXPECT_EQ(DataTypeController::NOT_RUNNING, bookmark_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, bookmark_dtc_->state());
bookmark_dtc_->Stop();
@@ -209,7 +216,7 @@ TEST_F(BookmarkDataTypeControllerTest, OnUnrecoverableError) {
&BookmarkDataTypeController::Stop));
SetStopExpectations();
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
bookmark_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
// This should cause bookmark_dtc_->Stop() to be called.
bookmark_dtc_->OnUnrecoverableError(FROM_HERE, "Test");
diff --git a/chrome/browser/sync/glue/bookmark_model_associator.cc b/chrome/browser/sync/glue/bookmark_model_associator.cc
index 743623d..c839501 100644
--- a/chrome/browser/sync/glue/bookmark_model_associator.cc
+++ b/chrome/browser/sync/glue/bookmark_model_associator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,9 +13,10 @@
#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
-#include "chrome/browser/sync/syncable/autofill_migration.h"
#include "chrome/browser/sync/glue/bookmark_change_processor.h"
-#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/syncable/autofill_migration.h"
+#include "chrome/browser/sync/syncable/nigori_util.h"
+#include "chrome/browser/sync/util/cryptographer.h"
#include "content/browser/browser_thread.h"
namespace browser_sync {
@@ -86,7 +87,7 @@ class BookmarkNodeFinder {
BookmarkNodeFinder::BookmarkNodeFinder(const BookmarkNode* parent_node)
: parent_node_(parent_node) {
- for (int i = 0; i < parent_node_->GetChildCount(); ++i) {
+ for (int i = 0; i < parent_node_->child_count(); ++i) {
child_nodes_.insert(parent_node_->GetChild(i));
}
}
@@ -95,7 +96,7 @@ const BookmarkNode* BookmarkNodeFinder::FindBookmarkNode(
const sync_api::BaseNode& sync_node) {
// Create a bookmark node from the given sync node.
BookmarkNode temp_node(sync_node.GetURL());
- temp_node.SetTitle(WideToUTF16Hack(sync_node.GetTitle()));
+ temp_node.set_title(WideToUTF16Hack(sync_node.GetTitle()));
if (sync_node.GetIsFolder())
temp_node.set_type(BookmarkNode::FOLDER);
else
@@ -146,7 +147,7 @@ void BookmarkNodeIdIndex::AddAll(const BookmarkNode* node) {
if (!node->is_folder())
return;
- for (int i = 0; i < node->GetChildCount(); ++i)
+ for (int i = 0; i < node->child_count(); ++i)
AddAll(node->GetChild(i));
}
@@ -156,15 +157,18 @@ const BookmarkNode* BookmarkNodeIdIndex::Find(int64 id) const {
}
BookmarkModelAssociator::BookmarkModelAssociator(
- ProfileSyncService* sync_service,
- UnrecoverableErrorHandler* persist_ids_error_handler)
- : sync_service_(sync_service),
- persist_ids_error_handler_(persist_ids_error_handler),
+ BookmarkModel* bookmark_model,
+ sync_api::UserShare* user_share,
+ UnrecoverableErrorHandler* unrecoverable_error_handler)
+ : bookmark_model_(bookmark_model),
+ user_share_(user_share),
+ unrecoverable_error_handler_(unrecoverable_error_handler),
ALLOW_THIS_IN_INITIALIZER_LIST(persist_associations_(this)),
number_of_new_sync_nodes_created_at_association_(0) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(sync_service_);
- DCHECK(persist_ids_error_handler_);
+ DCHECK(bookmark_model_);
+ DCHECK(user_share_);
+ DCHECK(unrecoverable_error_handler_);
}
BookmarkModelAssociator::~BookmarkModelAssociator() {
@@ -237,7 +241,7 @@ bool BookmarkModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
return false;
}
- sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ sync_api::ReadTransaction trans(user_share_);
sync_api::ReadNode bookmark_bar_node(&trans);
if (!bookmark_bar_node.InitByIdLookup(bookmark_bar_sync_id)) {
@@ -287,7 +291,7 @@ bool BookmarkModelAssociator::AssociateTaggedPermanentNode(
bool BookmarkModelAssociator::GetSyncIdForTaggedNode(const std::string& tag,
int64* sync_id) {
- sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ sync_api::ReadTransaction trans(user_share_);
sync_api::ReadNode sync_node(&trans);
if (!sync_node.InitByTagLookup(tag.c_str()))
return false;
@@ -326,34 +330,34 @@ bool BookmarkModelAssociator::BuildAssociations() {
// This algorithm will not do well if the folder name has changes but the
// children under them are all the same.
- BookmarkModel* model = sync_service_->profile()->GetBookmarkModel();
- DCHECK(model->IsLoaded());
+ DCHECK(bookmark_model_->IsLoaded());
// To prime our association, we associate the top-level nodes, Bookmark Bar
// and Other Bookmarks.
- if (!AssociateTaggedPermanentNode(model->other_node(), kOtherBookmarksTag)) {
+ if (!AssociateTaggedPermanentNode(bookmark_model_->other_node(),
+ kOtherBookmarksTag)) {
LOG(ERROR) << "Server did not create top-level nodes. Possibly we "
<< "are running against an out-of-date server?";
return false;
}
- if (!AssociateTaggedPermanentNode(model->GetBookmarkBarNode(),
+ if (!AssociateTaggedPermanentNode(bookmark_model_->GetBookmarkBarNode(),
kBookmarkBarTag)) {
LOG(ERROR) << "Server did not create top-level nodes. Possibly we "
<< "are running against an out-of-date server?";
return false;
}
int64 bookmark_bar_sync_id = GetSyncIdFromChromeId(
- model->GetBookmarkBarNode()->id());
+ bookmark_model_->GetBookmarkBarNode()->id());
DCHECK(bookmark_bar_sync_id != sync_api::kInvalidId);
int64 other_bookmarks_sync_id = GetSyncIdFromChromeId(
- model->other_node()->id());
+ bookmark_model_->other_node()->id());
DCHECK(other_bookmarks_sync_id != sync_api::kInvalidId);
std::stack<int64> dfs_stack;
dfs_stack.push(other_bookmarks_sync_id);
dfs_stack.push(bookmark_bar_sync_id);
- sync_api::WriteTransaction trans(sync_service_->GetUserShare());
+ sync_api::WriteTransaction trans(user_share_);
while (!dfs_stack.empty()) {
int64 sync_parent_id = dfs_stack.top();
@@ -382,17 +386,17 @@ bool BookmarkModelAssociator::BuildAssociations() {
const BookmarkNode* child_node = NULL;
child_node = node_finder.FindBookmarkNode(sync_child_node);
if (child_node) {
- model->Move(child_node, parent_node, index);
+ bookmark_model_->Move(child_node, parent_node, index);
// Set the favicon for bookmark node from sync node or vice versa.
if (BookmarkChangeProcessor::SetBookmarkFavicon(
- &sync_child_node, child_node, sync_service_->profile())) {
- BookmarkChangeProcessor::SetSyncNodeFavicon(child_node, model,
- &sync_child_node);
+ &sync_child_node, child_node, bookmark_model_)) {
+ BookmarkChangeProcessor::SetSyncNodeFavicon(
+ child_node, bookmark_model_, &sync_child_node);
}
} else {
// Create a new bookmark node for the sync node.
child_node = BookmarkChangeProcessor::CreateBookmarkNode(
- &sync_child_node, parent_node, model, index);
+ &sync_child_node, parent_node, bookmark_model_, index);
}
Associate(child_node, sync_child_id);
if (sync_child_node.GetIsFolder())
@@ -407,24 +411,16 @@ bool BookmarkModelAssociator::BuildAssociations() {
// the right positions: from 0 to index - 1.
// So the children starting from index in the parent bookmark node are the
// ones that are not present in the parent sync node. So create them.
- for (int i = index; i < parent_node->GetChildCount(); ++i) {
+ for (int i = index; i < parent_node->child_count(); ++i) {
sync_child_id = BookmarkChangeProcessor::CreateSyncNode(
- parent_node, model, i, &trans, this, sync_service_);
+ parent_node, bookmark_model_, i, &trans, this,
+ unrecoverable_error_handler_);
if (parent_node->GetChild(i)->is_folder())
dfs_stack.push(sync_child_id);
number_of_new_sync_nodes_created_at_association_++;
}
}
- if (sync_service_->GetAutofillMigrationState() !=
- syncable::MIGRATED) {
- syncable::AutofillMigrationDebugInfo debug_info;
- debug_info.bookmarks_added_during_migration =
- number_of_new_sync_nodes_created_at_association_;
- sync_service_->SetAutofillMigrationDebugInfo(
- syncable::AutofillMigrationDebugInfo::BOOKMARK_ADDED,
- debug_info);
- }
return true;
}
@@ -448,7 +444,7 @@ void BookmarkModelAssociator::PersistAssociations() {
return;
}
- sync_api::WriteTransaction trans(sync_service_->GetUserShare());
+ sync_api::WriteTransaction trans(user_share_);
DirtyAssociationsSyncIds::iterator iter;
for (iter = dirty_associations_sync_ids_.begin();
iter != dirty_associations_sync_ids_.end();
@@ -456,7 +452,7 @@ void BookmarkModelAssociator::PersistAssociations() {
int64 sync_id = *iter;
sync_api::WriteNode sync_node(&trans);
if (!sync_node.InitByIdLookup(sync_id)) {
- persist_ids_error_handler_->OnUnrecoverableError(FROM_HERE,
+ unrecoverable_error_handler_->OnUnrecoverableError(FROM_HERE,
"Could not lookup bookmark node for ID persistence.");
return;
}
@@ -470,11 +466,10 @@ void BookmarkModelAssociator::PersistAssociations() {
}
bool BookmarkModelAssociator::LoadAssociations() {
- BookmarkModel* model = sync_service_->profile()->GetBookmarkModel();
- DCHECK(model->IsLoaded());
+ DCHECK(bookmark_model_->IsLoaded());
// If the bookmarks changed externally, our previous associations may not be
// valid; so return false.
- if (model->file_changed())
+ if (bookmark_model_->file_changed())
return false;
// Our persisted associations should be valid. Try to populate id association
@@ -495,14 +490,14 @@ bool BookmarkModelAssociator::LoadAssociations() {
// Build a bookmark node ID index since we are going to repeatedly search for
// bookmark nodes by their IDs.
BookmarkNodeIdIndex id_index;
- id_index.AddAll(model->GetBookmarkBarNode());
- id_index.AddAll(model->other_node());
+ id_index.AddAll(bookmark_model_->GetBookmarkBarNode());
+ id_index.AddAll(bookmark_model_->other_node());
std::stack<int64> dfs_stack;
dfs_stack.push(other_bookmarks_id);
dfs_stack.push(bookmark_bar_id);
- sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ sync_api::ReadTransaction trans(user_share_);
// Count total number of nodes in sync model so that we can compare that
// with the total number of nodes in the bookmark model.
@@ -526,8 +521,8 @@ bool BookmarkModelAssociator::LoadAssociations() {
// Don't try to call NodesMatch on permanent nodes like bookmark bar and
// other bookmarks. They are not expected to match.
- if (node != model->GetBookmarkBarNode() &&
- node != model->other_node() &&
+ if (node != bookmark_model_->GetBookmarkBarNode() &&
+ node != bookmark_model_->other_node() &&
!NodesMatch(node, &sync_parent))
return false;
@@ -554,4 +549,13 @@ bool BookmarkModelAssociator::LoadAssociations() {
return sync_node_count == id_index.count();
}
+bool BookmarkModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(user_share_);
+ const syncable::ModelTypeSet& encrypted_types =
+ GetEncryptedDataTypes(trans.GetWrappedTrans());
+ return encrypted_types.count(syncable::BOOKMARKS) == 0 ||
+ trans.GetCryptographer()->is_ready();
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/bookmark_model_associator.h b/chrome/browser/sync/glue/bookmark_model_associator.h
index 035f684..ac0da1c 100644
--- a/chrome/browser/sync/glue/bookmark_model_associator.h
+++ b/chrome/browser/sync/glue/bookmark_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -15,16 +15,16 @@
#include "chrome/browser/sync/unrecoverable_error_handler.h"
#include "chrome/browser/sync/glue/model_associator.h"
+class BookmarkModel;
class BookmarkNode;
namespace sync_api {
class BaseNode;
class BaseTransaction;
class ReadNode;
+struct UserShare;
}
-class ProfileSyncService;
-
namespace browser_sync {
class BookmarkChangeProcessor;
@@ -37,8 +37,10 @@ class BookmarkModelAssociator
: public PerDataTypeAssociatorInterface<BookmarkNode, int64> {
public:
static syncable::ModelType model_type() { return syncable::BOOKMARKS; }
- BookmarkModelAssociator(ProfileSyncService* sync_service,
- UnrecoverableErrorHandler* persist_ids_error_handler);
+ BookmarkModelAssociator(
+ BookmarkModel* bookmark_model,
+ sync_api::UserShare* user_share,
+ UnrecoverableErrorHandler* unrecoverable_error_handler);
virtual ~BookmarkModelAssociator();
// AssociatorInterface implementation.
@@ -83,15 +85,15 @@ class BookmarkModelAssociator
// thread.
}
+ // See ModelAssociator interface.
+ virtual bool CryptoReadyIfNecessary();
+
protected:
// Stores the id of the node with the given tag in |sync_id|.
// Returns of that node was found successfully.
// Tests override this.
virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id);
- // Used by TestBookmarkModelAssociator.
- ProfileSyncService* sync_service() { return sync_service_; }
-
private:
typedef std::map<int64, int64> BookmarkIdToSyncIdMap;
typedef std::map<int64, const BookmarkNode*> SyncIdToBookmarkNodeMap;
@@ -123,8 +125,9 @@ class BookmarkModelAssociator
bool NodesMatch(const BookmarkNode* bookmark,
const sync_api::BaseNode* sync_node) const;
- ProfileSyncService* sync_service_;
- UnrecoverableErrorHandler* persist_ids_error_handler_;
+ BookmarkModel* bookmark_model_;
+ sync_api::UserShare* user_share_;
+ UnrecoverableErrorHandler* unrecoverable_error_handler_;
BookmarkIdToSyncIdMap id_map_;
SyncIdToBookmarkNodeMap id_map_inverse_;
// Stores sync ids for dirty associations.
diff --git a/chrome/browser/sync/glue/data_type_controller.h b/chrome/browser/sync/glue/data_type_controller.h
index adedad7..3108310 100644
--- a/chrome/browser/sync/glue/data_type_controller.h
+++ b/chrome/browser/sync/glue/data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,9 +6,11 @@
#define CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_CONTROLLER_H__
#pragma once
+#include <string>
#include <map>
#include "base/callback.h"
+#include "base/tracked.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/unrecoverable_error_handler.h"
@@ -51,7 +53,8 @@ class DataTypeController
MAX_START_RESULT
};
- typedef Callback1<StartResult>::Type StartCallback;
+ typedef Callback2<StartResult,
+ const tracked_objects::Location&>::Type StartCallback;
typedef std::map<syncable::ModelType,
scoped_refptr<DataTypeController> > TypeMap;
@@ -71,23 +74,19 @@ class DataTypeController
// result.
virtual void Stop() = 0;
- // Returns true if the user has indicated that they want this data
- // type to be enabled.
- virtual bool enabled() = 0;
-
// Unique model type for this data type controller.
- virtual syncable::ModelType type() = 0;
+ virtual syncable::ModelType type() const = 0;
// Name of this data type. For logging purposes only.
- virtual const char* name() const = 0;
+ virtual std::string name() const = 0;
// The model safe group of this data type. This should reflect the
// thread that should be used to modify the data type's native
// model.
- virtual browser_sync::ModelSafeGroup model_safe_group() = 0;
+ virtual browser_sync::ModelSafeGroup model_safe_group() const = 0;
// Current state of the data type controller.
- virtual State state() = 0;
+ virtual State state() const = 0;
protected:
friend struct BrowserThread::DeleteOnThread<BrowserThread::UI>;
diff --git a/chrome/browser/sync/glue/data_type_controller_mock.h b/chrome/browser/sync/glue/data_type_controller_mock.h
index 83d0b6b..afde5cb 100644
--- a/chrome/browser/sync/glue/data_type_controller_mock.h
+++ b/chrome/browser/sync/glue/data_type_controller_mock.h
@@ -19,10 +19,10 @@ class DataTypeControllerMock : public DataTypeController {
MOCK_METHOD1(Start, void(StartCallback* start_callback));
MOCK_METHOD0(Stop, void());
MOCK_METHOD0(enabled, bool());
- MOCK_METHOD0(type, syncable::ModelType());
- MOCK_CONST_METHOD0(name, const char*());
- MOCK_METHOD0(model_safe_group, browser_sync::ModelSafeGroup());
- MOCK_METHOD0(state, State());
+ MOCK_CONST_METHOD0(type, syncable::ModelType());
+ MOCK_CONST_METHOD0(name, std::string());
+ MOCK_CONST_METHOD0(model_safe_group, browser_sync::ModelSafeGroup());
+ MOCK_CONST_METHOD0(state, State());
MOCK_METHOD2(OnUnrecoverableError, void(const tracked_objects::Location&,
const std::string&));
};
diff --git a/chrome/browser/sync/glue/data_type_manager.cc b/chrome/browser/sync/glue/data_type_manager.cc
new file mode 100644
index 0000000..993230b
--- /dev/null
+++ b/chrome/browser/sync/glue/data_type_manager.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/glue/data_type_manager.h"
+
+namespace browser_sync {
+
+DataTypeManager::ConfigureResultWithErrorLocation::
+ ~ConfigureResultWithErrorLocation() {}
+
+DataTypeManager::ConfigureResultWithErrorLocation::
+ ConfigureResultWithErrorLocation() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/data_type_manager.h b/chrome/browser/sync/glue/data_type_manager.h
index 99a9c29..4c97bee 100644
--- a/chrome/browser/sync/glue/data_type_manager.h
+++ b/chrome/browser/sync/glue/data_type_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,6 +8,7 @@
#include <set>
+#include "base/memory/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/syncable/model_type.h"
@@ -20,17 +21,13 @@ class DataTypeManager {
public:
enum State {
STOPPED, // No data types are currently running.
- RESTARTING, // Configuration has been changed...
DOWNLOAD_PENDING, // Not implemented yet: Waiting for the syncer to
// complete the initial download of new data
// types.
- // TODO(tim): Deprecated. Bug 26339.
- PAUSE_PENDING, // Waiting for the sync backend to pause.
CONFIGURING, // Data types are being started.
-
- // TODO(tim): Deprecated. Bug 26339.
- RESUME_PENDING, // Waiting for the sync backend to resume.
+ BLOCKED, // We can't move forward with configuration because some
+ // external action must take place (i.e. passphrase).
CONFIGURED, // All enabled data types are running.
STOPPING // Data types are being stopped.
@@ -47,6 +44,32 @@ class DataTypeManager {
typedef std::set<syncable::ModelType> TypeSet;
+ // In case of an error the location is filled with the location the
+ // error originated from. In case of a success the error location value
+ // is to be not used.
+ // TODO(tim): We should rename this / ConfigureResult to something more
+ // flexible like SyncConfigureDoneDetails.
+ struct ConfigureResultWithErrorLocation {
+ ConfigureResult result;
+ TypeSet requested_types;
+ scoped_ptr<tracked_objects::Location> location;
+
+ ConfigureResultWithErrorLocation();
+ ConfigureResultWithErrorLocation(const ConfigureResult& result,
+ const tracked_objects::Location& location,
+ const TypeSet& requested_types)
+ : result(result),
+ requested_types(requested_types) {
+ this->location.reset(new tracked_objects::Location(
+ location.function_name(),
+ location.file_name(),
+ location.line_number()));
+ }
+
+ ~ConfigureResultWithErrorLocation();
+ };
+
+
virtual ~DataTypeManager() {}
// Begins asynchronous configuration of data types. Any currently
diff --git a/chrome/browser/sync/glue/data_type_manager_impl.cc b/chrome/browser/sync/glue/data_type_manager_impl.cc
index bb89c6e..ef7936b 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl.cc
@@ -1,36 +1,44 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
+#include "chrome/browser/sync/glue/data_type_manager_impl.h"
+
#include <algorithm>
#include <functional>
+#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/message_loop.h"
-#include "base/task.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/browser/sync/glue/data_type_manager_impl.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_source.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_source.h"
namespace browser_sync {
namespace {
static const syncable::ModelType kStartOrder[] = {
+ syncable::NIGORI, // Listed for completeness.
syncable::BOOKMARKS,
syncable::PREFERENCES,
syncable::AUTOFILL,
syncable::AUTOFILL_PROFILE,
+ syncable::EXTENSIONS,
+ syncable::APPS,
syncable::THEMES,
syncable::TYPED_URLS,
syncable::PASSWORDS,
syncable::SESSIONS,
};
+COMPILE_ASSERT(arraysize(kStartOrder) ==
+ syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE,
+ kStartOrder_IncorrectSize);
+
// Comparator used when sorting data type controllers.
class SortComparator : public std::binary_function<DataTypeController*,
DataTypeController*,
@@ -50,18 +58,14 @@ class SortComparator : public std::binary_function<DataTypeController*,
} // namespace
-DataTypeManagerImpl::DataTypeManagerImpl(
- SyncBackendHost* backend,
+DataTypeManagerImpl::DataTypeManagerImpl(SyncBackendHost* backend,
const DataTypeController::TypeMap& controllers)
: backend_(backend),
controllers_(controllers),
state_(DataTypeManager::STOPPED),
- current_dtc_(NULL),
- pause_pending_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ needs_reconfigure_(false),
+ method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
DCHECK(backend_);
- DCHECK_GT(arraysize(kStartOrder), 0U);
// Ensure all data type controllers are stopped.
for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
it != controllers_.end(); ++it) {
@@ -73,7 +77,25 @@ DataTypeManagerImpl::DataTypeManagerImpl(
start_order_[kStartOrder[i]] = i;
}
-DataTypeManagerImpl::~DataTypeManagerImpl() {
+DataTypeManagerImpl::~DataTypeManagerImpl() {}
+
+bool DataTypeManagerImpl::GetControllersNeedingStart(
+ std::vector<DataTypeController*>* needs_start) {
+ // Add any data type controllers into the needs_start_ list that are
+ // currently NOT_RUNNING or STOPPING.
+ bool found_any = false;
+ for (TypeSet::const_iterator it = last_requested_types_.begin();
+ it != last_requested_types_.end(); ++it) {
+ DataTypeController::TypeMap::const_iterator dtc = controllers_.find(*it);
+ if (dtc != controllers_.end() &&
+ (dtc->second->state() == DataTypeController::NOT_RUNNING ||
+ dtc->second->state() == DataTypeController::STOPPING)) {
+ found_any = true;
+ if (needs_start)
+ needs_start->push_back(dtc->second.get());
+ }
+ }
+ return found_any;
}
void DataTypeManagerImpl::Configure(const TypeSet& desired_types) {
@@ -84,22 +106,17 @@ void DataTypeManagerImpl::Configure(const TypeSet& desired_types) {
return;
}
- backend_->UpdateEnabledTypes(desired_types);
-
last_requested_types_ = desired_types;
- // Add any data type controllers into the needs_start_ list that are
- // currently NOT_RUNNING or STOPPING.
- needs_start_.clear();
- for (TypeSet::const_iterator it = desired_types.begin();
- it != desired_types.end(); ++it) {
- DataTypeController::TypeMap::const_iterator dtc = controllers_.find(*it);
- if (dtc != controllers_.end() &&
- (dtc->second->state() == DataTypeController::NOT_RUNNING ||
- dtc->second->state() == DataTypeController::STOPPING)) {
- needs_start_.push_back(dtc->second.get());
- VLOG(1) << "Will start " << dtc->second->name();
- }
+ // Only proceed if we're in a steady state or blocked.
+ if (state_ != STOPPED && state_ != CONFIGURED && state_ != BLOCKED) {
+ VLOG(1) << "Received configure request while configuration in flight. "
+ << "Postponing until current configuration complete.";
+ needs_reconfigure_ = true;
+ return;
}
+
+ needs_start_.clear();
+ GetControllersNeedingStart(&needs_start_);
// Sort these according to kStartOrder.
std::sort(needs_start_.begin(),
needs_start_.end(),
@@ -124,36 +141,18 @@ void DataTypeManagerImpl::Configure(const TypeSet& desired_types) {
needs_stop_.end(),
SortComparator(&start_order_));
- // If nothing changed, we're done.
- if (needs_start_.empty() && needs_stop_.empty()) {
- state_ = CONFIGURED;
- NotifyStart();
- NotifyDone(OK);
- return;
- }
-
+ // Restart to start/stop data types and notify the backend that the
+ // desired types have changed (need to do this even if there aren't any
+ // types to start/stop, because it could be that some types haven't
+ // started due to crypto errors but the backend host needs to know that we're
+ // disabling them anyway).
Restart();
}
void DataTypeManagerImpl::Restart() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Restarting...";
- // If we are currently waiting for an asynchronous process to
- // complete, change our state to RESTARTING so those processes know
- // that we want to start over when they finish.
- if (state_ == DOWNLOAD_PENDING || state_ == PAUSE_PENDING ||
- state_ == CONFIGURING || state_ == RESUME_PENDING) {
- state_ = RESTARTING;
- return;
- }
- if (pause_pending_) { // Catch for http://crbug.com/73218.
- NOTREACHED() << "Attempted to restart DataTypeManager while already "
- << " configuring.";
- return;
- }
- DCHECK(state_ == STOPPED || state_ == RESTARTING || state_ == CONFIGURED);
- current_dtc_ = NULL;
+ DCHECK(state_ == STOPPED || state_ == CONFIGURED || state_ == BLOCKED);
// Starting from a "steady state" (stopped or configured) state
// should send a start notification.
@@ -174,122 +173,96 @@ void DataTypeManagerImpl::Restart() {
controllers_,
last_requested_types_,
method_factory_.NewRunnableMethod(&DataTypeManagerImpl::DownloadReady));
-
- // If there were new types needing download, a nudge will have been sent and
- // we should be in DOWNLOAD_PENDING. In that case we start the syncer thread
- // (which is idempotent) to fetch these updates.
- // However, we could actually be in PAUSE_PENDING here as if no new types
- // were needed, our DownloadReady callback will have fired and we will have
- // requested a pause already (so starting the syncer thread will still not
- // let it make forward progress as the pause needs to be resumed by us).
- // Because both the pause and start syncing commands are posted FCFS to the
- // core thread, there is no race between the pause and the start; the pause
- // will always win, so we will always start paused if we don't need to
- // download new types. Thus, in almost all cases, the syncer thread DOES NOT
- // start before model association. But...
- //
- // TODO(tim): Bug 47957. There is still subtle badness here. If we just
- // restarted the browser and were upgraded in between, we may be in a state
- // where a bunch of data types do have initial sync ended, but a new guy
- // does not. In this case, what we really want is to _only_ download updates
- // for that new type and not the ones that have already finished and we've
- // presumably associated before. What happens now is the syncer is nudged
- // and it does a sync from timestamp 0 for only the new types, and sends the
- // OnSyncCycleCompleted event, which is how we get the DownloadReady call.
- // We request a pause at this point, but it is done asynchronously. So in
- // theory, the syncer could charge forward with another sync (for _all_
- // types) before the pause is serviced, which could be bad for associating
- // models as we'll merge the present cloud with the immediate past, which
- // opens the door to bugs like "bookmark came back from dead". A lot more
- // stars have to align now for this to happen, but it's still there.
- backend_->StartSyncingWithServer();
}
void DataTypeManagerImpl::DownloadReady() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(state_ == DOWNLOAD_PENDING || state_ == RESTARTING);
+ DCHECK(state_ == DOWNLOAD_PENDING);
- // If we had a restart while waiting for downloads, just restart.
- // Note: Restart() can cause DownloadReady to be directly invoked, so we post
- // a task to avoid re-entrancy issues.
- if (state_ == RESTARTING) {
- MessageLoop::current()->PostTask(FROM_HERE,
- method_factory_.NewRunnableMethod(&DataTypeManagerImpl::Restart));
- return;
- }
-
- // Pause the sync backend before starting the data types.
- state_ = PAUSE_PENDING;
- PauseSyncer();
+ state_ = CONFIGURING;
+ StartNextType();
}
void DataTypeManagerImpl::StartNextType() {
// If there are any data types left to start, start the one at the
// front of the list.
if (!needs_start_.empty()) {
- current_dtc_ = needs_start_[0];
- VLOG(1) << "Starting " << current_dtc_->name();
- current_dtc_->Start(
+ VLOG(1) << "Starting " << needs_start_[0]->name();
+ needs_start_[0]->Start(
NewCallback(this, &DataTypeManagerImpl::TypeStartCallback));
return;
}
- // If no more data types need starting, we're done. Resume the sync
- // backend to finish.
DCHECK_EQ(state_, CONFIGURING);
- state_ = RESUME_PENDING;
- ResumeSyncer();
+
+ if (needs_reconfigure_) {
+ // An attempt was made to reconfigure while we were already configuring.
+ // This can be because a passphrase was accepted or the user changed the
+ // set of desired types. Either way, |last_requested_types_| will contain
+ // the most recent set of desired types, so we just call configure.
+ // Note: we do this whether or not GetControllersNeedingStart is true,
+ // because we may need to stop datatypes.
+ SetBlockedAndNotify();
+ needs_reconfigure_ = false;
+ VLOG(1) << "Reconfiguring due to previous configure attempt occuring while"
+ << " busy.";
+
+ // Unwind the stack before executing configure. The method configure and its
+ // callees are not re-entrant.
+ MessageLoop::current()->PostTask(FROM_HERE,
+ method_factory_.NewRunnableMethod(&DataTypeManagerImpl::Configure,
+ last_requested_types_));
+ return;
+ }
+
+ // Do a fresh calculation to see if controllers need starting to account for
+ // things like encryption, which may still need to be sorted out before we
+ // can announce we're "Done" configuration entirely.
+ if (GetControllersNeedingStart(NULL)) {
+ SetBlockedAndNotify();
+ return;
+ }
+
+ // If no more data types need starting, we're done.
+ state_ = CONFIGURED;
+ NotifyDone(OK, FROM_HERE);
}
void DataTypeManagerImpl::TypeStartCallback(
- DataTypeController::StartResult result) {
+ DataTypeController::StartResult result,
+ const tracked_objects::Location& location) {
// When the data type controller invokes this callback, it must be
// on the UI thread.
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(current_dtc_);
- // If configuration changed while this data type was starting, we
- // need to reset. Resume the syncer.
- if (state_ == RESTARTING) {
- ResumeSyncer();
+ if (state_ == STOPPING) {
+ // If we reach this callback while stopping, this means that
+ // DataTypeManager::Stop() was called while the current data type
+ // was starting. Now that it has finished starting, we can finish
+ // stopping the DataTypeManager. This is considered an ABORT.
+ FinishStopAndNotify(ABORTED, FROM_HERE);
+ return;
+ } else if (state_ == STOPPED) {
+ // If our state_ is STOPPED, we have already stopped all of the data
+ // types. We should not be getting callbacks from stopped data types.
+ LOG(ERROR) << "Start callback called by stopped data type!";
return;
}
// We're done with the data type at the head of the list -- remove it.
- DataTypeController* started_dtc = current_dtc_;
+ DataTypeController* started_dtc = needs_start_[0];
DCHECK(needs_start_.size());
DCHECK_EQ(needs_start_[0], started_dtc);
needs_start_.erase(needs_start_.begin());
- current_dtc_ = NULL;
- // If we reach this callback while stopping, this means that
- // DataTypeManager::Stop() was called while the current data type
- // was starting. Now that it has finished starting, we can finish
- // stopping the DataTypeManager. This is considered an ABORT.
- if (state_ == STOPPING) {
- FinishStopAndNotify(ABORTED);
- return;
- }
+ if (result == DataTypeController::NEEDS_CRYPTO) {
- // If our state_ is STOPPED, we have already stopped all of the data
- // types. We should not be getting callbacks from stopped data types.
- if (state_ == STOPPED) {
- LOG(ERROR) << "Start callback called by stopped data type!";
- return;
}
-
+ // If the type started normally, continue to the next type.
// If the type is waiting for the cryptographer, continue to the next type.
// Once the cryptographer is ready, we'll attempt to restart this type.
- if (result == DataTypeController::NEEDS_CRYPTO) {
- VLOG(1) << "Waiting for crypto " << started_dtc->name();
- StartNextType();
- return;
- }
-
- // If the type started normally, continue to the next type.
- if (result == DataTypeController::OK ||
+ if (result == DataTypeController::NEEDS_CRYPTO ||
+ result == DataTypeController::OK ||
result == DataTypeController::OK_FIRST_RUN) {
- VLOG(1) << "Started " << started_dtc->name();
StartNextType();
return;
}
@@ -313,7 +286,7 @@ void DataTypeManagerImpl::TypeStartCallback(
NOTREACHED();
break;
}
- FinishStopAndNotify(configure_result);
+ FinishStopAndNotify(configure_result, location);
}
void DataTypeManagerImpl::Stop() {
@@ -326,53 +299,33 @@ void DataTypeManagerImpl::Stop() {
// which will synchronously invoke the start callback.
if (state_ == CONFIGURING) {
state_ = STOPPING;
- current_dtc_->Stop();
- return;
- }
- // If Stop() is called while waiting for pause or resume, we no
- // longer care about this.
- bool aborted = false;
- if (state_ == PAUSE_PENDING) {
- pause_pending_ = false;
- RemoveObserver(NotificationType::SYNC_PAUSED);
- aborted = true;
- }
- if (state_ == RESUME_PENDING) {
- RemoveObserver(NotificationType::SYNC_RESUMED);
- aborted = true;
- }
+ DCHECK_LT(0U, needs_start_.size());
+ needs_start_[0]->Stop();
- // If Stop() is called while waiting for download, cancel all
- // outstanding tasks.
- if (state_ == DOWNLOAD_PENDING) {
- method_factory_.RevokeAll();
- aborted = true;
+ // By this point, the datatype should have invoked the start callback,
+ // triggering FinishStop to be called, and the state to reach STOPPED. If we
+ // aren't STOPPED, it means that a datatype controller didn't call the start
+ // callback appropriately.
+ DCHECK_EQ(STOPPED, state_);
+ return;
}
+ const bool download_pending = state_ == DOWNLOAD_PENDING;
state_ = STOPPING;
- if (aborted)
- FinishStopAndNotify(ABORTED);
- else
- FinishStop();
-}
-
-const DataTypeController::TypeMap& DataTypeManagerImpl::controllers() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- return controllers_;
-}
+ if (download_pending) {
+ // If Stop() is called while waiting for download, cancel all
+ // outstanding tasks.
+ method_factory_.RevokeAll();
+ FinishStopAndNotify(ABORTED, FROM_HERE);
+ return;
+ }
-DataTypeManager::State DataTypeManagerImpl::state() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- return state_;
+ FinishStop();
}
void DataTypeManagerImpl::FinishStop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(state_== CONFIGURING ||
- state_ == STOPPING ||
- state_ == PAUSE_PENDING ||
- state_ == RESUME_PENDING);
+ DCHECK(state_== CONFIGURING || state_ == STOPPING || state_ == BLOCKED);
// Simply call the Stop() method on all running data types.
for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
it != controllers_.end(); ++it) {
@@ -386,100 +339,43 @@ void DataTypeManagerImpl::FinishStop() {
state_ = STOPPED;
}
-void DataTypeManagerImpl::FinishStopAndNotify(ConfigureResult result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+void DataTypeManagerImpl::FinishStopAndNotify(ConfigureResult result,
+ const tracked_objects::Location& location) {
FinishStop();
- NotifyDone(result);
-}
-
-void DataTypeManagerImpl::Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- switch (type.value) {
- case NotificationType::SYNC_PAUSED:
- DCHECK(state_ == PAUSE_PENDING || state_ == RESTARTING);
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- pause_pending_ = false;
-
- RemoveObserver(NotificationType::SYNC_PAUSED);
-
- // If the state changed to RESTARTING while waiting to be
- // paused, resume the syncer so we can restart.
- if (state_ == RESTARTING) {
- ResumeSyncer();
- return;
- }
-
- state_ = CONFIGURING;
- StartNextType();
- break;
- case NotificationType::SYNC_RESUMED:
- DCHECK(state_ == RESUME_PENDING || state_ == RESTARTING);
- RemoveObserver(NotificationType::SYNC_RESUMED);
-
- // If we are resuming because of a restart, continue the restart.
- if (state_ == RESTARTING) {
- Restart();
- return;
- }
-
- state_ = CONFIGURED;
- NotifyDone(OK);
- break;
- default:
- NOTREACHED();
- }
-}
-
-void DataTypeManagerImpl::AddObserver(NotificationType type) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- notification_registrar_.Add(this,
- type,
- NotificationService::AllSources());
-}
-
-void DataTypeManagerImpl::RemoveObserver(NotificationType type) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- notification_registrar_.Remove(this,
- type,
- NotificationService::AllSources());
+ NotifyDone(result, location);
}
void DataTypeManagerImpl::NotifyStart() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
NotificationService::current()->Notify(
NotificationType::SYNC_CONFIGURE_START,
Source<DataTypeManager>(this),
NotificationService::NoDetails());
}
-void DataTypeManagerImpl::NotifyDone(ConfigureResult result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+void DataTypeManagerImpl::NotifyDone(ConfigureResult result,
+ const tracked_objects::Location& location) {
+ ConfigureResultWithErrorLocation result_with_location(result, location,
+ last_requested_types_);
NotificationService::current()->Notify(
NotificationType::SYNC_CONFIGURE_DONE,
Source<DataTypeManager>(this),
- Details<ConfigureResult>(&result));
+ Details<ConfigureResultWithErrorLocation>(&result_with_location));
}
-void DataTypeManagerImpl::ResumeSyncer() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- AddObserver(NotificationType::SYNC_RESUMED);
- if (!backend_->RequestResume()) {
- RemoveObserver(NotificationType::SYNC_RESUMED);
- FinishStopAndNotify(UNRECOVERABLE_ERROR);
- }
+const DataTypeController::TypeMap& DataTypeManagerImpl::controllers() {
+ return controllers_;
}
-void DataTypeManagerImpl::PauseSyncer() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- AddObserver(NotificationType::SYNC_PAUSED);
- pause_pending_ = true;
- if (!backend_->RequestPause()) {
- pause_pending_ = false;
- RemoveObserver(NotificationType::SYNC_PAUSED);
- FinishStopAndNotify(UNRECOVERABLE_ERROR);
- }
+DataTypeManager::State DataTypeManagerImpl::state() {
+ return state_;
+}
+
+void DataTypeManagerImpl::SetBlockedAndNotify() {
+ state_ = BLOCKED;
+ NotificationService::current()->Notify(
+ NotificationType::SYNC_CONFIGURE_BLOCKED,
+ Source<DataTypeManager>(this),
+ NotificationService::NoDetails());
}
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/data_type_manager_impl.h b/chrome/browser/sync/glue/data_type_manager_impl.h
index cc9e956..841af2b 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl.h
+++ b/chrome/browser/sync/glue/data_type_manager_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,39 +13,24 @@
#include "base/basictypes.h"
#include "base/task.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_type.h"
-
-class NotificationSource;
-class NotificationDetails;
namespace browser_sync {
class DataTypeController;
class SyncBackendHost;
-class DataTypeManagerImpl : public DataTypeManager,
- public NotificationObserver {
+class DataTypeManagerImpl : public DataTypeManager {
public:
DataTypeManagerImpl(SyncBackendHost* backend,
- const DataTypeController::TypeMap& controllers);
+ const DataTypeController::TypeMap& controllers);
virtual ~DataTypeManagerImpl();
// DataTypeManager interface.
virtual void Configure(const TypeSet& desired_types);
-
virtual void Stop();
-
virtual const DataTypeController::TypeMap& controllers();
-
virtual State state();
- // NotificationObserver implementation.
- virtual void Observe(NotificationType type,
- const NotificationSource& source,
- const NotificationDetails& details);
-
private:
// Starts the next data type in the kStartOrder list, indicated by
// the current_type_ member. If there are no more data types to
@@ -53,38 +38,40 @@ class DataTypeManagerImpl : public DataTypeManager,
void StartNextType();
// Callback passed to each data type controller on startup.
- void TypeStartCallback(DataTypeController::StartResult result);
+ void TypeStartCallback(DataTypeController::StartResult result,
+ const tracked_objects::Location& from_here);
// Stops all data types.
void FinishStop();
- void FinishStopAndNotify(ConfigureResult result);
+ void FinishStopAndNotify(ConfigureResult result,
+ const tracked_objects::Location& location);
+
+ // Returns true if any last_requested_types_ currently needs to start model
+ // association. If non-null, fills |needs_start| with all such controllers.
+ bool GetControllersNeedingStart(
+ std::vector<DataTypeController*>* needs_start);
void Restart();
void DownloadReady();
- void AddObserver(NotificationType type);
- void RemoveObserver(NotificationType type);
void NotifyStart();
- void NotifyDone(ConfigureResult result);
- void ResumeSyncer();
- void PauseSyncer();
+ void NotifyDone(ConfigureResult result,
+ const tracked_objects::Location& location);
+ void SetBlockedAndNotify();
SyncBackendHost* backend_;
// Map of all data type controllers that are available for sync.
// This list is determined at startup by various command line flags.
const DataTypeController::TypeMap controllers_;
State state_;
- DataTypeController* current_dtc_;
std::map<syncable::ModelType, int> start_order_;
TypeSet last_requested_types_;
std::vector<DataTypeController*> needs_start_;
std::vector<DataTypeController*> needs_stop_;
- // Safety check to ensure we never request more than one pause. This can be
- // true while state_ != PAUSE_PENDING if we attempt to restart while in
- // PAUSE_PENDING state. See http://crbug.com/73218.
- bool pause_pending_;
+ // Whether an attempt to reconfigure was made while we were busy configuring.
+ // The |last_requested_types_| will reflect the newest set of requested types.
+ bool needs_reconfigure_;
- NotificationRegistrar notification_registrar_;
ScopedRunnableMethodFactory<DataTypeManagerImpl> method_factory_;
DISALLOW_COPY_AND_ASSIGN(DataTypeManagerImpl);
diff --git a/chrome/browser/sync/glue/data_type_manager_impl2.cc b/chrome/browser/sync/glue/data_type_manager_impl2.cc
deleted file mode 100644
index af816fb..0000000
--- a/chrome/browser/sync/glue/data_type_manager_impl2.cc
+++ /dev/null
@@ -1,337 +0,0 @@
-// Copyright (c) 2011 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.
-
-#include "chrome/browser/sync/glue/data_type_manager_impl2.h"
-
-#include "base/compiler_specific.h"
-#include "base/logging.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_source.h"
-#include "content/browser/browser_thread.h"
-
-namespace browser_sync {
-
-namespace {
-
-static const syncable::ModelType kStartOrder[] = {
- syncable::NIGORI, // Listed for completeness.
- syncable::BOOKMARKS,
- syncable::PREFERENCES,
- syncable::AUTOFILL,
- syncable::AUTOFILL_PROFILE,
- syncable::EXTENSIONS,
- syncable::APPS,
- syncable::THEMES,
- syncable::TYPED_URLS,
- syncable::PASSWORDS,
- syncable::SESSIONS,
-};
-
-COMPILE_ASSERT(arraysize(kStartOrder) ==
- syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE,
- kStartOrder_IncorrectSize);
-
-// Comparator used when sorting data type controllers.
-class SortComparator : public std::binary_function<DataTypeController*,
- DataTypeController*,
- bool> {
- public:
- explicit SortComparator(std::map<syncable::ModelType, int>* order)
- : order_(order) { }
-
- // Returns true if lhs precedes rhs.
- bool operator() (DataTypeController* lhs, DataTypeController* rhs) {
- return (*order_)[lhs->type()] < (*order_)[rhs->type()];
- }
-
- private:
- std::map<syncable::ModelType, int>* order_;
-};
-
-} // namespace
-
-DataTypeManagerImpl2::DataTypeManagerImpl2(SyncBackendHost* backend,
- const DataTypeController::TypeMap& controllers)
- : backend_(backend),
- controllers_(controllers),
- state_(DataTypeManager::STOPPED),
- current_dtc_(NULL),
- method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
- DCHECK(backend_);
- // Ensure all data type controllers are stopped.
- for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
- it != controllers_.end(); ++it) {
- DCHECK_EQ(DataTypeController::NOT_RUNNING, (*it).second->state());
- }
-
- // Build a ModelType -> order map for sorting.
- for (int i = 0; i < static_cast<int>(arraysize(kStartOrder)); i++)
- start_order_[kStartOrder[i]] = i;
-}
-
-DataTypeManagerImpl2::~DataTypeManagerImpl2() {}
-
-void DataTypeManagerImpl2::Configure(const TypeSet& desired_types) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (state_ == STOPPING) {
- // You can not set a configuration while stopping.
- LOG(ERROR) << "Configuration set while stopping.";
- return;
- }
-
- last_requested_types_ = desired_types;
- // Add any data type controllers into the needs_start_ list that are
- // currently NOT_RUNNING or STOPPING.
- needs_start_.clear();
- for (TypeSet::const_iterator it = desired_types.begin();
- it != desired_types.end(); ++it) {
- DataTypeController::TypeMap::const_iterator dtc = controllers_.find(*it);
- if (dtc != controllers_.end() &&
- (dtc->second->state() == DataTypeController::NOT_RUNNING ||
- dtc->second->state() == DataTypeController::STOPPING)) {
- needs_start_.push_back(dtc->second.get());
- VLOG(1) << "Will start " << dtc->second->name();
- }
- }
- // Sort these according to kStartOrder.
- std::sort(needs_start_.begin(),
- needs_start_.end(),
- SortComparator(&start_order_));
-
- // Add any data type controllers into that needs_stop_ list that are
- // currently MODEL_STARTING, ASSOCIATING, or RUNNING.
- needs_stop_.clear();
- for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
- it != controllers_.end(); ++it) {
- DataTypeController* dtc = (*it).second;
- if (desired_types.count(dtc->type()) == 0 && (
- dtc->state() == DataTypeController::MODEL_STARTING ||
- dtc->state() == DataTypeController::ASSOCIATING ||
- dtc->state() == DataTypeController::RUNNING)) {
- needs_stop_.push_back(dtc);
- VLOG(1) << "Will stop " << dtc->name();
- }
- }
- // Sort these according to kStartOrder.
- std::sort(needs_stop_.begin(),
- needs_stop_.end(),
- SortComparator(&start_order_));
-
- // If nothing changed, we're done.
- if (needs_start_.empty() && needs_stop_.empty()) {
- state_ = CONFIGURED;
- NotifyStart();
- NotifyDone(OK);
- return;
- }
-
- Restart();
-}
-
-void DataTypeManagerImpl2::Restart() {
- VLOG(1) << "Restarting...";
-
- // If we are currently waiting for an asynchronous process to
- // complete, change our state to RESTARTING so those processes know
- // that we want to start over when they finish.
- if (state_ == DOWNLOAD_PENDING || state_ == CONFIGURING) {
- state_ = RESTARTING;
- return;
- }
-
- DCHECK(state_ == STOPPED || state_ == RESTARTING || state_ == CONFIGURED);
- current_dtc_ = NULL;
-
- // Starting from a "steady state" (stopped or configured) state
- // should send a start notification.
- if (state_ == STOPPED || state_ == CONFIGURED)
- NotifyStart();
-
- // Stop requested data types.
- for (size_t i = 0; i < needs_stop_.size(); ++i) {
- VLOG(1) << "Stopping " << needs_stop_[i]->name();
- needs_stop_[i]->Stop();
- }
- needs_stop_.clear();
-
- // Tell the backend about the new set of data types we wish to sync.
- // The task will be invoked when updates are downloaded.
- state_ = DOWNLOAD_PENDING;
- backend_->ConfigureDataTypes(
- controllers_,
- last_requested_types_,
- method_factory_.NewRunnableMethod(&DataTypeManagerImpl2::DownloadReady));
-}
-
-void DataTypeManagerImpl2::DownloadReady() {
- DCHECK(state_ == DOWNLOAD_PENDING || state_ == RESTARTING);
-
- // If we had a restart while waiting for downloads, just restart.
- // Note: Restart() can cause DownloadReady to be directly invoked, so we post
- // a task to avoid re-entrancy issues.
- if (state_ == RESTARTING) {
- MessageLoop::current()->PostTask(FROM_HERE,
- method_factory_.NewRunnableMethod(&DataTypeManagerImpl2::Restart));
- return;
- }
-
- state_ = CONFIGURING;
- StartNextType();
-}
-
-void DataTypeManagerImpl2::StartNextType() {
- // If there are any data types left to start, start the one at the
- // front of the list.
- if (!needs_start_.empty()) {
- current_dtc_ = needs_start_[0];
- VLOG(1) << "Starting " << current_dtc_->name();
- current_dtc_->Start(
- NewCallback(this, &DataTypeManagerImpl2::TypeStartCallback));
- return;
- }
-
- // If no more data types need starting, we're done.
- DCHECK_EQ(state_, CONFIGURING);
- state_ = CONFIGURED;
- NotifyDone(OK);
-}
-
-void DataTypeManagerImpl2::TypeStartCallback(
- DataTypeController::StartResult result) {
- // When the data type controller invokes this callback, it must be
- // on the UI thread.
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(current_dtc_);
-
- if (state_ == RESTARTING) {
- // If configuration changed while this data type was starting, we
- // need to reset.
- Restart();
- return;
- } else if (state_ == STOPPING) {
- // If we reach this callback while stopping, this means that
- // DataTypeManager::Stop() was called while the current data type
- // was starting. Now that it has finished starting, we can finish
- // stopping the DataTypeManager. This is considered an ABORT.
- FinishStopAndNotify(ABORTED);
- return;
- } else if (state_ == STOPPED) {
- // If our state_ is STOPPED, we have already stopped all of the data
- // types. We should not be getting callbacks from stopped data types.
- LOG(ERROR) << "Start callback called by stopped data type!";
- return;
- }
-
- // We're done with the data type at the head of the list -- remove it.
- DataTypeController* started_dtc = current_dtc_;
- DCHECK(needs_start_.size());
- DCHECK_EQ(needs_start_[0], started_dtc);
- needs_start_.erase(needs_start_.begin());
- current_dtc_ = NULL;
-
- // If the type started normally, continue to the next type.
- // If the type is waiting for the cryptographer, continue to the next type.
- // Once the cryptographer is ready, we'll attempt to restart this type.
- if (result == DataTypeController::NEEDS_CRYPTO ||
- result == DataTypeController::OK ||
- result == DataTypeController::OK_FIRST_RUN) {
- StartNextType();
- return;
- }
-
- // Any other result is a fatal error. Shut down any types we've
- // managed to start up to this point and pass the result to the
- // callback.
- VLOG(1) << "Failed " << started_dtc->name();
- ConfigureResult configure_result = DataTypeManager::ABORTED;
- switch (result) {
- case DataTypeController::ABORTED:
- configure_result = DataTypeManager::ABORTED;
- break;
- case DataTypeController::ASSOCIATION_FAILED:
- configure_result = DataTypeManager::ASSOCIATION_FAILED;
- break;
- case DataTypeController::UNRECOVERABLE_ERROR:
- configure_result = DataTypeManager::UNRECOVERABLE_ERROR;
- break;
- default:
- NOTREACHED();
- break;
- }
- FinishStopAndNotify(configure_result);
-}
-
-void DataTypeManagerImpl2::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- if (state_ == STOPPED)
- return;
-
- // If we are currently configuring, then the current type is in a
- // partially started state. Abort the startup of the current type,
- // which will synchronously invoke the start callback.
- if (state_ == CONFIGURING) {
- state_ = STOPPING;
- current_dtc_->Stop();
- return;
- }
-
- const bool download_pending = state_ == DOWNLOAD_PENDING;
- state_ = STOPPING;
- if (download_pending) {
- // If Stop() is called while waiting for download, cancel all
- // outstanding tasks.
- method_factory_.RevokeAll();
- FinishStopAndNotify(ABORTED);
- return;
- }
-
- FinishStop();
-}
-
-void DataTypeManagerImpl2::FinishStop() {
- DCHECK(state_== CONFIGURING || state_ == STOPPING);
- // Simply call the Stop() method on all running data types.
- for (DataTypeController::TypeMap::const_iterator it = controllers_.begin();
- it != controllers_.end(); ++it) {
- DataTypeController* dtc = (*it).second;
- if (dtc->state() != DataTypeController::NOT_RUNNING &&
- dtc->state() != DataTypeController::STOPPING) {
- dtc->Stop();
- VLOG(1) << "Stopped " << dtc->name();
- }
- }
- state_ = STOPPED;
-}
-
-void DataTypeManagerImpl2::FinishStopAndNotify(ConfigureResult result) {
- FinishStop();
- NotifyDone(result);
-}
-
-void DataTypeManagerImpl2::NotifyStart() {
- NotificationService::current()->Notify(
- NotificationType::SYNC_CONFIGURE_START,
- Source<DataTypeManager>(this),
- NotificationService::NoDetails());
-}
-
-void DataTypeManagerImpl2::NotifyDone(ConfigureResult result) {
- NotificationService::current()->Notify(
- NotificationType::SYNC_CONFIGURE_DONE,
- Source<DataTypeManager>(this),
- Details<ConfigureResult>(&result));
-}
-
-const DataTypeController::TypeMap& DataTypeManagerImpl2::controllers() {
- return controllers_;
-}
-
-DataTypeManager::State DataTypeManagerImpl2::state() {
- return state_;
-}
-
-} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/data_type_manager_impl2.h b/chrome/browser/sync/glue/data_type_manager_impl2.h
deleted file mode 100644
index 570a38b..0000000
--- a/chrome/browser/sync/glue/data_type_manager_impl2.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (c) 2011 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.
-
-#ifndef CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_MANAGER_IMPL2_H__
-#define CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_MANAGER_IMPL2_H__
-#pragma once
-
-#include "chrome/browser/sync/glue/data_type_manager.h"
-
-#include <map>
-#include <vector>
-
-#include "base/basictypes.h"
-#include "base/task.h"
-
-namespace browser_sync {
-
-class DataTypeController;
-class SyncBackendHost;
-
-class DataTypeManagerImpl2 : public DataTypeManager {
- public:
- DataTypeManagerImpl2(SyncBackendHost* backend,
- const DataTypeController::TypeMap& controllers);
- virtual ~DataTypeManagerImpl2();
-
- // DataTypeManager interface.
- virtual void Configure(const TypeSet& desired_types);
- virtual void Stop();
- virtual const DataTypeController::TypeMap& controllers();
- virtual State state();
-
- private:
- // Starts the next data type in the kStartOrder list, indicated by
- // the current_type_ member. If there are no more data types to
- // start, the stashed start_callback_ is invoked.
- void StartNextType();
-
- // Callback passed to each data type controller on startup.
- void TypeStartCallback(DataTypeController::StartResult result);
-
- // Stops all data types.
- void FinishStop();
- void FinishStopAndNotify(ConfigureResult result);
-
- void Restart();
- void DownloadReady();
- void NotifyStart();
- void NotifyDone(ConfigureResult result);
-
- SyncBackendHost* backend_;
- // Map of all data type controllers that are available for sync.
- // This list is determined at startup by various command line flags.
- const DataTypeController::TypeMap controllers_;
- State state_;
- DataTypeController* current_dtc_;
- std::map<syncable::ModelType, int> start_order_;
- TypeSet last_requested_types_;
- std::vector<DataTypeController*> needs_start_;
- std::vector<DataTypeController*> needs_stop_;
-
- ScopedRunnableMethodFactory<DataTypeManagerImpl2> method_factory_;
-
- DISALLOW_COPY_AND_ASSIGN(DataTypeManagerImpl2);
-};
-
-} // namespace browser_sync
-
-#endif // CHROME_BROWSER_SYNC_GLUE_DATA_TYPE_MANAGER_IMPL2_H__
diff --git a/chrome/browser/sync/glue/data_type_manager_impl2_unittest.cc b/chrome/browser/sync/glue/data_type_manager_impl2_unittest.cc
index 4f9f9b3..999579c 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl2_unittest.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl2_unittest.cc
@@ -2,12 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/sync/glue/data_type_manager_impl2.h"
+#include "chrome/browser/sync/glue/data_type_manager_impl.h"
#include <set>
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/stl_util-inl.h"
#include "base/task.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
@@ -25,24 +25,42 @@
#include "testing/gtest/include/gtest/gtest.h"
using browser_sync::DataTypeManager;
-using browser_sync::DataTypeManagerImpl2;
+using browser_sync::DataTypeManagerImpl;
using browser_sync::DataTypeController;
using browser_sync::DataTypeControllerMock;
using browser_sync::SyncBackendHostMock;
using testing::_;
+using testing::AtLeast;
using testing::DoAll;
using testing::DoDefault;
using testing::InSequence;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::InvokeWithoutArgs;
+using testing::Mock;
using testing::Property;
using testing::Pointee;
using testing::Return;
using testing::SaveArg;
ACTION_P(InvokeCallback, callback_result) {
- arg0->Run(callback_result);
+ arg0->Run(callback_result, FROM_HERE);
delete arg0;
}
+ACTION_P2(InvokeCallbackPointer, callback, argument) {
+ callback->Run(argument, FROM_HERE);
+ delete callback;
+}
+
+DataTypeManager::ConfigureResult GetResult(
+ const NotificationDetails& details) {
+ DataTypeManager::ConfigureResultWithErrorLocation* result_with_location =
+ Details<DataTypeManager::ConfigureResultWithErrorLocation>(
+ details).ptr();
+ return result_with_location->result;
+}
+
class DataTypeManagerImpl2Test : public testing::Test {
public:
DataTypeManagerImpl2Test()
@@ -79,10 +97,14 @@ class DataTypeManagerImpl2Test : public testing::Test {
DataTypeControllerMock* MakePasswordDTC() {
DataTypeControllerMock* dtc = new DataTypeControllerMock();
+ SetPasswordDTCExpectations(dtc);
+ return dtc;
+ }
+
+ void SetPasswordDTCExpectations(DataTypeControllerMock* dtc) {
EXPECT_CALL(*dtc, enabled()).WillRepeatedly(Return(true));
EXPECT_CALL(*dtc, type()).WillRepeatedly(Return(syncable::PASSWORDS));
EXPECT_CALL(*dtc, name()).WillRepeatedly(Return("passwords"));
- return dtc;
}
void SetStartStopExpectations(DataTypeControllerMock* mock_dtc) {
@@ -126,12 +148,12 @@ class DataTypeManagerImpl2Test : public testing::Test {
_, _));
}
+
void SetConfigureDoneExpectation(DataTypeManager::ConfigureResult result) {
EXPECT_CALL(
observer_,
Observe(NotificationType(NotificationType::SYNC_CONFIGURE_DONE), _,
- Property(&Details<DataTypeManager::ConfigureResult>::ptr,
- Pointee(result))));
+ ::testing::ResultOf(&GetResult, result)));
}
MessageLoopForUI message_loop_;
@@ -144,7 +166,7 @@ class DataTypeManagerImpl2Test : public testing::Test {
};
TEST_F(DataTypeManagerImpl2Test, NoControllers) {
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::OK);
dtm.Configure(types_);
@@ -158,7 +180,7 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOne) {
SetStartStopExpectations(bookmark_dtc);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
types_.insert(syncable::BOOKMARKS);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::OK);
@@ -174,7 +196,7 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOneStopWhileStarting) {
DataTypeController::MODEL_STARTING);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
types_.insert(syncable::BOOKMARKS);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::OK);
@@ -189,7 +211,7 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOneStopWhileAssociating) {
SetBusyStartStopExpectations(bookmark_dtc, DataTypeController::ASSOCIATING);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
types_.insert(syncable::BOOKMARKS);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::OK);
@@ -201,22 +223,37 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOneStopWhileAssociating) {
TEST_F(DataTypeManagerImpl2Test, OneWaitingForCrypto) {
DataTypeControllerMock* password_dtc = MakePasswordDTC();
- EXPECT_CALL(*password_dtc, state()).
+ EXPECT_CALL(*password_dtc, state()).Times(AtLeast(2)).
WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
EXPECT_CALL(*password_dtc, Start(_)).
WillOnce(InvokeCallback((DataTypeController::NEEDS_CRYPTO)));
- EXPECT_CALL(*password_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
controllers_[syncable::PASSWORDS] = password_dtc;
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
types_.insert(syncable::PASSWORDS);
SetConfigureStartExpectation();
+
+ dtm.Configure(types_);
+ EXPECT_EQ(DataTypeManager::BLOCKED, dtm.state());
+
+ Mock::VerifyAndClearExpectations(&backend_);
+ Mock::VerifyAndClearExpectations(&observer_);
+ Mock::VerifyAndClearExpectations(password_dtc);
+
SetConfigureDoneExpectation(DataTypeManager::OK);
+ SetPasswordDTCExpectations(password_dtc);
+ EXPECT_CALL(*password_dtc, state()).
+ WillOnce(Return(DataTypeController::NOT_RUNNING)).
+ WillRepeatedly(Return(DataTypeController::RUNNING));
+ EXPECT_CALL(*password_dtc, Start(_)).
+ WillOnce(InvokeCallback((DataTypeController::OK)));
+ EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
dtm.Configure(types_);
+
EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
+ EXPECT_CALL(*password_dtc, Stop()).Times(1);
dtm.Stop();
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
@@ -230,7 +267,7 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOneThenAnother) {
controllers_[syncable::PREFERENCES] = preference_dtc;
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(2);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
types_.insert(syncable::BOOKMARKS);
SetConfigureStartExpectation();
@@ -257,7 +294,7 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOneThenSwitch) {
controllers_[syncable::PREFERENCES] = preference_dtc;
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(2);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
types_.insert(syncable::BOOKMARKS);
SetConfigureStartExpectation();
@@ -276,6 +313,18 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureOneThenSwitch) {
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
+void DoConfigureDataTypes(
+ const DataTypeController::TypeMap& data_type_controllers,
+ const syncable::ModelTypeSet& types,
+ CancelableTask* ready_task) {
+ ready_task->Run();
+ delete ready_task;
+}
+
+void QuitMessageLoop() {
+ MessageLoop::current()->Quit();
+}
+
TEST_F(DataTypeManagerImpl2Test, ConfigureWhileOneInFlight) {
DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
// Save the callback here so we can interrupt startup.
@@ -298,8 +347,12 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureWhileOneInFlight) {
SetStartStopExpectations(preference_dtc);
controllers_[syncable::PREFERENCES] = preference_dtc;
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(2);
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
+ EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _))
+ .WillOnce(Invoke(DoConfigureDataTypes))
+ .WillOnce(DoAll(Invoke(DoConfigureDataTypes),
+ InvokeWithoutArgs(QuitMessageLoop)));
+
types_.insert(syncable::BOOKMARKS);
SetConfigureStartExpectation();
@@ -310,9 +363,11 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureWhileOneInFlight) {
// preferences and continue starting bookmarks.
types_.insert(syncable::PREFERENCES);
dtm.Configure(types_);
- callback->Run(DataTypeController::OK);
+ callback->Run(DataTypeController::OK, FROM_HERE);
delete callback;
+ MessageLoop::current()->Run();
+
EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
dtm.Stop();
@@ -328,7 +383,7 @@ TEST_F(DataTypeManagerImpl2Test, OneFailingController) {
WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::ASSOCIATION_FAILED);
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
@@ -348,12 +403,11 @@ TEST_F(DataTypeManagerImpl2Test, StopWhileInFlight) {
DataTypeController::StartCallback* callback;
EXPECT_CALL(*preference_dtc, Start(_)).
WillOnce(SaveArg<0>(&callback));
- EXPECT_CALL(*preference_dtc, Stop()).Times(1);
EXPECT_CALL(*preference_dtc, state()).
WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
controllers_[syncable::PREFERENCES] = preference_dtc;
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::ABORTED);
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
@@ -366,9 +420,9 @@ TEST_F(DataTypeManagerImpl2Test, StopWhileInFlight) {
EXPECT_EQ(DataTypeManager::CONFIGURING, dtm.state());
// Call stop before the preference callback is invoked.
+ EXPECT_CALL(*preference_dtc, Stop()).
+ WillOnce(InvokeCallbackPointer(callback, DataTypeController::ABORTED));
dtm.Stop();
- callback->Run(DataTypeController::ABORTED);
- delete callback;
EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
}
@@ -385,7 +439,7 @@ TEST_F(DataTypeManagerImpl2Test, SecondControllerFails) {
WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
controllers_[syncable::PREFERENCES] = preference_dtc;
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::ASSOCIATION_FAILED);
EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
@@ -405,7 +459,7 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureWhileDownloadPending) {
SetStartStopExpectations(preference_dtc);
controllers_[syncable::PREFERENCES] = preference_dtc;
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::OK);
CancelableTask* task;
@@ -424,14 +478,10 @@ TEST_F(DataTypeManagerImpl2Test, ConfigureWhileDownloadPending) {
types_.insert(syncable::PREFERENCES);
dtm.Configure(types_);
- // Should now be RESTARTING.
- EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
-
// Running the task will queue a restart task to the message loop, and
// eventually get us configured.
task->Run();
delete task;
- EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
MessageLoop::current()->RunAllPending();
EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
@@ -444,7 +494,7 @@ TEST_F(DataTypeManagerImpl2Test, StopWhileDownloadPending) {
SetNotUsedExpectations(bookmark_dtc);
controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeManagerImpl2 dtm(&backend_, controllers_);
+ DataTypeManagerImpl dtm(&backend_, controllers_);
SetConfigureStartExpectation();
SetConfigureDoneExpectation(DataTypeManager::ABORTED);
CancelableTask* task;
diff --git a/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc b/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc
index f0cab02..7a37f38 100644
--- a/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc
+++ b/chrome/browser/sync/glue/data_type_manager_impl_unittest.cc
@@ -1,668 +1,3 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
-
-#include <set>
-
-#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/stl_util-inl.h"
-#include "base/task.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/browser/sync/glue/data_type_controller_mock.h"
-#include "chrome/browser/sync/glue/data_type_manager_impl.h"
-#include "chrome/browser/sync/glue/sync_backend_host_mock.h"
-#include "chrome/browser/sync/profile_sync_test_util.h"
-#include "chrome/browser/sync/syncable/model_type.h"
-#include "content/browser/browser_thread.h"
-#include "content/common/notification_details.h"
-#include "content/common/notification_observer_mock.h"
-#include "content/common/notification_registrar.h"
-#include "content/common/notification_service.h"
-#include "content/common/notification_type.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-using browser_sync::DataTypeManager;
-using browser_sync::DataTypeManagerImpl;
-using browser_sync::DataTypeController;
-using browser_sync::DataTypeControllerMock;
-using browser_sync::SyncBackendHostMock;
-using testing::_;
-using testing::DoAll;
-using testing::DoDefault;
-using testing::InSequence;
-using testing::Property;
-using testing::Pointee;
-using testing::Return;
-using testing::SaveArg;
-
-ACTION_P(InvokeCallback, callback_result) {
- arg0->Run(callback_result);
- delete arg0;
-}
-
-class DataTypeManagerImplTest : public testing::Test {
- public:
- DataTypeManagerImplTest()
- : ui_thread_(BrowserThread::UI, &message_loop_) {}
-
- virtual ~DataTypeManagerImplTest() {
- }
-
- protected:
- virtual void SetUp() {
- registrar_.Add(&observer_,
- NotificationType::SYNC_CONFIGURE_START,
- NotificationService::AllSources());
- registrar_.Add(&observer_,
- NotificationType::SYNC_CONFIGURE_DONE,
- NotificationService::AllSources());
- }
-
- DataTypeControllerMock* MakeBookmarkDTC() {
- DataTypeControllerMock* dtc = new DataTypeControllerMock();
- EXPECT_CALL(*dtc, enabled()).WillRepeatedly(Return(true));
- EXPECT_CALL(*dtc, type()).WillRepeatedly(Return(syncable::BOOKMARKS));
- EXPECT_CALL(*dtc, name()).WillRepeatedly(Return("bookmark"));
- return dtc;
- }
-
- DataTypeControllerMock* MakePreferenceDTC() {
- DataTypeControllerMock* dtc = new DataTypeControllerMock();
- EXPECT_CALL(*dtc, enabled()).WillRepeatedly(Return(true));
- EXPECT_CALL(*dtc, type()).WillRepeatedly(Return(syncable::PREFERENCES));
- EXPECT_CALL(*dtc, name()).WillRepeatedly(Return("preference"));
- return dtc;
- }
-
- DataTypeControllerMock* MakePasswordDTC() {
- DataTypeControllerMock* dtc = new DataTypeControllerMock();
- EXPECT_CALL(*dtc, enabled()).WillRepeatedly(Return(true));
- EXPECT_CALL(*dtc, type()).WillRepeatedly(Return(syncable::PASSWORDS));
- EXPECT_CALL(*dtc, name()).WillRepeatedly(Return("passwords"));
- return dtc;
- }
-
- void SetStartStopExpectations(DataTypeControllerMock* mock_dtc) {
- InSequence seq;
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- EXPECT_CALL(*mock_dtc, Start(_)).
- WillOnce(InvokeCallback((DataTypeController::OK)));
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(DataTypeController::RUNNING));
- EXPECT_CALL(*mock_dtc, Stop()).Times(1);
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- }
-
- void SetBusyStartStopExpectations(DataTypeControllerMock* mock_dtc,
- DataTypeController::State busy_state) {
- InSequence seq;
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- EXPECT_CALL(*mock_dtc, Start(_)).
- WillOnce(InvokeCallback((DataTypeController::OK)));
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(busy_state));
- EXPECT_CALL(*mock_dtc, Stop()).Times(1);
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- }
-
- void SetNotUsedExpectations(DataTypeControllerMock* mock_dtc) {
- EXPECT_CALL(*mock_dtc, Start(_)).Times(0);
- EXPECT_CALL(*mock_dtc, Stop()).Times(0);
- EXPECT_CALL(*mock_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- }
-
- void SetConfigureStartExpectation() {
- EXPECT_CALL(
- observer_,
- Observe(NotificationType(NotificationType::SYNC_CONFIGURE_START),
- _, _));
- }
-
- void SetConfigureDoneExpectation(DataTypeManager::ConfigureResult result) {
- EXPECT_CALL(
- observer_,
- Observe(NotificationType(NotificationType::SYNC_CONFIGURE_DONE), _,
- Property(&Details<DataTypeManager::ConfigureResult>::ptr,
- Pointee(result))));
- }
-
- void SetBackendExpectations(int times) {
- EXPECT_CALL(backend_, UpdateEnabledTypes(_)).Times(times);
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(times);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(times);
- EXPECT_CALL(backend_, RequestPause()).Times(times);
- EXPECT_CALL(backend_, RequestResume()).Times(times);
- }
-
- MessageLoopForUI message_loop_;
- BrowserThread ui_thread_;
- DataTypeController::TypeMap controllers_;
- SyncBackendHostMock backend_;
- NotificationObserverMock observer_;
- NotificationRegistrar registrar_;
- std::set<syncable::ModelType> types_;
-};
-
-TEST_F(DataTypeManagerImplTest, NoControllers) {
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- EXPECT_CALL(backend_, UpdateEnabledTypes(_));
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureOne) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- SetBackendExpectations(1);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureOneStopWhileStarting) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetBusyStartStopExpectations(bookmark_dtc,
- DataTypeController::MODEL_STARTING);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- SetBackendExpectations(1);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureOneStopWhileAssociating) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetBusyStartStopExpectations(bookmark_dtc, DataTypeController::ASSOCIATING);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- SetBackendExpectations(1);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, OneWaitingForCrypto) {
- DataTypeControllerMock* password_dtc = MakePasswordDTC();
- EXPECT_CALL(*password_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- EXPECT_CALL(*password_dtc, Start(_)).
- WillOnce(InvokeCallback((DataTypeController::NEEDS_CRYPTO)));
- EXPECT_CALL(*password_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
-
- controllers_[syncable::PASSWORDS] = password_dtc;
- SetBackendExpectations(1);
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::PASSWORDS);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-
-TEST_F(DataTypeManagerImplTest, ConfigureOneThenAnother) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- SetStartStopExpectations(preference_dtc);
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- SetBackendExpectations(2);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
-
- types_.insert(syncable::PREFERENCES);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
-
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
-
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureOneThenSwitch) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- SetStartStopExpectations(preference_dtc);
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- SetBackendExpectations(2);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
-
- types_.clear();
- types_.insert(syncable::PREFERENCES);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
-
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureWhileOneInFlight) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- // Save the callback here so we can interrupt startup.
- DataTypeController::StartCallback* callback;
- {
- InSequence seq;
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- EXPECT_CALL(*bookmark_dtc, Start(_)).
- WillOnce(SaveArg<0>(&callback));
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::RUNNING));
- EXPECT_CALL(*bookmark_dtc, Stop()).Times(1);
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- }
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- SetStartStopExpectations(preference_dtc);
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- SetBackendExpectations(2);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
-
- // At this point, the bookmarks dtc should be in flight. Add
- // preferences and continue starting bookmarks.
- types_.insert(syncable::PREFERENCES);
- dtm.Configure(types_);
- callback->Run(DataTypeController::OK);
- delete callback;
-
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
-
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureWhilePausePending) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- SetStartStopExpectations(preference_dtc);
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- // Don't notify the first time pause is called.
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(2);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(2);
- EXPECT_CALL(backend_, RequestPause()).
- WillOnce(Return(true)).
- WillOnce(DoDefault());
- EXPECT_CALL(backend_, RequestResume()).Times(2);
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::PAUSE_PENDING, dtm.state());
-
- // Configure while pause pending.
- types_.insert(syncable::PREFERENCES);
- dtm.Configure(types_);
-
- // Should now be RESTARTING.
- EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
-
- // Send the SYNC_PAUSED notification. This will allow the DTM to
- // wake up and restart itself with the new configuration.
- NotificationService::current()->Notify(NotificationType::SYNC_PAUSED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, StopWhilePausePending) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetNotUsedExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- // Never notify when RequestPause is called.
- EXPECT_CALL(backend_, RequestPause()).WillOnce(Return(true));
- EXPECT_CALL(backend_, RequestResume()).Times(0);
- DataTypeManagerImpl dtm(&backend_, controllers_);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::ABORTED);
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::PAUSE_PENDING, dtm.state());
-
- // Stop while pause pending.
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-
- // We should be able to safely handle a SYNC_PAUSED notification.
- NotificationService::current()->Notify(NotificationType::SYNC_PAUSED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureWhileResumePending) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- SetStartStopExpectations(preference_dtc);
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(2);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(2);
- EXPECT_CALL(backend_, RequestPause()).Times(2);
- // Don't notify the first time resume is called.
- EXPECT_CALL(backend_, RequestResume()).
- WillOnce(Return(true)).
- WillOnce(DoDefault());
- DataTypeManagerImpl dtm(&backend_, controllers_);
- types_.insert(syncable::BOOKMARKS);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::RESUME_PENDING, dtm.state());
-
- // Configure while resume pending.
- types_.insert(syncable::PREFERENCES);
- dtm.Configure(types_);
-
- // Should now be RESTARTING.
- EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
-
- // Send the SYNC_PAUSED notification. This will allow the DTM to
- // wake up and restart itself with the new configuration.
- NotificationService::current()->Notify(NotificationType::SYNC_RESUMED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, StopWhileResumePending) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- EXPECT_CALL(backend_, RequestPause()).Times(1);
- // Never notify pause resumed.
- EXPECT_CALL(backend_, RequestResume()).WillOnce(Return(true));
- DataTypeManagerImpl dtm(&backend_, controllers_);
-
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::ABORTED);
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::RESUME_PENDING, dtm.state());
-
- // Stop while pause pending.
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-
- // We should be able to safely handle a SYNC_RESUMED notification.
- NotificationService::current()->Notify(NotificationType::SYNC_RESUMED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
-}
-
-TEST_F(DataTypeManagerImplTest, OneFailingController) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, Start(_)).
- WillOnce(InvokeCallback((DataTypeController::ASSOCIATION_FAILED)));
- EXPECT_CALL(*bookmark_dtc, Stop()).Times(0);
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::ASSOCIATION_FAILED);
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- EXPECT_CALL(backend_, RequestPause()).Times(1);
- EXPECT_CALL(backend_, RequestResume()).Times(0);
-
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, StopWhileInFlight) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- // Save the callback here so we can interrupt startup.
- DataTypeController::StartCallback* callback;
- EXPECT_CALL(*preference_dtc, Start(_)).
- WillOnce(SaveArg<0>(&callback));
- EXPECT_CALL(*preference_dtc, Stop()).Times(1);
- EXPECT_CALL(*preference_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::ABORTED);
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- EXPECT_CALL(backend_, RequestPause()).Times(1);
- EXPECT_CALL(backend_, RequestResume()).Times(0);
-
- types_.insert(syncable::BOOKMARKS);
- types_.insert(syncable::PREFERENCES);
- dtm.Configure(types_);
- // Configure should stop in the CONFIGURING state because we are
- // waiting for the preferences callback to be invoked.
- EXPECT_EQ(DataTypeManager::CONFIGURING, dtm.state());
-
- // Call stop before the preference callback is invoked.
- dtm.Stop();
- callback->Run(DataTypeController::ABORTED);
- delete callback;
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, SecondControllerFails) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- EXPECT_CALL(*preference_dtc, Start(_)).
- WillOnce(InvokeCallback((DataTypeController::ASSOCIATION_FAILED)));
- EXPECT_CALL(*preference_dtc, Stop()).Times(0);
- EXPECT_CALL(*preference_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::ASSOCIATION_FAILED);
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- EXPECT_CALL(backend_, RequestPause()).Times(1);
- EXPECT_CALL(backend_, RequestResume()).Times(0);
-
- types_.insert(syncable::BOOKMARKS);
- types_.insert(syncable::PREFERENCES);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, PauseFailed) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- EXPECT_CALL(*bookmark_dtc, Start(_)).Times(0);
- EXPECT_CALL(*bookmark_dtc, state()).
- WillRepeatedly(Return(DataTypeController::NOT_RUNNING));
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::UNRECOVERABLE_ERROR);
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- EXPECT_CALL(backend_, RequestPause()).WillOnce(Return(false));
- EXPECT_CALL(backend_, RequestResume()).Times(0);
-
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ResumeFailed) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::UNRECOVERABLE_ERROR);
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).Times(1);
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(1);
- EXPECT_CALL(backend_, RequestPause()).Times(1);
- EXPECT_CALL(backend_, RequestResume()).WillOnce(Return(false));
-
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, ConfigureWhileDownloadPending) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetStartStopExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeControllerMock* preference_dtc = MakePreferenceDTC();
- SetStartStopExpectations(preference_dtc);
- controllers_[syncable::PREFERENCES] = preference_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::OK);
- CancelableTask* task;
- // Grab the task the first time this is called so we can configure
- // before it is finished.
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).
- WillOnce(SaveArg<2>(&task)).
- WillOnce(DoDefault());
- EXPECT_CALL(backend_, StartSyncingWithServer()).Times(2);
- EXPECT_CALL(backend_, RequestPause()).Times(1);
- EXPECT_CALL(backend_, RequestResume()).Times(1);
-
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- // Configure should stop in the DOWNLOAD_PENDING state because we
- // are waiting for the download ready task to be run.
- EXPECT_EQ(DataTypeManager::DOWNLOAD_PENDING, dtm.state());
-
- types_.insert(syncable::PREFERENCES);
- dtm.Configure(types_);
-
- // Should now be RESTARTING.
- EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
-
- // Running the task will queue a restart task to the message loop, and
- // eventually get us configured.
- task->Run();
- delete task;
- EXPECT_EQ(DataTypeManager::RESTARTING, dtm.state());
- MessageLoop::current()->RunAllPending();
- EXPECT_EQ(DataTypeManager::CONFIGURED, dtm.state());
-
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-}
-
-TEST_F(DataTypeManagerImplTest, StopWhileDownloadPending) {
- DataTypeControllerMock* bookmark_dtc = MakeBookmarkDTC();
- SetNotUsedExpectations(bookmark_dtc);
- controllers_[syncable::BOOKMARKS] = bookmark_dtc;
-
- DataTypeManagerImpl dtm(&backend_, controllers_);
- SetConfigureStartExpectation();
- SetConfigureDoneExpectation(DataTypeManager::ABORTED);
- CancelableTask* task;
- // Grab the task the first time this is called so we can stop
- // before it is finished.
- EXPECT_CALL(backend_, ConfigureDataTypes(_, _, _)).
- WillOnce(SaveArg<2>(&task));
- EXPECT_CALL(backend_, StartSyncingWithServer());
- EXPECT_CALL(backend_, RequestPause()).Times(0);
- EXPECT_CALL(backend_, RequestResume()).Times(0);
-
- types_.insert(syncable::BOOKMARKS);
- dtm.Configure(types_);
- // Configure should stop in the DOWNLOAD_PENDING state because we
- // are waiting for the download ready task to be run.
- EXPECT_EQ(DataTypeManager::DOWNLOAD_PENDING, dtm.state());
-
- dtm.Stop();
- EXPECT_EQ(DataTypeManager::STOPPED, dtm.state());
-
- // It should be perfectly safe to run this task even though the DTM
- // has been stopped.
- task->Run();
- delete task;
-}
diff --git a/chrome/browser/sync/glue/data_type_manager_mock.cc b/chrome/browser/sync/glue/data_type_manager_mock.cc
index 9cc445e..0afef59 100644
--- a/chrome/browser/sync/glue/data_type_manager_mock.cc
+++ b/chrome/browser/sync/glue/data_type_manager_mock.cc
@@ -2,12 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/tracked.h"
#include "chrome/browser/sync/glue/data_type_manager_mock.h"
namespace browser_sync {
DataTypeManagerMock::DataTypeManagerMock()
- : result_(OK) {
+ : result_(OK, FROM_HERE, syncable::ModelTypeSet()) {
+
// By default, calling Configure will send a SYNC_CONFIGURE_START
// and SYNC_CONFIGURE_DONE notification with a DataTypeManager::OK
// detail.
diff --git a/chrome/browser/sync/glue/data_type_manager_mock.h b/chrome/browser/sync/glue/data_type_manager_mock.h
index 05a3665..eba4d61 100644
--- a/chrome/browser/sync/glue/data_type_manager_mock.h
+++ b/chrome/browser/sync/glue/data_type_manager_mock.h
@@ -8,16 +8,17 @@
#include "chrome/browser/sync/glue/data_type_manager.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
ACTION_P3(NotifyFromDataTypeManagerWithResult, dtm, type, result) {
NotificationService::current()->Notify(
type,
Source<browser_sync::DataTypeManager>(dtm),
- Details<browser_sync::DataTypeManager::ConfigureResult>(result));
+ Details<browser_sync::DataTypeManager::ConfigureResultWithErrorLocation>(
+ result));
}
ACTION_P2(NotifyFromDataTypeManager, dtm, type) {
@@ -39,7 +40,7 @@ class DataTypeManagerMock : public DataTypeManager {
MOCK_METHOD0(state, State());
private:
- DataTypeManager::ConfigureResult result_;
+ browser_sync::DataTypeManager::ConfigureResultWithErrorLocation result_;
};
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/database_model_worker_unittest.cc b/chrome/browser/sync/glue/database_model_worker_unittest.cc
index 0a70ef6..40fc978 100644
--- a/chrome/browser/sync/glue/database_model_worker_unittest.cc
+++ b/chrome/browser/sync/glue/database_model_worker_unittest.cc
@@ -1,10 +1,10 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/threading/thread.h"
#include "base/timer.h"
#include "chrome/browser/sync/glue/database_model_worker.h"
diff --git a/chrome/browser/sync/glue/do_optimistic_refresh_task.h b/chrome/browser/sync/glue/do_optimistic_refresh_task.h
index f98d436..31cdebc 100644
--- a/chrome/browser/sync/glue/do_optimistic_refresh_task.h
+++ b/chrome/browser/sync/glue/do_optimistic_refresh_task.h
@@ -1,11 +1,11 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#ifndef CHROME_BROWSER_SYNC_GLUE_DO_OPTIMISTIC_REFRESH_TASK_H_
#define CHROME_BROWSER_SYNC_GLUE_DO_OPTIMISTIC_REFRESH_TASK_H_
#pragma once
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "chrome/browser/autofill/personal_data_manager.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/glue/extension_change_processor.cc b/chrome/browser/sync/glue/extension_change_processor.cc
index 7cf1a2e..dd0da34 100644
--- a/chrome/browser/sync/glue/extension_change_processor.cc
+++ b/chrome/browser/sync/glue/extension_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,15 +9,17 @@
#include "base/logging.h"
#include "base/stl_util-inl.h"
-#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
+#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/extension_sync.h"
#include "chrome/browser/sync/glue/extension_util.h"
+#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/common/extensions/extension.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_source.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
namespace browser_sync {
@@ -26,7 +28,9 @@ ExtensionChangeProcessor::ExtensionChangeProcessor(
UnrecoverableErrorHandler* error_handler)
: ChangeProcessor(error_handler),
traits_(traits),
- profile_(NULL) {
+ profile_(NULL),
+ extension_service_(NULL),
+ user_share_(NULL) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(error_handler);
}
@@ -65,7 +69,7 @@ void ExtensionChangeProcessor::Observe(NotificationType type,
const std::string& id = uninstalled_extension_info->extension_id;
VLOG(1) << "Removing server data for uninstalled extension " << id
<< " of type " << uninstalled_extension_info->extension_type;
- RemoveServerData(traits_, id, profile_->GetProfileSyncService());
+ RemoveServerData(traits_, id, user_share_);
}
} else {
const Extension* extension = NULL;
@@ -81,8 +85,8 @@ void ExtensionChangeProcessor::Observe(NotificationType type,
return;
}
std::string error;
- if (!UpdateServerData(traits_, *extension,
- profile_->GetProfileSyncService(), &error)) {
+ if (!UpdateServerData(traits_, *extension, *extension_service_,
+ user_share_, &error)) {
error_handler()->OnUnrecoverableError(FROM_HERE, error);
}
}
@@ -96,10 +100,9 @@ void ExtensionChangeProcessor::ApplyChangesFromSyncModel(
if (!running()) {
return;
}
- ExtensionService* extensions_service =
- GetExtensionServiceFromProfile(profile_);
for (int i = 0; i < change_count; ++i) {
const sync_api::SyncManager::ChangeRecord& change = changes[i];
+ sync_pb::ExtensionSpecifics specifics;
switch (change.action) {
case sync_api::SyncManager::ChangeRecord::ACTION_ADD:
case sync_api::SyncManager::ChangeRecord::ACTION_UPDATE: {
@@ -112,30 +115,14 @@ void ExtensionChangeProcessor::ApplyChangesFromSyncModel(
return;
}
DCHECK_EQ(node.GetModelType(), traits_.model_type);
- const sync_pb::ExtensionSpecifics& specifics =
- (*traits_.extension_specifics_getter)(node);
- if (!IsExtensionSpecificsValid(specifics)) {
- std::string error =
- std::string("Invalid server specifics: ") +
- ExtensionSpecificsToString(specifics);
- error_handler()->OnUnrecoverableError(FROM_HERE, error);
- return;
- }
- StopObserving();
- UpdateClient(traits_, specifics, extensions_service);
- StartObserving();
+ specifics = (*traits_.extension_specifics_getter)(node);
break;
}
case sync_api::SyncManager::ChangeRecord::ACTION_DELETE: {
- sync_pb::ExtensionSpecifics specifics;
- if ((*traits_.extension_specifics_entity_getter)(
+ if (!(*traits_.extension_specifics_entity_getter)(
change.specifics, &specifics)) {
- StopObserving();
- RemoveFromClient(traits_, specifics.id(), extensions_service);
- StartObserving();
- } else {
std::stringstream error;
- error << "Could not get extension ID for deleted node "
+ error << "Could not get extension specifics from deleted node "
<< change.id;
error_handler()->OnUnrecoverableError(FROM_HERE, error.str());
LOG(DFATAL) << error.str();
@@ -143,13 +130,32 @@ void ExtensionChangeProcessor::ApplyChangesFromSyncModel(
break;
}
}
+ ExtensionSyncData sync_data;
+ if (!GetExtensionSyncData(specifics, &sync_data)) {
+ // TODO(akalin): Should probably recover or drop.
+ std::string error =
+ std::string("Invalid server specifics: ") +
+ ExtensionSpecificsToString(specifics);
+ error_handler()->OnUnrecoverableError(FROM_HERE, error);
+ return;
+ }
+ sync_data.uninstalled =
+ (change.action == sync_api::SyncManager::ChangeRecord::ACTION_DELETE);
+ StopObserving();
+ extension_service_->ProcessSyncData(sync_data,
+ traits_.is_valid_and_syncable);
+ StartObserving();
}
}
void ExtensionChangeProcessor::StartImpl(Profile* profile) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(profile);
profile_ = profile;
+ extension_service_ = profile_->GetExtensionService();
+ user_share_ = profile_->GetProfileSyncService()->GetUserShare();
+ DCHECK(profile_);
+ DCHECK(extension_service_);
+ DCHECK(user_share_);
StartObserving();
}
@@ -157,6 +163,8 @@ void ExtensionChangeProcessor::StopImpl() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
StopObserving();
profile_ = NULL;
+ extension_service_ = NULL;
+ user_share_ = NULL;
}
void ExtensionChangeProcessor::StartObserving() {
diff --git a/chrome/browser/sync/glue/extension_change_processor.h b/chrome/browser/sync/glue/extension_change_processor.h
index 4d50f8c..cf75a2c 100644
--- a/chrome/browser/sync/glue/extension_change_processor.h
+++ b/chrome/browser/sync/glue/extension_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,14 +10,11 @@
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/extension_sync_traits.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_type.h"
-#include "chrome/common/notification_registrar.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
-class ExtensionService;
-class NotificationDetails;
-class NotificationSource;
-class Profile;
+class ExtensionServiceInterface;
namespace browser_sync {
@@ -60,8 +57,11 @@ class ExtensionChangeProcessor : public ChangeProcessor,
NotificationRegistrar notification_registrar_;
const ExtensionSyncTraits traits_;
- // Owner of the ExtensionService. Non-NULL iff |running()| is true.
+
+ // Non-NULL iff |running()| is true.
Profile* profile_;
+ ExtensionServiceInterface* extension_service_;
+ sync_api::UserShare* user_share_;
DISALLOW_COPY_AND_ASSIGN(ExtensionChangeProcessor);
};
diff --git a/chrome/browser/sync/glue/extension_data_type_controller.cc b/chrome/browser/sync/glue/extension_data_type_controller.cc
index 47b9427..07d4d1c 100644
--- a/chrome/browser/sync/glue/extension_data_type_controller.cc
+++ b/chrome/browser/sync/glue/extension_data_type_controller.cc
@@ -1,17 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/extension_data_type_controller.h"
-#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/time.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/browser/sync/unrecoverable_error_handler.h"
-#include "content/browser/browser_thread.h"
namespace browser_sync {
@@ -19,110 +14,42 @@ ExtensionDataTypeController::ExtensionDataTypeController(
ProfileSyncFactory* profile_sync_factory,
Profile* profile,
ProfileSyncService* sync_service)
- : profile_sync_factory_(profile_sync_factory),
- profile_(profile),
- sync_service_(sync_service),
- state_(NOT_RUNNING) {
- DCHECK(profile_sync_factory);
- DCHECK(sync_service);
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
}
ExtensionDataTypeController::~ExtensionDataTypeController() {
}
-void ExtensionDataTypeController::Start(StartCallback* start_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(start_callback);
- if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
- delete start_callback;
- return;
- }
+syncable::ModelType ExtensionDataTypeController::type() const {
+ return syncable::EXTENSIONS;
+}
- start_callback_.reset(start_callback);
+bool ExtensionDataTypeController::StartModels() {
+ profile_->InitExtensions(true);
+ return true;
+}
- profile_->InitExtensions();
+void ExtensionDataTypeController::CreateSyncComponents() {
ProfileSyncFactory::SyncComponents sync_components =
profile_sync_factory_->CreateExtensionSyncComponents(sync_service_,
- this);
+ this);
model_associator_.reset(sync_components.model_associator);
change_processor_.reset(sync_components.change_processor);
-
- bool sync_has_nodes = false;
- if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
- StartFailed(UNRECOVERABLE_ERROR);
- return;
- }
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- bool merge_success = model_associator_->AssociateModels();
- UMA_HISTOGRAM_TIMES("Sync.ExtensionAssociationTime",
- base::TimeTicks::Now() - start_time);
- if (!merge_success) {
- StartFailed(ASSOCIATION_FAILED);
- return;
- }
-
- sync_service_->ActivateDataType(this, change_processor_.get());
- state_ = RUNNING;
- FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK);
-}
-
-void ExtensionDataTypeController::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (change_processor_ != NULL)
- sync_service_->DeactivateDataType(this, change_processor_.get());
-
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
- change_processor_.reset();
- model_associator_.reset();
- start_callback_.reset();
-
- state_ = NOT_RUNNING;
-}
-
-bool ExtensionDataTypeController::enabled() {
- return true;
-}
-
-syncable::ModelType ExtensionDataTypeController::type() {
- return syncable::EXTENSIONS;
-}
-
-browser_sync::ModelSafeGroup ExtensionDataTypeController::model_safe_group() {
- return browser_sync::GROUP_UI;
-}
-
-const char* ExtensionDataTypeController::name() const {
- // For logging only.
- return "extension";
-}
-
-DataTypeController::State ExtensionDataTypeController::state() {
- return state_;
}
-void ExtensionDataTypeController::OnUnrecoverableError(
+void ExtensionDataTypeController::RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
UMA_HISTOGRAM_COUNTS("Sync.ExtensionRunFailures", 1);
- sync_service_->OnUnrecoverableError(from_here, message);
}
-void ExtensionDataTypeController::FinishStart(StartResult result) {
- start_callback_->Run(result);
- start_callback_.reset();
+void ExtensionDataTypeController::RecordAssociationTime(base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.ExtensionAssociationTime", time);
}
-void ExtensionDataTypeController::StartFailed(StartResult result) {
- model_associator_.reset();
- change_processor_.reset();
- start_callback_->Run(result);
- start_callback_.reset();
+void ExtensionDataTypeController::RecordStartFailure(StartResult result) {
UMA_HISTOGRAM_ENUMERATION("Sync.ExtensionStartFailures",
result,
MAX_START_RESULT);
diff --git a/chrome/browser/sync/glue/extension_data_type_controller.h b/chrome/browser/sync/glue/extension_data_type_controller.h
index 0c031df..761db08 100644
--- a/chrome/browser/sync/glue/extension_data_type_controller.h
+++ b/chrome/browser/sync/glue/extension_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,20 +8,11 @@
#include <string>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-
-class Profile;
-class ProfileSyncFactory;
-class ProfileSyncService;
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
namespace browser_sync {
-class AssociatorInterface;
-class ChangeProcessor;
-
-class ExtensionDataTypeController : public DataTypeController {
+class ExtensionDataTypeController : public FrontendDataTypeController {
public:
ExtensionDataTypeController(
ProfileSyncFactory* profile_sync_factory,
@@ -30,41 +21,17 @@ class ExtensionDataTypeController : public DataTypeController {
virtual ~ExtensionDataTypeController();
// DataTypeController implementation.
- virtual void Start(StartCallback* start_callback);
-
- virtual void Stop();
-
- virtual bool enabled();
-
- virtual syncable::ModelType type();
-
- virtual browser_sync::ModelSafeGroup model_safe_group();
-
- virtual const char* name() const;
-
- virtual State state();
+ virtual syncable::ModelType type() const;
- // UnrecoverableErrorHandler interface.
- virtual void OnUnrecoverableError(
+ private:
+ // DataTypeController implementations.
+ virtual bool StartModels();
+ virtual void CreateSyncComponents();
+ virtual void RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message);
-
- private:
- // Helper method to run the stashed start callback with a given result.
- void FinishStart(StartResult result);
-
- // Cleans up state and calls callback when start fails.
- void StartFailed(StartResult result);
-
- ProfileSyncFactory* profile_sync_factory_;
- Profile* profile_;
- ProfileSyncService* sync_service_;
-
- State state_;
-
- scoped_ptr<StartCallback> start_callback_;
- scoped_ptr<AssociatorInterface> model_associator_;
- scoped_ptr<ChangeProcessor> change_processor_;
+ virtual void RecordAssociationTime(base::TimeDelta time);
+ virtual void RecordStartFailure(StartResult result);
DISALLOW_COPY_AND_ASSIGN(ExtensionDataTypeController);
};
diff --git a/chrome/browser/sync/glue/extension_data_type_controller_unittest.cc b/chrome/browser/sync/glue/extension_data_type_controller_unittest.cc
index 29220b1..4a6953c 100644
--- a/chrome/browser/sync/glue/extension_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/extension_data_type_controller_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/sync/glue/extension_data_type_controller.h"
#include "chrome/browser/sync/glue/change_processor_mock.h"
@@ -28,7 +28,8 @@ using testing::SetArgumentPointee;
class StartCallback {
public:
- MOCK_METHOD1(Run, void(DataTypeController::StartResult result));
+ MOCK_METHOD2(Run, void(DataTypeController::StartResult result,
+ const tracked_objects::Location& location));
};
class ExtensionDataTypeControllerTest : public testing::Test {
@@ -54,6 +55,8 @@ class ExtensionDataTypeControllerTest : public testing::Test {
}
void SetAssociateExpectations() {
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
EXPECT_CALL(*model_associator_, AssociateModels()).
@@ -85,7 +88,7 @@ TEST_F(ExtensionDataTypeControllerTest, Start) {
SetAssociateExpectations();
SetActivateExpectations();
EXPECT_EQ(DataTypeController::NOT_RUNNING, extension_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, extension_dtc_->state());
}
@@ -96,7 +99,7 @@ TEST_F(ExtensionDataTypeControllerTest, StartFirstRun) {
SetActivateExpectations();
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -107,7 +110,7 @@ TEST_F(ExtensionDataTypeControllerTest, StartOk) {
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -117,7 +120,7 @@ TEST_F(ExtensionDataTypeControllerTest, StartAssociationFailed) {
EXPECT_CALL(*model_associator_, AssociateModels()).
WillRepeatedly(Return(false));
- EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, extension_dtc_->state());
}
@@ -126,9 +129,11 @@ TEST_F(ExtensionDataTypeControllerTest,
StartAssociationTriggersUnrecoverableError) {
SetStartExpectations();
// Set up association to fail with an unrecoverable error.
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, extension_dtc_->state());
}
@@ -141,7 +146,7 @@ TEST_F(ExtensionDataTypeControllerTest, Stop) {
EXPECT_EQ(DataTypeController::NOT_RUNNING, extension_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, extension_dtc_->state());
extension_dtc_->Stop();
@@ -160,7 +165,7 @@ TEST_F(ExtensionDataTypeControllerTest, OnUnrecoverableError) {
&ExtensionDataTypeController::Stop));
SetStopExpectations();
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
extension_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
// This should cause extension_dtc_->Stop() to be called.
extension_dtc_->OnUnrecoverableError(FROM_HERE, "Test");
diff --git a/chrome/browser/sync/glue/extension_model_associator.cc b/chrome/browser/sync/glue/extension_model_associator.cc
index c381705..383d819 100644
--- a/chrome/browser/sync/glue/extension_model_associator.cc
+++ b/chrome/browser/sync/glue/extension_model_associator.cc
@@ -1,23 +1,30 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/extension_model_associator.h"
#include "base/logging.h"
+#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/extension_data.h"
#include "chrome/browser/sync/glue/extension_sync_traits.h"
#include "chrome/browser/sync/glue/extension_sync.h"
+#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
+#include "chrome/browser/sync/syncable/nigori_util.h"
#include "content/browser/browser_thread.h"
namespace browser_sync {
ExtensionModelAssociator::ExtensionModelAssociator(
- const ExtensionSyncTraits& traits, ProfileSyncService* sync_service)
- : traits_(traits), sync_service_(sync_service) {
+ const ExtensionSyncTraits& traits,
+ ExtensionServiceInterface* extension_service,
+ sync_api::UserShare* user_share)
+ : traits_(traits), extension_service_(extension_service),
+ user_share_(user_share) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(sync_service_);
+ DCHECK(extension_service_);
+ DCHECK(user_share_);
}
ExtensionModelAssociator::~ExtensionModelAssociator() {
@@ -27,10 +34,12 @@ ExtensionModelAssociator::~ExtensionModelAssociator() {
bool ExtensionModelAssociator::AssociateModels() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
ExtensionDataMap extension_data_map;
- if (!SlurpExtensionData(traits_, sync_service_, &extension_data_map)) {
+ if (!SlurpExtensionData(
+ traits_, *extension_service_, user_share_, &extension_data_map)) {
return false;
}
- if (!FlushExtensionData(traits_, extension_data_map, sync_service_)) {
+ if (!FlushExtensionData(
+ traits_, extension_data_map, extension_service_, user_share_)) {
return false;
}
@@ -45,7 +54,16 @@ bool ExtensionModelAssociator::DisassociateModels() {
bool ExtensionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- return RootNodeHasChildren(traits_.root_node_tag, sync_service_, has_nodes);
+ return RootNodeHasChildren(traits_.root_node_tag, user_share_, has_nodes);
+}
+
+bool ExtensionModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(user_share_);
+ const syncable::ModelTypeSet& encrypted_types =
+ GetEncryptedDataTypes(trans.GetWrappedTrans());
+ return encrypted_types.count(traits_.model_type) == 0 ||
+ trans.GetCryptographer()->is_ready();
}
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extension_model_associator.h b/chrome/browser/sync/glue/extension_model_associator.h
index 0063d2d..ab4eb9c 100644
--- a/chrome/browser/sync/glue/extension_model_associator.h
+++ b/chrome/browser/sync/glue/extension_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,7 +11,11 @@
#include "chrome/browser/sync/glue/model_associator.h"
#include "chrome/browser/sync/syncable/model_type.h"
-class ProfileSyncService;
+class ExtensionServiceInterface;
+
+namespace sync_api {
+struct UserShare;
+} // namespace sync_api
namespace browser_sync {
@@ -21,7 +25,8 @@ class ExtensionModelAssociator : public AssociatorInterface {
public:
// Does not take ownership of sync_service.
ExtensionModelAssociator(const ExtensionSyncTraits& traits,
- ProfileSyncService* sync_service);
+ ExtensionServiceInterface* extension_service,
+ sync_api::UserShare* user_share);
virtual ~ExtensionModelAssociator();
// Used by profile_sync_test_util.h.
@@ -35,11 +40,11 @@ class ExtensionModelAssociator : public AssociatorInterface {
// No implementation needed, this associator runs on the main
// thread.
}
-
+ virtual bool CryptoReadyIfNecessary();
private:
const ExtensionSyncTraits traits_;
- // Weak pointer. Always non-NULL.
- ProfileSyncService* sync_service_;
+ ExtensionServiceInterface* const extension_service_;
+ sync_api::UserShare* const user_share_;
DISALLOW_COPY_AND_ASSIGN(ExtensionModelAssociator);
};
diff --git a/chrome/browser/sync/glue/extension_sync.cc b/chrome/browser/sync/glue/extension_sync.cc
index 3933515..1aedc4a 100644
--- a/chrome/browser/sync/glue/extension_sync.cc
+++ b/chrome/browser/sync/glue/extension_sync.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,6 +9,7 @@
#include "base/logging.h"
#include "chrome/browser/extensions/extension_updater.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/extension_data.h"
@@ -19,11 +20,11 @@
namespace browser_sync {
bool RootNodeHasChildren(const char* tag,
- ProfileSyncService* sync_service,
+ sync_api::UserShare* user_share,
bool* has_children) {
CHECK(has_children);
*has_children = false;
- sync_api::ReadTransaction trans(sync_service->GetUserShare());
+ sync_api::ReadTransaction trans(user_share);
sync_api::ReadNode node(&trans);
if (!node.InitByTagLookup(tag)) {
LOG(ERROR) << "Root node with tag " << tag << " does not exist";
@@ -33,22 +34,8 @@ bool RootNodeHasChildren(const char* tag,
return true;
}
-ExtensionService* GetExtensionServiceFromProfile(
- Profile* profile) {
- CHECK(profile);
- ExtensionService* extensions_service = profile->GetExtensionService();
- CHECK(extensions_service);
- return extensions_service;
-}
-
namespace {
-ExtensionService* GetExtensionServiceFromProfileSyncService(
- ProfileSyncService* sync_service) {
- CHECK(sync_service);
- return GetExtensionServiceFromProfile(sync_service->profile());
-}
-
// Updates the value in |extension_data_map| from the given data,
// creating an entry if necessary. Returns a pointer to the
// updated/created ExtensionData object.
@@ -80,7 +67,7 @@ ExtensionData* SetOrCreateExtensionData(
void ReadClientDataFromExtensionList(
const ExtensionList& extensions,
IsValidAndSyncablePredicate is_valid_and_syncable,
- ExtensionService* extensions_service,
+ const ExtensionServiceInterface& extensions_service,
std::set<std::string>* unsynced_extensions,
ExtensionDataMap* extension_data_map) {
for (ExtensionList::const_iterator it = extensions.begin();
@@ -89,7 +76,7 @@ void ReadClientDataFromExtensionList(
const Extension& extension = **it;
if (is_valid_and_syncable(extension)) {
sync_pb::ExtensionSpecifics client_specifics;
- GetExtensionSpecifics(extension, extensions_service->extension_prefs(),
+ GetExtensionSpecifics(extension, extensions_service,
&client_specifics);
DcheckIsExtensionSpecificsValid(client_specifics);
const ExtensionData& extension_data =
@@ -110,17 +97,17 @@ void ReadClientDataFromExtensionList(
// enabled and disabled extensions from |extensions_service|.
void SlurpClientData(
IsValidAndSyncablePredicate is_valid_and_syncable,
- ExtensionService* extensions_service,
+ const ExtensionServiceInterface& extensions_service,
std::set<std::string>* unsynced_extensions,
ExtensionDataMap* extension_data_map) {
- const ExtensionList* extensions = extensions_service->extensions();
+ const ExtensionList* extensions = extensions_service.extensions();
CHECK(extensions);
ReadClientDataFromExtensionList(
*extensions, is_valid_and_syncable, extensions_service,
unsynced_extensions, extension_data_map);
const ExtensionList* disabled_extensions =
- extensions_service->disabled_extensions();
+ extensions_service.disabled_extensions();
CHECK(disabled_extensions);
ReadClientDataFromExtensionList(
*disabled_extensions, is_valid_and_syncable, extensions_service,
@@ -145,9 +132,9 @@ bool SlurpServerData(
const char* root_node_tag,
const ExtensionSpecificsGetter extension_specifics_getter,
const std::set<std::string>& unsynced_extensions,
- ProfileSyncService* sync_service,
+ sync_api::UserShare* user_share,
ExtensionDataMap* extension_data_map) {
- sync_api::WriteTransaction trans(sync_service->GetUserShare());
+ sync_api::WriteTransaction trans(user_share);
sync_api::ReadNode root(&trans);
if (!root.InitByTagLookup(root_node_tag)) {
LOG(ERROR) << GetRootNodeDoesNotExistError(root_node_tag);
@@ -188,10 +175,9 @@ bool SlurpServerData(
} // namespace
bool SlurpExtensionData(const ExtensionSyncTraits& traits,
- ProfileSyncService* sync_service,
+ const ExtensionServiceInterface& extensions_service,
+ sync_api::UserShare* user_share,
ExtensionDataMap* extension_data_map) {
- ExtensionService* extensions_service =
- GetExtensionServiceFromProfileSyncService(sync_service);
std::set<std::string> unsynced_extensions;
// Read client-side data first so server data takes precedence, and
@@ -202,7 +188,7 @@ bool SlurpExtensionData(const ExtensionSyncTraits& traits,
if (!SlurpServerData(
traits.root_node_tag, traits.extension_specifics_getter,
- unsynced_extensions, sync_service, extension_data_map)) {
+ unsynced_extensions, user_share, extension_data_map)) {
return false;
}
return true;
@@ -249,91 +235,20 @@ bool UpdateServer(
return true;
}
-// Tries to update the client data from the given extension data.
-// extension_data->ServerNeedsUpdate() must not hold and
-// extension_data->ClientNeedsUpdate() must hold before this function
-// is called. If the update was successful,
-// extension_data->ClientNeedsUpdate() will be false after this
-// function is called. Otherwise, the extension needs updating to a
-// new version.
-void TryUpdateClient(
- IsValidAndSyncablePredicate is_valid_and_syncable,
- ExtensionService* extensions_service,
- ExtensionData* extension_data) {
- DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
- DCHECK(extension_data->NeedsUpdate(ExtensionData::CLIENT));
- const sync_pb::ExtensionSpecifics& specifics =
- extension_data->merged_data();
- DcheckIsExtensionSpecificsValid(specifics);
- const std::string& id = specifics.id();
- const Extension* extension = extensions_service->GetExtensionById(id, true);
- if (extension) {
- if (!is_valid_and_syncable(*extension)) {
- LOG(DFATAL) << "TryUpdateClient() called for non-syncable extension "
- << extension->id();
- return;
- }
- SetExtensionProperties(specifics, extensions_service, extension);
- {
- sync_pb::ExtensionSpecifics extension_specifics;
- GetExtensionSpecifics(*extension, extensions_service->extension_prefs(),
- &extension_specifics);
- DCHECK(AreExtensionSpecificsUserPropertiesEqual(
- specifics, extension_specifics))
- << ExtensionSpecificsToString(specifics) << ", "
- << ExtensionSpecificsToString(extension_specifics);
- }
- if (!IsExtensionOutdated(*extension, specifics)) {
- extension_data->ResolveData(ExtensionData::CLIENT);
- DCHECK(!extension_data->NeedsUpdate(ExtensionData::CLIENT));
- }
- } else {
- GURL update_url(specifics.update_url());
- // TODO(akalin): Replace silent update with a list of enabled
- // permissions.
- extensions_service->AddPendingExtensionFromSync(
- id, update_url,
- is_valid_and_syncable,
- true, // install_silently
- specifics.enabled(),
- specifics.incognito_enabled());
- }
- DCHECK(!extension_data->NeedsUpdate(ExtensionData::SERVER));
-}
-
-// Kick off a run of the extension updater.
-//
-// TODO(akalin): Combine this with the similar function in
-// theme_util.cc.
-void NudgeExtensionUpdater(ExtensionService* extensions_service) {
- ExtensionUpdater* extension_updater = extensions_service->updater();
- // Auto-updates should now be on always (see the construction of the
- // ExtensionService in ProfileImpl::InitExtensions()).
- if (extension_updater) {
- extension_updater->CheckNow();
- } else {
- LOG(DFATAL) << "Extension updater unexpectedly NULL; "
- << "auto-updates may be turned off";
- }
-}
-
} // namespace
bool FlushExtensionData(const ExtensionSyncTraits& traits,
const ExtensionDataMap& extension_data_map,
- ProfileSyncService* sync_service) {
- sync_api::WriteTransaction trans(sync_service->GetUserShare());
+ ExtensionServiceInterface* extensions_service,
+ sync_api::UserShare* user_share) {
+ sync_api::WriteTransaction trans(user_share);
sync_api::ReadNode root(&trans);
if (!root.InitByTagLookup(traits.root_node_tag)) {
LOG(ERROR) << GetRootNodeDoesNotExistError(traits.root_node_tag);
return false;
}
- ExtensionService* extensions_service =
- GetExtensionServiceFromProfileSyncService(sync_service);
-
// Update server and client as necessary.
- bool should_nudge_extension_updater = false;
for (ExtensionDataMap::const_iterator it = extension_data_map.begin();
it != extension_data_map.end(); ++it) {
ExtensionData extension_data = it->second;
@@ -346,26 +261,22 @@ bool FlushExtensionData(const ExtensionSyncTraits& traits,
}
}
DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
- if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) {
- TryUpdateClient(traits.is_valid_and_syncable,
- extensions_service, &extension_data);
- if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) {
- should_nudge_extension_updater = true;
- }
+ ExtensionSyncData sync_data;
+ if (!GetExtensionSyncData(extension_data.merged_data(), &sync_data)) {
+ // TODO(akalin): Should probably recover or drop.
+ NOTREACHED();
+ return false;
}
- DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
+ extensions_service->ProcessSyncData(sync_data,
+ traits.is_valid_and_syncable);
}
-
- if (should_nudge_extension_updater) {
- NudgeExtensionUpdater(extensions_service);
- }
-
return true;
}
bool UpdateServerData(const ExtensionSyncTraits& traits,
const Extension& extension,
- ProfileSyncService* sync_service,
+ const ExtensionServiceInterface& extensions_service,
+ sync_api::UserShare* user_share,
std::string* error) {
const std::string& id = extension.id();
if (!traits.is_valid_and_syncable(extension)) {
@@ -376,16 +287,14 @@ bool UpdateServerData(const ExtensionSyncTraits& traits,
return false;
}
- ExtensionService* extensions_service =
- GetExtensionServiceFromProfileSyncService(sync_service);
sync_pb::ExtensionSpecifics client_data;
- GetExtensionSpecifics(extension, extensions_service->extension_prefs(),
+ GetExtensionSpecifics(extension, extensions_service,
&client_data);
DcheckIsExtensionSpecificsValid(client_data);
ExtensionData extension_data =
ExtensionData::FromData(ExtensionData::CLIENT, client_data);
- sync_api::WriteTransaction trans(sync_service->GetUserShare());
+ sync_api::WriteTransaction trans(user_share);
sync_api::ReadNode node(&trans);
if (node.InitByClientTagLookup(traits.model_type, id)) {
@@ -423,8 +332,8 @@ bool UpdateServerData(const ExtensionSyncTraits& traits,
void RemoveServerData(const ExtensionSyncTraits& traits,
const std::string& id,
- ProfileSyncService* sync_service) {
- sync_api::WriteTransaction trans(sync_service->GetUserShare());
+ sync_api::UserShare* user_share) {
+ sync_api::WriteTransaction trans(user_share);
sync_api::WriteNode write_node(&trans);
if (write_node.InitByClientTagLookup(traits.model_type, id)) {
write_node.Remove();
@@ -433,53 +342,4 @@ void RemoveServerData(const ExtensionSyncTraits& traits,
}
}
-void UpdateClient(const ExtensionSyncTraits& traits,
- const sync_pb::ExtensionSpecifics& server_data,
- ExtensionService* extensions_service) {
- DcheckIsExtensionSpecificsValid(server_data);
- ExtensionData extension_data =
- ExtensionData::FromData(ExtensionData::SERVER, server_data);
- const Extension* extension =
- extensions_service->GetExtensionById(server_data.id(), true);
- if (extension) {
- if (!traits.is_valid_and_syncable(*extension)) {
- LOG(WARNING) << "Ignoring server data for invalid or "
- << "non-syncable extension " << extension->id();
- return;
- }
- sync_pb::ExtensionSpecifics client_data;
- GetExtensionSpecifics(*extension, extensions_service->extension_prefs(),
- &client_data);
- DcheckIsExtensionSpecificsValid(client_data);
- extension_data =
- ExtensionData::FromData(ExtensionData::CLIENT, client_data);
- extension_data.SetData(ExtensionData::SERVER, true, server_data);
- }
- DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
- if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) {
- TryUpdateClient(traits.is_valid_and_syncable,
- extensions_service, &extension_data);
- if (extension_data.NeedsUpdate(ExtensionData::CLIENT)) {
- NudgeExtensionUpdater(extensions_service);
- }
- }
- DCHECK(!extension_data.NeedsUpdate(ExtensionData::SERVER));
-}
-
-void RemoveFromClient(const ExtensionSyncTraits& traits,
- const std::string& id,
- ExtensionService* extensions_service) {
- const Extension* extension = extensions_service->GetExtensionById(id, true);
- if (extension) {
- if (traits.is_valid_and_syncable(*extension)) {
- extensions_service->UninstallExtension(id, false);
- } else {
- LOG(WARNING) << "Ignoring server data for invalid or "
- << "non-syncable extension " << extension->id();
- }
- } else {
- LOG(ERROR) << "Trying to uninstall nonexistent extension " << id;
- }
-}
-
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extension_sync.h b/chrome/browser/sync/glue/extension_sync.h
index af7f9a5..e1f3f15 100644
--- a/chrome/browser/sync/glue/extension_sync.h
+++ b/chrome/browser/sync/glue/extension_sync.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,10 +13,14 @@
#include <string>
class Extension;
-class ExtensionService;
+class ExtensionServiceInterface;
class Profile;
class ProfileSyncService;
+namespace sync_api {
+struct UserShare;
+} // namespace sync_api
+
namespace sync_pb {
class ExtensionSpecifics;
} // namespace sync_pb
@@ -35,17 +39,16 @@ typedef std::map<std::string, ExtensionData> ExtensionDataMap;
// TODO(akalin): Move this somewhere where it can be used by
// other data types.
bool RootNodeHasChildren(const char* tag,
- ProfileSyncService* sync_service,
+ sync_api::UserShare* user_share,
bool* has_children);
-ExtensionService* GetExtensionServiceFromProfile(Profile* profile);
-
// Fills |extension_data_map| with both client-side information about
// installed extensions and the server-side information about
// extensions to be synced. Returns true iff this was successful; if
// unsuccessful, the contents of |extension_data_map| are undefined.
bool SlurpExtensionData(const ExtensionSyncTraits& traits,
- ProfileSyncService* sync_service,
+ const ExtensionServiceInterface& extensions_service,
+ sync_api::UserShare* user_share,
ExtensionDataMap* extension_data_map);
// Updates the server and client as necessary from
@@ -56,30 +59,22 @@ bool SlurpExtensionData(const ExtensionSyncTraits& traits,
// function is returned is that the updates were successfully started.
bool FlushExtensionData(const ExtensionSyncTraits& traits,
const ExtensionDataMap& extension_data_map,
- ProfileSyncService* sync_service);
+ ExtensionServiceInterface* extensions_service,
+ sync_api::UserShare* user_share);
// Updates the server data for the given extension. Returns true iff
// this was successful; if unsuccessful, an error string is put into
// |error|.
bool UpdateServerData(const ExtensionSyncTraits& traits,
const Extension& extension,
- ProfileSyncService* sync_service,
+ const ExtensionServiceInterface& extensions_service,
+ sync_api::UserShare* user_share,
std::string* error);
// Removes the server data for the given extension ID.
void RemoveServerData(const ExtensionSyncTraits& traits,
const std::string& id,
- ProfileSyncService* sync_service);
-
-// Starts updating the client data from the given server data.
-void UpdateClient(const ExtensionSyncTraits& traits,
- const sync_pb::ExtensionSpecifics& server_data,
- ExtensionService* extensions_service);
-
-// Removes existing client data for the given extension.
-void RemoveFromClient(const ExtensionSyncTraits& traits,
- const std::string& id,
- ExtensionService* extensions_service);
+ sync_api::UserShare* user_share);
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extension_util.cc b/chrome/browser/sync/glue/extension_util.cc
index 4713c92..5578f89 100644
--- a/chrome/browser/sync/glue/extension_util.cc
+++ b/chrome/browser/sync/glue/extension_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -7,15 +7,15 @@
#include <sstream>
#include "base/logging.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/stl_util-inl.h"
#include "base/version.h"
#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/browser/extensions/extension_service.h"
+#include "chrome/browser/extensions/extension_sync_data.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
-#include "googleurl/src/gurl.h"
namespace browser_sync {
@@ -143,21 +143,12 @@ bool AreExtensionSpecificsNonUserPropertiesEqual(
}
void GetExtensionSpecifics(const Extension& extension,
- ExtensionPrefs* extension_prefs,
+ const ExtensionServiceInterface& extension_service,
sync_pb::ExtensionSpecifics* specifics) {
- const std::string& id = extension.id();
- bool enabled =
- extension_prefs->GetExtensionState(id) == Extension::ENABLED;
- bool incognito_enabled = extension_prefs->IsIncognitoEnabled(id);
- GetExtensionSpecificsHelper(extension, enabled, incognito_enabled,
- specifics);
-}
-
-void GetExtensionSpecificsHelper(const Extension& extension,
- bool enabled, bool incognito_enabled,
- sync_pb::ExtensionSpecifics* specifics) {
DCHECK(IsExtensionValid(extension));
const std::string& id = extension.id();
+ bool enabled = extension_service.IsExtensionEnabled(id);
+ bool incognito_enabled = extension_service.IsIncognitoEnabled(id);
specifics->set_id(id);
specifics->set_version(extension.VersionString());
specifics->set_update_url(extension.update_url().spec());
@@ -167,52 +158,6 @@ void GetExtensionSpecificsHelper(const Extension& extension,
DcheckIsExtensionSpecificsValid(*specifics);
}
-bool IsExtensionOutdated(const Extension& extension,
- const sync_pb::ExtensionSpecifics& specifics) {
- DCHECK(IsExtensionValid(extension));
- DcheckIsExtensionSpecificsValid(specifics);
- scoped_ptr<Version> specifics_version(
- Version::GetVersionFromString(specifics.version()));
- if (!specifics_version.get()) {
- // If version is invalid, assume we're up-to-date.
- return false;
- }
- return extension.version()->CompareTo(*specifics_version) < 0;
-}
-
-void SetExtensionProperties(
- const sync_pb::ExtensionSpecifics& specifics,
- ExtensionService* extensions_service, const Extension* extension) {
- DcheckIsExtensionSpecificsValid(specifics);
- CHECK(extensions_service);
- CHECK(extension);
- DCHECK(IsExtensionValid(*extension));
- const std::string& id = extension->id();
- GURL update_url(specifics.update_url());
- if (update_url != extension->update_url()) {
- LOG(WARNING) << "specifics for extension " << id
- << "has a different update URL than the extension: "
- << update_url.spec() << " vs. " << extension->update_url();
- }
- ExtensionPrefs* extension_prefs = extensions_service->extension_prefs();
- bool enabled = extension_prefs->GetExtensionState(id) == Extension::ENABLED;
- if (enabled && !specifics.enabled()) {
- extensions_service->DisableExtension(id);
- } else if (!enabled && specifics.enabled()) {
- extensions_service->EnableExtension(id);
- }
- bool incognito_enabled = extension_prefs->IsIncognitoEnabled(id);
- if (incognito_enabled != specifics.incognito_enabled()) {
- extensions_service->SetIsIncognitoEnabled(
- extension, specifics.incognito_enabled());
- }
- if (specifics.name() != extension->name()) {
- LOG(WARNING) << "specifics for extension " << id
- << "has a different name than the extension: "
- << specifics.name() << " vs. " << extension->name();
- }
-}
-
void MergeExtensionSpecifics(
const sync_pb::ExtensionSpecifics& specifics,
bool merge_user_properties,
@@ -237,4 +182,31 @@ void MergeExtensionSpecifics(
}
}
+bool GetExtensionSyncData(
+ const sync_pb::ExtensionSpecifics& specifics,
+ ExtensionSyncData* sync_data) {
+ if (!Extension::IdIsValid(specifics.id())) {
+ return false;
+ }
+
+ scoped_ptr<Version> version(
+ Version::GetVersionFromString(specifics.version()));
+ if (!version.get()) {
+ return false;
+ }
+
+ // The update URL must be either empty or valid.
+ GURL update_url(specifics.update_url());
+ if (!update_url.is_empty() && !update_url.is_valid()) {
+ return false;
+ }
+
+ sync_data->id = specifics.id();
+ sync_data->update_url = update_url;
+ sync_data->version = *version;
+ sync_data->enabled = specifics.enabled();
+ sync_data->incognito_enabled = specifics.incognito_enabled();
+ return true;
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/extension_util.h b/chrome/browser/sync/glue/extension_util.h
index e908650..d914a75 100644
--- a/chrome/browser/sync/glue/extension_util.h
+++ b/chrome/browser/sync/glue/extension_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,7 +13,8 @@
class Extension;
class ExtensionPrefs;
-class ExtensionService;
+class ExtensionServiceInterface;
+struct ExtensionSyncData;
struct UninstalledExtensionInfo;
namespace sync_pb {
@@ -81,28 +82,9 @@ bool AreExtensionSpecificsNonUserPropertiesEqual(
// must be a syncable extension. |specifics| will be valid after this
// function is called.
void GetExtensionSpecifics(const Extension& extension,
- ExtensionPrefs* extension_prefs,
+ const ExtensionServiceInterface& extension_service,
sync_pb::ExtensionSpecifics* specifics);
-// Exposed only for testing. Pre- and post-conditions are the same as
-// GetExtensionSpecifics().
-void GetExtensionSpecificsHelper(const Extension& extension,
- bool enabled, bool incognito_enabled,
- sync_pb::ExtensionSpecifics* specifics);
-
-// Returns whether or not the extension should be updated according to
-// the specifics. |extension| must be syncable and |specifics| must
-// be valid.
-bool IsExtensionOutdated(const Extension& extension,
- const sync_pb::ExtensionSpecifics& specifics);
-
-// Sets properties of |extension| according to the information in
-// specifics. |extension| must be syncable and |specifics| must be
-// valid.
-void SetExtensionProperties(
- const sync_pb::ExtensionSpecifics& specifics,
- ExtensionService* extensions_service, const Extension* extension);
-
// Merge |specifics| into |merged_specifics|. Both must be valid and
// have the same ID. The merge policy is currently to copy the
// non-user properties of |specifics| into |merged_specifics| (and the
@@ -113,6 +95,12 @@ void MergeExtensionSpecifics(
bool merge_user_properties,
sync_pb::ExtensionSpecifics* merged_specifics);
+// Fills |sync_data| with the data from |specifics|. Returns true iff
+// succesful.
+bool GetExtensionSyncData(
+ const sync_pb::ExtensionSpecifics& specifics,
+ ExtensionSyncData* sync_data);
+
} // namespace browser_sync
#endif // CHROME_BROWSER_SYNC_GLUE_EXTENSION_UTIL_H_
diff --git a/chrome/browser/sync/glue/extension_util_unittest.cc b/chrome/browser/sync/glue/extension_util_unittest.cc
index 2ca92dd..5f8227c 100644
--- a/chrome/browser/sync/glue/extension_util_unittest.cc
+++ b/chrome/browser/sync/glue/extension_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,15 +6,21 @@
#include "base/file_path.h"
#include "base/values.h"
+#include "chrome/browser/extensions/mock_extension_service.h"
#include "chrome/browser/sync/protocol/extension_specifics.pb.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
namespace {
+using ::testing::_;
+using ::testing::Return;
+using ::testing::StrictMock;
+
#if defined(OS_WIN)
const FilePath::CharType kExtensionFilePath[] = FILE_PATH_LITERAL("c:\\foo");
#elif defined(OS_POSIX)
@@ -71,13 +77,7 @@ scoped_refptr<Extension> MakeExtension(
std::string error;
scoped_refptr<Extension> extension = Extension::Create(
- extension_path, location, source, false, true, &error);
-#if defined(OS_CHROMEOS)
- if (num_plugins > 0) { // plugins are illegal in extensions on chrome os.
- EXPECT_FALSE(extension);
- return NULL;
- }
-#endif
+ extension_path, location, source, Extension::STRICT_ERROR_CHECKS, &error);
EXPECT_TRUE(extension);
EXPECT_EQ("", error);
return extension;
@@ -135,6 +135,9 @@ TEST_F(ExtensionUtilTest, IsExtensionValid) {
Extension::INTERNAL, 0, file_path));
EXPECT_FALSE(IsExtensionValid(*extension));
}
+ // These last 2 tests don't make sense on Chrome OS, where extension plugins
+ // are not allowed.
+#if !defined(OS_CHROMEOS)
{
FilePath file_path(kExtensionFilePath);
scoped_refptr<Extension> extension(
@@ -149,6 +152,7 @@ TEST_F(ExtensionUtilTest, IsExtensionValid) {
Extension::INTERNAL, 2, file_path));
EXPECT_FALSE(extension && IsExtensionValid(*extension));
}
+#endif
}
TEST_F(ExtensionUtilTest, IsExtensionSpecificsUnset) {
@@ -386,18 +390,26 @@ scoped_refptr<Extension> MakeSyncableExtension(
source.SetString(extension_manifest_keys::kName, name);
std::string error;
scoped_refptr<Extension> extension = Extension::Create(
- extension_path, Extension::INTERNAL, source, false, true, &error);
+ extension_path, Extension::INTERNAL, source,
+ Extension::STRICT_ERROR_CHECKS, &error);
EXPECT_TRUE(extension);
EXPECT_EQ("", error);
return extension;
}
-TEST_F(ExtensionUtilTest, GetExtensionSpecificsHelper) {
+TEST_F(ExtensionUtilTest, GetExtensionSpecifics) {
FilePath file_path(kExtensionFilePath);
+ StrictMock<MockExtensionService> mock_extension_service;
+ EXPECT_CALL(mock_extension_service, IsExtensionEnabled(_))
+ .WillOnce(Return(true));
+ EXPECT_CALL(mock_extension_service, IsIncognitoEnabled(_))
+ .WillOnce(Return(false));
+
scoped_refptr<Extension> extension(
- MakeSyncableExtension(kValidVersion, kValidUpdateUrl1, kName, file_path));
+ MakeSyncableExtension(
+ kValidVersion, kValidUpdateUrl1, kName, file_path));
sync_pb::ExtensionSpecifics specifics;
- GetExtensionSpecificsHelper(*extension, true, false, &specifics);
+ GetExtensionSpecifics(*extension, mock_extension_service, &specifics);
EXPECT_EQ(extension->id(), specifics.id());
EXPECT_EQ(extension->VersionString(), kValidVersion);
EXPECT_EQ(extension->update_url().spec(), kValidUpdateUrl1);
@@ -406,22 +418,6 @@ TEST_F(ExtensionUtilTest, GetExtensionSpecificsHelper) {
EXPECT_EQ(kName, specifics.name());
}
-TEST_F(ExtensionUtilTest, IsExtensionOutdated) {
- FilePath file_path(kExtensionFilePath);
- scoped_refptr<Extension> extension(
- MakeSyncableExtension(kVersion2, kValidUpdateUrl1, kName, file_path));
- sync_pb::ExtensionSpecifics specifics;
- specifics.set_id(kValidId);
- specifics.set_update_url(kValidUpdateUrl1);
-
- specifics.set_version(kVersion1);
- EXPECT_FALSE(IsExtensionOutdated(*extension, specifics));
- specifics.set_version(kVersion2);
- EXPECT_FALSE(IsExtensionOutdated(*extension, specifics));
- specifics.set_version(kVersion3);
- EXPECT_TRUE(IsExtensionOutdated(*extension, specifics));
-}
-
// TODO(akalin): Make ExtensionService/ExtensionUpdater testable
// enough to be able to write a unittest for SetExtensionProperties().
diff --git a/chrome/browser/sync/glue/foreign_session_tracker.h b/chrome/browser/sync/glue/foreign_session_tracker.h
index dd99455..5076d8f 100644
--- a/chrome/browser/sync/glue/foreign_session_tracker.h
+++ b/chrome/browser/sync/glue/foreign_session_tracker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,7 +12,7 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/scoped_vector.h"
+#include "base/memory/scoped_vector.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/sessions/session_types.h"
diff --git a/chrome/browser/sync/glue/frontend_data_type_controller.cc b/chrome/browser/sync/glue/frontend_data_type_controller.cc
new file mode 100644
index 0000000..5e324f9
--- /dev/null
+++ b/chrome/browser/sync/glue/frontend_data_type_controller.cc
@@ -0,0 +1,172 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
+
+#include "base/logging.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/sync/glue/change_processor.h"
+#include "chrome/browser/sync/glue/model_associator.h"
+#include "chrome/browser/sync/profile_sync_factory.h"
+#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "content/browser/browser_thread.h"
+
+namespace browser_sync {
+
+FrontendDataTypeController::FrontendDataTypeController()
+ : profile_sync_factory_(NULL),
+ profile_(NULL),
+ sync_service_(NULL) {}
+
+FrontendDataTypeController::FrontendDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* sync_service)
+ : profile_sync_factory_(profile_sync_factory),
+ profile_(profile),
+ sync_service_(sync_service),
+ state_(NOT_RUNNING) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(profile_sync_factory);
+ DCHECK(profile);
+ DCHECK(sync_service);
+}
+
+FrontendDataTypeController::~FrontendDataTypeController() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+}
+
+void FrontendDataTypeController::Start(StartCallback* start_callback) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ DCHECK(start_callback);
+ if (state_ != NOT_RUNNING) {
+ start_callback->Run(BUSY, FROM_HERE);
+ delete start_callback;
+ return;
+ }
+
+ start_callback_.reset(start_callback);
+
+ state_ = MODEL_STARTING;
+ if (!StartModels()) {
+ // If we are waiting for some external service to load before associating
+ // or we failed to start the models, we exit early. state_ will control
+ // what we perform next.
+ DCHECK(state_ == NOT_RUNNING || state_ == MODEL_STARTING);
+ return;
+ }
+
+ state_ = ASSOCIATING;
+ if (!Associate()) {
+ // We failed to associate and are aborting.
+ DCHECK_EQ(state_, NOT_RUNNING);
+ return;
+ }
+ DCHECK_EQ(state_, RUNNING);
+}
+
+bool FrontendDataTypeController::StartModels() {
+ DCHECK_EQ(state_, MODEL_STARTING);
+ // By default, no additional services need to be started before we can proceed
+ // with model association.
+ return true;
+}
+
+bool FrontendDataTypeController::Associate() {
+ DCHECK_EQ(state_, ASSOCIATING);
+ CreateSyncComponents();
+
+ if (!model_associator_->CryptoReadyIfNecessary()) {
+ StartFailed(NEEDS_CRYPTO, FROM_HERE);
+ return false;
+ }
+
+ bool sync_has_nodes = false;
+ if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
+ StartFailed(UNRECOVERABLE_ERROR, FROM_HERE);
+ return false;
+ }
+
+ base::TimeTicks start_time = base::TimeTicks::Now();
+ bool merge_success = model_associator_->AssociateModels();
+ RecordAssociationTime(base::TimeTicks::Now() - start_time);
+ if (!merge_success) {
+ StartFailed(ASSOCIATION_FAILED, FROM_HERE);
+ return false;
+ }
+
+ sync_service_->ActivateDataType(this, change_processor_.get());
+ state_ = RUNNING;
+ FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK, FROM_HERE);
+ return true;
+}
+
+void FrontendDataTypeController::Stop() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // If Stop() is called while Start() is waiting for the datatype model to
+ // load, abort the start.
+ if (state_ == MODEL_STARTING)
+ FinishStart(ABORTED, FROM_HERE);
+ DCHECK(!start_callback_.get());
+
+ CleanupState();
+
+ if (change_processor_ != NULL)
+ sync_service_->DeactivateDataType(this, change_processor_.get());
+
+ if (model_associator_ != NULL)
+ model_associator_->DisassociateModels();
+
+ change_processor_.reset();
+ model_associator_.reset();
+
+ state_ = NOT_RUNNING;
+}
+
+void FrontendDataTypeController::CleanupState() {
+ // Do nothing by default.
+}
+
+browser_sync::ModelSafeGroup FrontendDataTypeController::model_safe_group()
+ const {
+ return browser_sync::GROUP_UI;
+}
+
+std::string FrontendDataTypeController::name() const {
+ // For logging only.
+ return syncable::ModelTypeToString(type());
+}
+
+DataTypeController::State FrontendDataTypeController::state() const {
+ return state_;
+}
+
+void FrontendDataTypeController::OnUnrecoverableError(
+ const tracked_objects::Location& from_here, const std::string& message) {
+ // The ProfileSyncService will invoke our Stop() method in response to this.
+ RecordUnrecoverableError(from_here, message);
+ sync_service_->OnUnrecoverableError(from_here, message);
+}
+
+void FrontendDataTypeController::FinishStart(StartResult result,
+ const tracked_objects::Location& location) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ start_callback_->Run(result, location);
+ start_callback_.reset();
+}
+
+void FrontendDataTypeController::StartFailed(StartResult result,
+ const tracked_objects::Location& location) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ CleanupState();
+ model_associator_.reset();
+ change_processor_.reset();
+ state_ = NOT_RUNNING;
+ start_callback_->Run(result, location);
+ start_callback_.reset();
+ RecordStartFailure(result);
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/frontend_data_type_controller.h b/chrome/browser/sync/glue/frontend_data_type_controller.h
new file mode 100644
index 0000000..d228bb9
--- /dev/null
+++ b/chrome/browser/sync/glue/frontend_data_type_controller.h
@@ -0,0 +1,113 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_FRONTEND_DATA_TYPE_CONTROLLER_H__
+#define CHROME_BROWSER_SYNC_GLUE_FRONTEND_DATA_TYPE_CONTROLLER_H__
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/sync/glue/data_type_controller.h"
+
+class Profile;
+class ProfileSyncService;
+class ProfileSyncFactory;
+
+namespace base { class TimeDelta; }
+namespace browser_sync {
+
+class AssociatorInterface;
+class ChangeProcessor;
+
+// Implementation for datatypes that reside on the frontend thread
+// (UI thread). This is the same thread we perform initialization on, so we
+// don't have to worry about thread safety. The main start/stop funtionality is
+// implemented by default.
+// Derived classes must implement (at least):
+// syncable::ModelType type() const
+// void CreateSyncComponents();
+// void RecordUnrecoverableError(
+// const tracked_objects::Location& from_here,
+// const std::string& message);
+// void RecordAssociationTime(base::TimeDelta time);
+// void RecordStartFailure(StartResult result);
+class FrontendDataTypeController : public DataTypeController {
+ public:
+ FrontendDataTypeController(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* sync_service);
+ virtual ~FrontendDataTypeController();
+
+ // DataTypeController interface.
+ virtual void Start(StartCallback* start_callback);
+
+ virtual void Stop();
+
+ virtual syncable::ModelType type() const = 0;
+
+ virtual browser_sync::ModelSafeGroup model_safe_group() const;
+
+ virtual std::string name() const;
+
+ virtual State state() const;
+
+ // UnrecoverableErrorHandler interface.
+ virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
+ const std::string& message);
+ protected:
+ // For testing only.
+ FrontendDataTypeController();
+
+ // Kick off any dependent services that need to be running before we can
+ // associate models. The default implementation is a no-op.
+ virtual bool StartModels();
+
+ // Build sync components and associate models.
+ virtual bool Associate();
+
+ // Perform any DataType controller specific state cleanup before stopping
+ // the datatype controller. The default implementation is a no-op.
+ virtual void CleanupState();
+
+ // Helper method to run the stashed start callback with a given result.
+ virtual void FinishStart(StartResult result,
+ const tracked_objects::Location& from_here);
+
+ // Cleans up state and calls callback when start fails.
+ virtual void StartFailed(StartResult result,
+ const tracked_objects::Location& from_here);
+
+ // Datatype specific creation of sync components.
+ virtual void CreateSyncComponents() = 0;
+
+ // DataType specific histogram methods. Because histograms use static's, the
+ // specific datatype controllers must implement this themselves.
+ // Record unrecoverable errors.
+ virtual void RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) = 0;
+ // Record association time.
+ virtual void RecordAssociationTime(base::TimeDelta time) = 0;
+ // Record causes of start failure.
+ virtual void RecordStartFailure(StartResult result) = 0;
+
+ ProfileSyncFactory* const profile_sync_factory_;
+ Profile* const profile_;
+ ProfileSyncService* const sync_service_;
+
+ State state_;
+
+ scoped_ptr<StartCallback> start_callback_;
+ scoped_ptr<AssociatorInterface> model_associator_;
+ scoped_ptr<ChangeProcessor> change_processor_;
+
+ DISALLOW_COPY_AND_ASSIGN(FrontendDataTypeController);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_FRONTEND_DATA_TYPE_CONTROLLER_H__
diff --git a/chrome/browser/sync/glue/frontend_data_type_controller_mock.cc b/chrome/browser/sync/glue/frontend_data_type_controller_mock.cc
new file mode 100644
index 0000000..405d328
--- /dev/null
+++ b/chrome/browser/sync/glue/frontend_data_type_controller_mock.cc
@@ -0,0 +1,13 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/glue/frontend_data_type_controller_mock.h"
+
+namespace browser_sync {
+
+FrontendDataTypeControllerMock::FrontendDataTypeControllerMock() {}
+
+FrontendDataTypeControllerMock::~FrontendDataTypeControllerMock() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/frontend_data_type_controller_mock.h b/chrome/browser/sync/glue/frontend_data_type_controller_mock.h
new file mode 100644
index 0000000..0485b1f
--- /dev/null
+++ b/chrome/browser/sync/glue/frontend_data_type_controller_mock.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_GLUE_FRONTEND_DATA_TYPE_CONTROLLER_MOCK_H__
+#define CHROME_BROWSER_SYNC_GLUE_FRONTEND_DATA_TYPE_CONTROLLER_MOCK_H__
+#pragma once
+
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace browser_sync {
+
+class FrontendDataTypeControllerMock : public FrontendDataTypeController {
+ public:
+ FrontendDataTypeControllerMock();
+ virtual ~FrontendDataTypeControllerMock();
+
+ // DataTypeController mocks.
+ MOCK_METHOD1(Start, void(StartCallback* start_callback));
+ MOCK_METHOD0(Stop, void());
+ MOCK_METHOD0(enabled, bool());
+ MOCK_CONST_METHOD0(type, syncable::ModelType());
+ MOCK_CONST_METHOD0(name, std::string());
+ MOCK_CONST_METHOD0(model_safe_group, browser_sync::ModelSafeGroup());
+ MOCK_CONST_METHOD0(state, State());
+ MOCK_METHOD2(OnUnrecoverableError, void(const tracked_objects::Location&,
+ const std::string&));
+
+ // FrontendDataTypeController mocks.
+ MOCK_METHOD0(StartModels, bool());
+ MOCK_METHOD0(Associate, bool());
+ MOCK_METHOD0(CleanupState, void());
+ MOCK_METHOD2(FinishStart, void(StartResult result,
+ const tracked_objects::Location& from_here));
+ MOCK_METHOD2(StartFailed, void(StartResult result,
+ const tracked_objects::Location& from_here));
+ MOCK_METHOD0(CreateSyncComponents, void());
+ MOCK_METHOD2(RecordUnrecoverableError, void(const tracked_objects::Location&,
+ const std::string&));
+ MOCK_METHOD1(RecordAssociationTime, void(base::TimeDelta time));
+ MOCK_METHOD1(RecordStartFailure, void(StartResult result));
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_FRONTEND_DATA_TYPE_CONTROLLER_MOCK_H__
diff --git a/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc b/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc
new file mode 100644
index 0000000..c0c2290
--- /dev/null
+++ b/chrome/browser/sync/glue/frontend_data_type_controller_unittest.cc
@@ -0,0 +1,242 @@
+// Copyright (c) 2011 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.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/tracked_objects.h"
+#include "chrome/browser/sync/glue/change_processor_mock.h"
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
+#include "chrome/browser/sync/glue/frontend_data_type_controller_mock.h"
+#include "chrome/browser/sync/glue/model_associator_mock.h"
+#include "chrome/browser/sync/profile_sync_factory_mock.h"
+#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/test/profile_mock.h"
+#include "content/browser/browser_thread.h"
+
+using browser_sync::ChangeProcessorMock;
+using browser_sync::DataTypeController;
+using browser_sync::FrontendDataTypeController;
+using browser_sync::FrontendDataTypeControllerMock;
+using browser_sync::ModelAssociatorMock;
+using testing::_;
+using testing::DoAll;
+using testing::InvokeWithoutArgs;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::StrictMock;
+
+class StartCallback {
+ public:
+ MOCK_METHOD2(Run, void(DataTypeController::StartResult result,
+ const tracked_objects::Location& from_here));
+};
+
+class FrontendDataTypeControllerFake : public FrontendDataTypeController {
+ public:
+ FrontendDataTypeControllerFake(
+ ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
+ ProfileSyncService* sync_service,
+ FrontendDataTypeControllerMock* mock)
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service),
+ mock_(mock) {}
+ virtual syncable::ModelType type() const { return syncable::BOOKMARKS; }
+
+ private:
+ virtual void CreateSyncComponents() {
+ ProfileSyncFactory::SyncComponents sync_components =
+ profile_sync_factory_->
+ CreateBookmarkSyncComponents(sync_service_, this);
+ model_associator_.reset(sync_components.model_associator);
+ change_processor_.reset(sync_components.change_processor);
+ }
+
+ // We mock the following methods because their default implementations do
+ // nothing, but we still want to make sure they're called appropriately.
+ virtual bool StartModels() {
+ return mock_->StartModels();
+ }
+ virtual void CleanupState() {
+ mock_->CleanupState();
+ }
+ virtual void RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message) {
+ mock_->RecordUnrecoverableError(from_here, message);
+ }
+ virtual void RecordAssociationTime(base::TimeDelta time) {
+ mock_->RecordAssociationTime(time);
+ }
+ virtual void RecordStartFailure(DataTypeController::StartResult result) {
+ mock_->RecordStartFailure(result);
+ }
+ private:
+ FrontendDataTypeControllerMock* mock_;
+};
+
+class FrontendDataTypeControllerTest : public testing::Test {
+ public:
+ FrontendDataTypeControllerTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_) {}
+
+ virtual void SetUp() {
+ profile_sync_factory_.reset(new ProfileSyncFactoryMock());
+ dtc_mock_ = new StrictMock<FrontendDataTypeControllerMock>();
+ frontend_dtc_ =
+ new FrontendDataTypeControllerFake(profile_sync_factory_.get(),
+ &profile_,
+ &service_,
+ dtc_mock_.get());
+ }
+
+ protected:
+ void SetStartExpectations() {
+ EXPECT_CALL(*dtc_mock_, StartModels()).WillOnce(Return(true));
+ model_associator_ = new ModelAssociatorMock();
+ change_processor_ = new ChangeProcessorMock();
+ EXPECT_CALL(*profile_sync_factory_, CreateBookmarkSyncComponents(_, _)).
+ WillOnce(Return(ProfileSyncFactory::SyncComponents(model_associator_,
+ change_processor_)));
+ }
+
+ void SetAssociateExpectations() {
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillOnce(Return(true));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(*model_associator_, AssociateModels()).
+ WillOnce(Return(true));
+ EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_));
+ }
+
+ void SetActivateExpectations(DataTypeController::StartResult result) {
+ EXPECT_CALL(service_, ActivateDataType(_, _));
+ EXPECT_CALL(start_callback_, Run(result,_));
+ }
+
+ void SetStopExpectations() {
+ EXPECT_CALL(*dtc_mock_, CleanupState());
+ EXPECT_CALL(service_, DeactivateDataType(_, _));
+ EXPECT_CALL(*model_associator_, DisassociateModels());
+ }
+
+ void SetStartFailExpectations(DataTypeController::StartResult result) {
+ EXPECT_CALL(*dtc_mock_, CleanupState());
+ EXPECT_CALL(*dtc_mock_, RecordStartFailure(result));
+ EXPECT_CALL(start_callback_, Run(result,_));
+ }
+
+ MessageLoopForUI message_loop_;
+ BrowserThread ui_thread_;
+ scoped_refptr<FrontendDataTypeControllerFake> frontend_dtc_;
+ scoped_ptr<ProfileSyncFactoryMock> profile_sync_factory_;
+ scoped_refptr<FrontendDataTypeControllerMock> dtc_mock_;
+ ProfileMock profile_;
+ ProfileSyncServiceMock service_;
+ ModelAssociatorMock* model_associator_;
+ ChangeProcessorMock* change_processor_;
+ StartCallback start_callback_;
+};
+
+TEST_F(FrontendDataTypeControllerTest, StartOk) {
+ SetStartExpectations();
+ SetAssociateExpectations();
+ SetActivateExpectations(DataTypeController::OK);
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
+}
+
+TEST_F(FrontendDataTypeControllerTest, StartFirstRun) {
+ SetStartExpectations();
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillOnce(Return(true));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillOnce(DoAll(SetArgumentPointee<0>(false), Return(true)));
+ EXPECT_CALL(*model_associator_, AssociateModels()).
+ WillOnce(Return(true));
+ EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_));
+ SetActivateExpectations(DataTypeController::OK_FIRST_RUN);
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
+}
+
+TEST_F(FrontendDataTypeControllerTest, StartAssociationFailed) {
+ SetStartExpectations();
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillOnce(Return(true));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillOnce(DoAll(SetArgumentPointee<0>(true), Return(true)));
+ EXPECT_CALL(*model_associator_, AssociateModels()).
+ WillOnce(Return(false));
+ EXPECT_CALL(*dtc_mock_, RecordAssociationTime(_));
+ SetStartFailExpectations(DataTypeController::ASSOCIATION_FAILED);
+ // Set up association to fail with an association failed error.
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+}
+
+TEST_F(FrontendDataTypeControllerTest,
+ StartAssociationTriggersUnrecoverableError) {
+ SetStartExpectations();
+ SetStartFailExpectations(DataTypeController::UNRECOVERABLE_ERROR);
+ // Set up association to fail with an unrecoverable error.
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
+ EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+}
+
+TEST_F(FrontendDataTypeControllerTest, StartAssociationCryptoNotReady) {
+ SetStartExpectations();
+ SetStartFailExpectations(DataTypeController::NEEDS_CRYPTO);
+ // Set up association to fail with a NEEDS_CRYPTO error.
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(false));
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+}
+
+TEST_F(FrontendDataTypeControllerTest, Stop) {
+ SetStartExpectations();
+ SetAssociateExpectations();
+ SetActivateExpectations(DataTypeController::OK);
+ SetStopExpectations();
+
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Stop();
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+}
+
+TEST_F(FrontendDataTypeControllerTest, OnUnrecoverableError) {
+ SetStartExpectations();
+ SetAssociateExpectations();
+ SetActivateExpectations(DataTypeController::OK);
+ EXPECT_CALL(*dtc_mock_, RecordUnrecoverableError(_, "Test"));
+ EXPECT_CALL(service_, OnUnrecoverableError(_,_)).
+ WillOnce(InvokeWithoutArgs(frontend_dtc_.get(),
+ &FrontendDataTypeController::Stop));
+ SetStopExpectations();
+
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+ frontend_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
+ EXPECT_EQ(DataTypeController::RUNNING, frontend_dtc_->state());
+ // This should cause frontend_dtc_->Stop() to be called.
+ frontend_dtc_->OnUnrecoverableError(FROM_HERE, "Test");
+ EXPECT_EQ(DataTypeController::NOT_RUNNING, frontend_dtc_->state());
+}
diff --git a/chrome/browser/sync/glue/history_model_worker.cc b/chrome/browser/sync/glue/history_model_worker.cc
index e880476..a1abf13 100644
--- a/chrome/browser/sync/glue/history_model_worker.cc
+++ b/chrome/browser/sync/glue/history_model_worker.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/history_model_worker.h"
+#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
-#include "base/ref_counted.h"
#include "base/task.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/history/history.h"
diff --git a/chrome/browser/sync/glue/history_model_worker.h b/chrome/browser/sync/glue/history_model_worker.h
index e449a93..f533acf 100644
--- a/chrome/browser/sync/glue/history_model_worker.h
+++ b/chrome/browser/sync/glue/history_model_worker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,7 +10,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "content/browser/cancelable_request.h"
class HistoryService;
diff --git a/chrome/browser/sync/glue/http_bridge.cc b/chrome/browser/sync/glue/http_bridge.cc
index 610de89..70498f7 100644
--- a/chrome/browser/sync/glue/http_bridge.cc
+++ b/chrome/browser/sync/glue/http_bridge.cc
@@ -11,6 +11,7 @@
#include "net/base/cookie_monster.h"
#include "net/base/host_resolver.h"
#include "net/base/load_flags.h"
+#include "net/base/net_errors.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
#include "net/http/http_response_headers.h"
@@ -22,7 +23,7 @@
namespace browser_sync {
HttpBridge::RequestContextGetter::RequestContextGetter(
- URLRequestContextGetter* baseline_context_getter)
+ net::URLRequestContextGetter* baseline_context_getter)
: baseline_context_getter_(baseline_context_getter) {
}
@@ -49,7 +50,7 @@ HttpBridge::RequestContextGetter::GetIOMessageLoopProxy() const {
}
HttpBridgeFactory::HttpBridgeFactory(
- URLRequestContextGetter* baseline_context_getter) {
+ net::URLRequestContextGetter* baseline_context_getter) {
DCHECK(baseline_context_getter != NULL);
request_context_getter_ =
new HttpBridge::RequestContextGetter(baseline_context_getter);
@@ -111,14 +112,17 @@ HttpBridge::RequestContext::~RequestContext() {
delete http_transaction_factory();
}
+HttpBridge::URLFetchState::URLFetchState() : url_poster(NULL),
+ aborted(false),
+ request_completed(false),
+ request_succeeded(false),
+ http_response_code(-1),
+ os_error_code(-1) {}
+HttpBridge::URLFetchState::~URLFetchState() {}
+
HttpBridge::HttpBridge(HttpBridge::RequestContextGetter* context_getter)
: context_getter_for_request_(context_getter),
- url_poster_(NULL),
created_on_loop_(MessageLoop::current()),
- request_completed_(false),
- request_succeeded_(false),
- http_response_code_(-1),
- os_error_code_(-1),
http_post_completed_(false, false) {
}
@@ -127,7 +131,10 @@ HttpBridge::~HttpBridge() {
void HttpBridge::SetUserAgent(const char* user_agent) {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(!request_completed_);
+ if (DCHECK_IS_ON()) {
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(!fetch_state_.request_completed);
+ }
context_getter_for_request_->set_user_agent(user_agent);
}
@@ -139,7 +146,10 @@ void HttpBridge::SetExtraRequestHeaders(const char * headers) {
void HttpBridge::SetURL(const char* url, int port) {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(!request_completed_);
+ if (DCHECK_IS_ON()) {
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(!fetch_state_.request_completed);
+ }
DCHECK(url_for_request_.is_empty())
<< "HttpBridge::SetURL called more than once?!";
GURL temp(url);
@@ -154,7 +164,10 @@ void HttpBridge::SetPostPayload(const char* content_type,
int content_length,
const char* content) {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(!request_completed_);
+ if (DCHECK_IS_ON()) {
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(!fetch_state_.request_completed);
+ }
DCHECK(content_type_.empty()) << "Bridge payload already set.";
DCHECK_GE(content_length, 0) << "Content length < 0";
content_type_ = content_type;
@@ -170,57 +183,88 @@ void HttpBridge::SetPostPayload(const char* content_type,
bool HttpBridge::MakeSynchronousPost(int* os_error_code, int* response_code) {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(!request_completed_);
+ if (DCHECK_IS_ON()) {
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(!fetch_state_.request_completed);
+ }
DCHECK(url_for_request_.is_valid()) << "Invalid URL for request";
DCHECK(!content_type_.empty()) << "Payload not set";
- BrowserThread::PostTask(
- BrowserThread::IO, FROM_HERE,
- NewRunnableMethod(this, &HttpBridge::CallMakeAsynchronousPost));
+ if (!BrowserThread::PostTask(
+ BrowserThread::IO, FROM_HERE,
+ NewRunnableMethod(this, &HttpBridge::CallMakeAsynchronousPost))) {
+ // This usually happens when we're in a unit test.
+ LOG(WARNING) << "Could not post CallMakeAsynchronousPost task";
+ return false;
+ }
- if (!http_post_completed_.Wait()) // Block until network request completes.
- NOTREACHED(); // See OnURLFetchComplete.
+ if (!http_post_completed_.Wait()) // Block until network request completes
+ NOTREACHED(); // or is aborted. See OnURLFetchComplete
+ // and Abort.
- DCHECK(request_completed_);
- *os_error_code = os_error_code_;
- *response_code = http_response_code_;
- return request_succeeded_;
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(fetch_state_.request_completed || fetch_state_.aborted);
+ *os_error_code = fetch_state_.os_error_code;
+ *response_code = fetch_state_.http_response_code;
+ return fetch_state_.request_succeeded;
}
void HttpBridge::MakeAsynchronousPost() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
- DCHECK(!request_completed_);
-
- url_poster_ = new URLFetcher(url_for_request_, URLFetcher::POST, this);
- url_poster_->set_request_context(context_getter_for_request_);
- url_poster_->set_upload_data(content_type_, request_content_);
- url_poster_->set_extra_request_headers(extra_headers_);
- url_poster_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
- url_poster_->Start();
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(!fetch_state_.request_completed);
+ if (fetch_state_.aborted)
+ return;
+
+ fetch_state_.url_poster = new URLFetcher(url_for_request_,
+ URLFetcher::POST, this);
+ fetch_state_.url_poster->set_request_context(context_getter_for_request_);
+ fetch_state_.url_poster->set_upload_data(content_type_, request_content_);
+ fetch_state_.url_poster->set_extra_request_headers(extra_headers_);
+ fetch_state_.url_poster->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
+ fetch_state_.url_poster->Start();
}
int HttpBridge::GetResponseContentLength() const {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(request_completed_);
- return response_content_.size();
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(fetch_state_.request_completed);
+ return fetch_state_.response_content.size();
}
const char* HttpBridge::GetResponseContent() const {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(request_completed_);
- return response_content_.data();
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(fetch_state_.request_completed);
+ return fetch_state_.response_content.data();
}
const std::string HttpBridge::GetResponseHeaderValue(
const std::string& name) const {
DCHECK_EQ(MessageLoop::current(), created_on_loop_);
- DCHECK(request_completed_);
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(fetch_state_.request_completed);
+
std::string value;
- response_headers_->EnumerateHeader(NULL, name, &value);
+ fetch_state_.response_headers->EnumerateHeader(NULL, name, &value);
return value;
}
+void HttpBridge::Abort() {
+ base::AutoLock lock(fetch_state_lock_);
+ DCHECK(!fetch_state_.aborted);
+ if (fetch_state_.aborted || fetch_state_.request_completed)
+ return;
+
+ fetch_state_.aborted = true;
+ BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
+ fetch_state_.url_poster);
+ fetch_state_.url_poster = NULL;
+ fetch_state_.os_error_code = net::ERR_ABORTED;
+ http_post_completed_.Signal();
+}
+
void HttpBridge::OnURLFetchComplete(const URLFetcher *source,
const GURL &url,
const net::URLRequestStatus &status,
@@ -228,20 +272,24 @@ void HttpBridge::OnURLFetchComplete(const URLFetcher *source,
const ResponseCookies &cookies,
const std::string &data) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+ base::AutoLock lock(fetch_state_lock_);
+ if (fetch_state_.aborted)
+ return;
- request_completed_ = true;
- request_succeeded_ = (net::URLRequestStatus::SUCCESS == status.status());
- http_response_code_ = response_code;
- os_error_code_ = status.os_error();
+ fetch_state_.request_completed = true;
+ fetch_state_.request_succeeded =
+ (net::URLRequestStatus::SUCCESS == status.status());
+ fetch_state_.http_response_code = response_code;
+ fetch_state_.os_error_code = status.os_error();
- response_content_ = data;
- response_headers_ = source->response_headers();
+ fetch_state_.response_content = data;
+ fetch_state_.response_headers = source->response_headers();
// End of the line for url_poster_. It lives only on the IO loop.
// We defer deletion because we're inside a callback from a component of the
// URLFetcher, so it seems most natural / "polite" to let the stack unwind.
- MessageLoop::current()->DeleteSoon(FROM_HERE, url_poster_);
- url_poster_ = NULL;
+ MessageLoop::current()->DeleteSoon(FROM_HERE, fetch_state_.url_poster);
+ fetch_state_.url_poster = NULL;
// Wake the blocked syncer thread in MakeSynchronousPost.
// WARNING: DONT DO ANYTHING AFTER THIS CALL! |this| may be deleted!
diff --git a/chrome/browser/sync/glue/http_bridge.h b/chrome/browser/sync/glue/http_bridge.h
index 57c9f94..2ff3588 100644
--- a/chrome/browser/sync/glue/http_bridge.h
+++ b/chrome/browser/sync/glue/http_bridge.h
@@ -8,13 +8,18 @@
#include <string>
-#include "base/ref_counted.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
#include "base/synchronization/waitable_event.h"
+#include "chrome/browser/sync/engine/http_post_provider_factory.h"
+#include "chrome/browser/sync/engine/http_post_provider_interface.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/common/net/url_fetcher.h"
-#include "chrome/common/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
#include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
class MessageLoop;
@@ -35,12 +40,6 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
// A request context used for HTTP requests bridged from the sync backend.
// A bridged RequestContext has a dedicated in-memory cookie store and does
// not use a cache. Thus the same type can be used for incognito mode.
- // TODO(timsteele): We subclass here instead of add a factory method on
- // ChromeURLRequestContext because:
- // 1) we want the ability to set_user_agent
- // 2) avoids ifdefs for now
- // 3) not sure we want to strictly follow settings for cookie policy,
- // accept lang/charset, since changing these could break syncing.
class RequestContext : public net::URLRequestContext {
public:
// |baseline_context| is used to obtain the accept-language,
@@ -71,15 +70,15 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
};
// Lazy-getter for RequestContext objects.
- class RequestContextGetter : public URLRequestContextGetter {
+ class RequestContextGetter : public net::URLRequestContextGetter {
public:
explicit RequestContextGetter(
- URLRequestContextGetter* baseline_context_getter);
+ net::URLRequestContextGetter* baseline_context_getter);
void set_user_agent(const std::string& ua) { user_agent_ = ua; }
bool is_user_agent_set() const { return !user_agent_.empty(); }
- // URLRequestContextGetter implementation.
+ // net::URLRequestContextGetter implementation.
virtual net::URLRequestContext* GetURLRequestContext();
virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const;
@@ -89,7 +88,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
// User agent to apply to the net::URLRequestContext.
std::string user_agent_;
- scoped_refptr<URLRequestContextGetter> baseline_context_getter_;
+ scoped_refptr<net::URLRequestContextGetter> baseline_context_getter_;
// Lazily initialized by GetURLRequestContext().
scoped_refptr<RequestContext> context_;
@@ -106,6 +105,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
virtual void SetPostPayload(const char* content_type, int content_length,
const char* content);
virtual bool MakeSynchronousPost(int* os_error_code, int* response_code);
+ virtual void Abort();
// WARNING: these response content methods are used to extract plain old data
// and not null terminated strings, so you should make sure you have read
@@ -125,7 +125,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
const std::string& data);
#if defined(UNIT_TEST)
- URLRequestContextGetter* GetRequestContextGetter() const {
+ net::URLRequestContextGetter* GetRequestContextGetter() const {
return context_getter_for_request_;
}
#endif
@@ -150,13 +150,6 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
// RequestContext definition for details.
scoped_refptr<RequestContextGetter> context_getter_for_request_;
- // Our hook into the network layer is a URLFetcher. USED ONLY ON THE IO LOOP,
- // so we can block created_on_loop_ while the fetch is in progress.
- // NOTE: This is not a scoped_ptr for a reason. It must be deleted on the same
- // thread that created it, which isn't the same thread |this| gets deleted on.
- // We must manually delete url_poster_ on the IO loop.
- URLFetcher* url_poster_;
-
// The message loop of the thread we were created on. This is the thread that
// will block on MakeSynchronousPost while the IO thread fetches data from
// the network.
@@ -171,39 +164,64 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
std::string content_type_;
std::string request_content_;
std::string extra_headers_;
- scoped_refptr<net::HttpResponseHeaders> response_headers_;
-
- // Cached response data.
- bool request_completed_;
- bool request_succeeded_;
- int http_response_code_;
- int os_error_code_;
- std::string response_content_;
// A waitable event we use to provide blocking semantics to
// MakeSynchronousPost. We block created_on_loop_ while the IO loop fetches
// network request.
base::WaitableEvent http_post_completed_;
+ struct URLFetchState {
+ URLFetchState();
+ ~URLFetchState();
+ // Our hook into the network layer is a URLFetcher. USED ONLY ON THE IO
+ // LOOP, so we can block created_on_loop_ while the fetch is in progress.
+ // NOTE: This is not a scoped_ptr for a reason. It must be deleted on the
+ // same thread that created it, which isn't the same thread |this| gets
+ // deleted on. We must manually delete url_poster_ on the IO loop.
+ URLFetcher* url_poster;
+
+ // Used to support 'Abort' functionality.
+ bool aborted;
+
+ // Cached response data.
+ bool request_completed;
+ bool request_succeeded;
+ int http_response_code;
+ int os_error_code;
+ std::string response_content;
+ scoped_refptr<net::HttpResponseHeaders> response_headers;
+ };
+
+ // This lock synchronizes use of state involved in the flow to fetch a URL
+ // using URLFetcher. Because we can Abort() from any thread, for example,
+ // this flow needs to be synchronized to gracefully clean up URLFetcher and
+ // return appropriate values in os_error_code.
+ mutable base::Lock fetch_state_lock_;
+ URLFetchState fetch_state_;
+
DISALLOW_COPY_AND_ASSIGN(HttpBridge);
};
-class HttpBridgeFactory
- : public sync_api::HttpPostProviderFactory {
+class HttpBridgeFactory : public sync_api::HttpPostProviderFactory {
public:
- explicit HttpBridgeFactory(URLRequestContextGetter* baseline_context_getter);
+ explicit HttpBridgeFactory(
+ net::URLRequestContextGetter* baseline_context_getter);
virtual ~HttpBridgeFactory();
- virtual sync_api::HttpPostProviderInterface* Create();
- virtual void Destroy(sync_api::HttpPostProviderInterface* http);
+
+ // sync_api::HttpPostProviderFactory:
+ virtual sync_api::HttpPostProviderInterface* Create() OVERRIDE;
+ virtual void Destroy(sync_api::HttpPostProviderInterface* http) OVERRIDE;
+
private:
// This request context is built on top of the baseline context and shares
// common components.
HttpBridge::RequestContextGetter* GetRequestContextGetter();
+
scoped_refptr<HttpBridge::RequestContextGetter> request_context_getter_;
+
DISALLOW_COPY_AND_ASSIGN(HttpBridgeFactory);
};
} // namespace browser_sync
#endif // CHROME_BROWSER_SYNC_GLUE_HTTP_BRIDGE_H_
-
diff --git a/chrome/browser/sync/glue/http_bridge_unittest.cc b/chrome/browser/sync/glue/http_bridge_unittest.cc
index 4104d2f..995c53b 100644
--- a/chrome/browser/sync/glue/http_bridge_unittest.cc
+++ b/chrome/browser/sync/glue/http_bridge_unittest.cc
@@ -51,6 +51,10 @@ class HttpBridgeTest : public testing::Test {
return bridge;
}
+ static void Abort(HttpBridge* bridge) {
+ bridge->Abort();
+ }
+
static void TestSameHttpNetworkSession(MessageLoop* main_message_loop,
HttpBridgeTest* test) {
scoped_refptr<HttpBridge> http_bridge(test->BuildBridge());
@@ -97,14 +101,19 @@ class DummyURLFetcher : public TestURLFetcher {
// back with dummy response info.
class ShuntedHttpBridge : public HttpBridge {
public:
- ShuntedHttpBridge(URLRequestContextGetter* baseline_context_getter,
- HttpBridgeTest* test)
+ // If |never_finishes| is true, the simulated request never actually
+ // returns.
+ ShuntedHttpBridge(net::URLRequestContextGetter* baseline_context_getter,
+ HttpBridgeTest* test, bool never_finishes)
: HttpBridge(new HttpBridge::RequestContextGetter(
baseline_context_getter)),
- test_(test) { }
+ test_(test), never_finishes_(never_finishes) { }
protected:
virtual void MakeAsynchronousPost() {
ASSERT_TRUE(MessageLoop::current() == test_->io_thread_loop());
+ if (never_finishes_)
+ return;
+
// We don't actually want to make a request for this test, so just callback
// as if it completed.
test_->io_thread_loop()->PostTask(FROM_HERE,
@@ -125,6 +134,7 @@ class ShuntedHttpBridge : public HttpBridge {
200, cookies, response_content);
}
HttpBridgeTest* test_;
+ bool never_finishes_;
};
TEST_F(HttpBridgeTest, TestUsesSameHttpNetworkSession) {
@@ -139,10 +149,10 @@ TEST_F(HttpBridgeTest, TestUsesSameHttpNetworkSession) {
// Test the HttpBridge without actually making any network requests.
TEST_F(HttpBridgeTest, TestMakeSynchronousPostShunted) {
- scoped_refptr<URLRequestContextGetter> ctx_getter(
+ scoped_refptr<net::URLRequestContextGetter> ctx_getter(
new TestURLRequestContextGetter());
scoped_refptr<HttpBridge> http_bridge(new ShuntedHttpBridge(
- ctx_getter, this));
+ ctx_getter, this, false));
http_bridge->SetUserAgent("bob");
http_bridge->SetURL("http://www.google.com", 9999);
http_bridge->SetPostPayload("text/plain", 2, " ");
@@ -261,3 +271,40 @@ TEST_F(HttpBridgeTest, TestResponseHeader) {
EXPECT_EQ(http_bridge->GetResponseHeaderValue("Content-type"), "text/html");
EXPECT_TRUE(http_bridge->GetResponseHeaderValue("invalid-header").empty());
}
+
+TEST_F(HttpBridgeTest, Abort) {
+ scoped_refptr<net::URLRequestContextGetter> ctx_getter(
+ new TestURLRequestContextGetter());
+ scoped_refptr<ShuntedHttpBridge> http_bridge(new ShuntedHttpBridge(
+ ctx_getter, this, true));
+ http_bridge->SetUserAgent("bob");
+ http_bridge->SetURL("http://www.google.com", 9999);
+ http_bridge->SetPostPayload("text/plain", 2, " ");
+
+ int os_error = 0;
+ int response_code = 0;
+
+ BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableFunction(
+ &HttpBridgeTest::Abort, http_bridge));
+ bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
+ EXPECT_FALSE(success);
+ EXPECT_EQ(net::ERR_ABORTED, os_error);
+}
+
+TEST_F(HttpBridgeTest, AbortLate) {
+ scoped_refptr<net::URLRequestContextGetter> ctx_getter(
+ new TestURLRequestContextGetter());
+ scoped_refptr<ShuntedHttpBridge> http_bridge(new ShuntedHttpBridge(
+ ctx_getter, this, false));
+ http_bridge->SetUserAgent("bob");
+ http_bridge->SetURL("http://www.google.com", 9999);
+ http_bridge->SetPostPayload("text/plain", 2, " ");
+
+ int os_error = 0;
+ int response_code = 0;
+
+ bool success = http_bridge->MakeSynchronousPost(&os_error, &response_code);
+ ASSERT_TRUE(success);
+ http_bridge->Abort();
+ // Ensures no double-free of URLFetcher, etc.
+}
diff --git a/chrome/browser/sync/glue/model_associator.h b/chrome/browser/sync/glue/model_associator.h
index 7d8f985..eaa2a45 100644
--- a/chrome/browser/sync/glue/model_associator.h
+++ b/chrome/browser/sync/glue/model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -43,6 +43,12 @@ class AssociatorInterface {
// method is only implemented for model associators that are invoked
// off the main thread.
virtual void AbortAssociation() = 0;
+
+ // Returns whether the datatype is ready for encryption/decryption if the
+ // sync service requires it.
+ // TODO(zea): This should be implemented automatically for each datatype, see
+ // http://crbug.com/76232.
+ virtual bool CryptoReadyIfNecessary() = 0;
};
// In addition to the generic methods, association can refer to operations
diff --git a/chrome/browser/sync/glue/model_associator_mock.h b/chrome/browser/sync/glue/model_associator_mock.h
index a949932..51cb7c4 100644
--- a/chrome/browser/sync/glue/model_associator_mock.h
+++ b/chrome/browser/sync/glue/model_associator_mock.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -20,6 +20,7 @@ class ModelAssociatorMock : public AssociatorInterface {
MOCK_METHOD0(DisassociateModels, bool());
MOCK_METHOD1(SyncModelHasUserCreatedNodes, bool(bool* has_nodes));
MOCK_METHOD0(AbortAssociation, void());
+ MOCK_METHOD0(CryptoReadyIfNecessary, bool());
};
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/password_change_processor.cc b/chrome/browser/sync/glue/password_change_processor.cc
index 30d77fa..de47d0a 100644
--- a/chrome/browser/sync/glue/password_change_processor.cc
+++ b/chrome/browser/sync/glue/password_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -14,9 +14,9 @@
#include "chrome/browser/sync/glue/password_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
#include "webkit/glue/password_form.h"
namespace browser_sync {
@@ -130,7 +130,6 @@ void PasswordChangeProcessor::ApplyChangesFromSyncModel(
DCHECK(expected_loop_ == MessageLoop::current());
if (!running())
return;
- StopObserving();
sync_api::ReadNode password_root(trans);
if (!password_root.InitByTagLookup(kPasswordTag)) {
@@ -139,9 +138,8 @@ void PasswordChangeProcessor::ApplyChangesFromSyncModel(
return;
}
- PasswordModelAssociator::PasswordVector new_passwords;
- PasswordModelAssociator::PasswordVector updated_passwords;
- PasswordModelAssociator::PasswordVector deleted_passwords;
+ DCHECK(deleted_passwords_.empty() && new_passwords_.empty() &&
+ updated_passwords_.empty());
for (int i = 0; i < change_count; ++i) {
if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE ==
@@ -154,7 +152,7 @@ void PasswordChangeProcessor::ApplyChangesFromSyncModel(
const sync_pb::PasswordSpecificsData& password = extra->unencrypted();
webkit_glue::PasswordForm form;
PasswordModelAssociator::CopyPassword(password, &form);
- deleted_passwords.push_back(form);
+ deleted_passwords_.push_back(form);
model_associator_->Disassociate(changes[i].id);
continue;
}
@@ -178,21 +176,32 @@ void PasswordChangeProcessor::ApplyChangesFromSyncModel(
if (sync_api::SyncManager::ChangeRecord::ACTION_ADD == changes[i].action) {
std::string tag(PasswordModelAssociator::MakeTag(password));
model_associator_->Associate(&tag, sync_node.GetId());
- new_passwords.push_back(password);
+ new_passwords_.push_back(password);
} else {
DCHECK(sync_api::SyncManager::ChangeRecord::ACTION_UPDATE ==
changes[i].action);
- updated_passwords.push_back(password);
+ updated_passwords_.push_back(password);
}
}
+}
+
+void PasswordChangeProcessor::CommitChangesFromSyncModel() {
+ DCHECK(expected_loop_ == MessageLoop::current());
+ if (!running())
+ return;
+ StopObserving();
- if (!model_associator_->WriteToPasswordStore(&new_passwords,
- &updated_passwords,
- &deleted_passwords)) {
+ if (!model_associator_->WriteToPasswordStore(&new_passwords_,
+ &updated_passwords_,
+ &deleted_passwords_)) {
error_handler()->OnUnrecoverableError(FROM_HERE, "Error writing passwords");
return;
}
+ deleted_passwords_.clear();
+ new_passwords_.clear();
+ updated_passwords_.clear();
+
StartObserving();
}
diff --git a/chrome/browser/sync/glue/password_change_processor.h b/chrome/browser/sync/glue/password_change_processor.h
index 99955bf..155a1b3 100644
--- a/chrome/browser/sync/glue/password_change_processor.h
+++ b/chrome/browser/sync/glue/password_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,10 +9,12 @@
#include "chrome/browser/sync/glue/change_processor.h"
#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chrome/browser/sync/glue/password_model_associator.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
class PasswordStore;
class MessageLoop;
@@ -20,7 +22,6 @@ class NotificationService;
namespace browser_sync {
-class PasswordModelAssociator;
class UnrecoverableErrorHandler;
// This class is responsible for taking changes from the password backend and
@@ -39,17 +40,23 @@ class PasswordChangeProcessor : public ChangeProcessor,
// Passwords -> sync_api model change application.
virtual void Observe(NotificationType type,
const NotificationSource& source,
- const NotificationDetails& details);
+ const NotificationDetails& details) OVERRIDE;
// sync_api model -> WebDataService change application.
virtual void ApplyChangesFromSyncModel(
const sync_api::BaseTransaction* trans,
const sync_api::SyncManager::ChangeRecord* changes,
- int change_count);
+ int change_count) OVERRIDE;
+
+ // Commit changes buffered during ApplyChanges. We must commit them to the
+ // password store only after the sync_api transaction is released, else there
+ // is risk of deadlock due to the password store posting tasks to the UI
+ // thread (http://crbug.com/70658).
+ virtual void CommitChangesFromSyncModel() OVERRIDE;
protected:
- virtual void StartImpl(Profile* profile);
- virtual void StopImpl();
+ virtual void StartImpl(Profile* profile) OVERRIDE;
+ virtual void StopImpl() OVERRIDE;
private:
void StartObserving();
@@ -63,6 +70,12 @@ class PasswordChangeProcessor : public ChangeProcessor,
// holding a reference.
PasswordStore* password_store_;
+ // Buffers used between ApplyChangesFromSyncModel and
+ // CommitChangesFromSyncModel.
+ PasswordModelAssociator::PasswordVector new_passwords_;
+ PasswordModelAssociator::PasswordVector updated_passwords_;
+ PasswordModelAssociator::PasswordVector deleted_passwords_;
+
NotificationRegistrar notification_registrar_;
bool observing_;
diff --git a/chrome/browser/sync/glue/password_data_type_controller.cc b/chrome/browser/sync/glue/password_data_type_controller.cc
index 77c09da..2589861 100644
--- a/chrome/browser/sync/glue/password_data_type_controller.cc
+++ b/chrome/browser/sync/glue/password_data_type_controller.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -25,7 +25,10 @@ PasswordDataTypeController::PasswordDataTypeController(
: profile_sync_factory_(profile_sync_factory),
profile_(profile),
sync_service_(sync_service),
- state_(NOT_RUNNING) {
+ state_(NOT_RUNNING),
+ abort_association_(false),
+ abort_association_complete_(false, false),
+ datatype_stopped_(false, false) {
DCHECK(profile_sync_factory);
DCHECK(profile);
DCHECK(sync_service);
@@ -38,7 +41,7 @@ void PasswordDataTypeController::Start(StartCallback* start_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(start_callback);
if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
+ start_callback->Run(BUSY, FROM_HERE);
delete start_callback;
return;
}
@@ -46,71 +49,104 @@ void PasswordDataTypeController::Start(StartCallback* start_callback) {
password_store_ = profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS);
if (!password_store_.get()) {
LOG(ERROR) << "PasswordStore not initialized, password datatype controller"
- << " aborting.";
+ << " aborting.";
state_ = NOT_RUNNING;
- start_callback->Run(ABORTED);
- delete start_callback;
- return;
- }
-
- if (!sync_service_->IsCryptographerReady()) {
- start_callback->Run(NEEDS_CRYPTO);
+ start_callback->Run(ABORTED, FROM_HERE);
delete start_callback;
return;
}
start_callback_.reset(start_callback);
+ abort_association_ = false;
set_state(ASSOCIATING);
password_store_->ScheduleTask(
NewRunnableMethod(this, &PasswordDataTypeController::StartImpl));
}
+// TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
+// distinguishing chrome shutdown from sync shutdown, we should be able to avoid
+// this (http://crbug.com/55662). Further, all this functionality should be
+// abstracted to a higher layer, where we could ensure all datatypes are doing
+// the same thing (http://crbug.com/76232).
void PasswordDataTypeController::Stop() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // If Stop() is called while Start() is waiting for association to
+ // complete, we need to abort the association and wait for the PASSWORD
+ // thread to finish the StartImpl() task.
+ if (state_ == ASSOCIATING) {
+ {
+ base::AutoLock lock(abort_association_lock_);
+ abort_association_ = true;
+ if (model_associator_.get())
+ model_associator_->AbortAssociation();
+ }
+ // Wait for the model association to abort.
+ abort_association_complete_.Wait();
+ StartDoneImpl(ABORTED, STOPPING);
+ }
+
+ // If Stop() is called while Start() is waiting for another service to load,
+ // abort the start.
+ if (state_ == MODEL_STARTING)
+ StartDoneImpl(ABORTED, STOPPING);
+
+ DCHECK(!start_callback_.get());
+
if (change_processor_ != NULL)
sync_service_->DeactivateDataType(this, change_processor_.get());
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
set_state(NOT_RUNNING);
DCHECK(password_store_.get());
password_store_->ScheduleTask(
NewRunnableMethod(this, &PasswordDataTypeController::StopImpl));
+ datatype_stopped_.Wait();
}
bool PasswordDataTypeController::enabled() {
return true;
}
-syncable::ModelType PasswordDataTypeController::type() {
+syncable::ModelType PasswordDataTypeController::type() const {
return syncable::PASSWORDS;
}
-browser_sync::ModelSafeGroup PasswordDataTypeController::model_safe_group() {
+browser_sync::ModelSafeGroup PasswordDataTypeController::model_safe_group()
+ const {
return browser_sync::GROUP_PASSWORD;
}
-const char* PasswordDataTypeController::name() const {
+std::string PasswordDataTypeController::name() const {
// For logging only.
return "password";
}
-DataTypeController::State PasswordDataTypeController::state() {
+DataTypeController::State PasswordDataTypeController::state() const {
return state_;
}
void PasswordDataTypeController::StartImpl() {
// No additional services need to be started before we can proceed
// with model association.
- ProfileSyncFactory::SyncComponents sync_components =
- profile_sync_factory_->CreatePasswordSyncComponents(
- sync_service_,
- password_store_.get(),
- this);
- model_associator_.reset(sync_components.model_associator);
- change_processor_.reset(sync_components.change_processor);
+ {
+ base::AutoLock lock(abort_association_lock_);
+ if (abort_association_) {
+ abort_association_complete_.Signal();
+ return;
+ }
+ ProfileSyncFactory::SyncComponents sync_components =
+ profile_sync_factory_->CreatePasswordSyncComponents(
+ sync_service_,
+ password_store_.get(),
+ this);
+ model_associator_.reset(sync_components.model_associator);
+ change_processor_.reset(sync_components.change_processor);
+ }
+
+ if (!model_associator_->CryptoReadyIfNecessary()) {
+ StartFailed(NEEDS_CRYPTO);
+ return;
+ }
bool sync_has_nodes = false;
if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
@@ -134,12 +170,16 @@ void PasswordDataTypeController::StartImpl() {
void PasswordDataTypeController::StartDone(
DataTypeController::StartResult result,
DataTypeController::State new_state) {
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(
- this,
- &PasswordDataTypeController::StartDoneImpl,
- result,
- new_state));
+ abort_association_complete_.Signal();
+ base::AutoLock lock(abort_association_lock_);
+ if (!abort_association_) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &PasswordDataTypeController::StartDoneImpl,
+ result,
+ new_state));
+ }
}
void PasswordDataTypeController::StartDoneImpl(
@@ -147,15 +187,18 @@ void PasswordDataTypeController::StartDoneImpl(
DataTypeController::State new_state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
set_state(new_state);
- start_callback_->Run(result);
+ start_callback_->Run(result, FROM_HERE);
start_callback_.reset();
}
void PasswordDataTypeController::StopImpl() {
+ if (model_associator_ != NULL)
+ model_associator_->DisassociateModels();
+
change_processor_.reset();
model_associator_.reset();
- state_ = NOT_RUNNING;
+ datatype_stopped_.Signal();
}
void PasswordDataTypeController::StartFailed(StartResult result) {
diff --git a/chrome/browser/sync/glue/password_data_type_controller.h b/chrome/browser/sync/glue/password_data_type_controller.h
index 56fb55b..1a3a9e4 100644
--- a/chrome/browser/sync/glue/password_data_type_controller.h
+++ b/chrome/browser/sync/glue/password_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,7 +9,8 @@
#include <string>
#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
@@ -40,13 +41,13 @@ class PasswordDataTypeController : public DataTypeController {
virtual bool enabled();
- virtual syncable::ModelType type();
+ virtual syncable::ModelType type() const;
- virtual browser_sync::ModelSafeGroup model_safe_group();
+ virtual browser_sync::ModelSafeGroup model_safe_group() const;
- virtual const char* name() const;
+ virtual std::string name() const;
- virtual State state();
+ virtual State state() const;
// UnrecoverableHandler implementation
virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
@@ -76,6 +77,14 @@ class PasswordDataTypeController : public DataTypeController {
scoped_ptr<StartCallback> start_callback_;
scoped_refptr<PasswordStore> password_store_;
+ base::Lock abort_association_lock_;
+ bool abort_association_;
+ base::WaitableEvent abort_association_complete_;
+
+ // Barrier to ensure that the datatype has been stopped on the DB thread
+ // from the UI thread.
+ base::WaitableEvent datatype_stopped_;
+
DISALLOW_COPY_AND_ASSIGN(PasswordDataTypeController);
};
diff --git a/chrome/browser/sync/glue/password_model_associator.cc b/chrome/browser/sync/glue/password_model_associator.cc
index 3ed5946..baaffde 100644
--- a/chrome/browser/sync/glue/password_model_associator.cc
+++ b/chrome/browser/sync/glue/password_model_associator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -45,14 +45,9 @@ bool PasswordModelAssociator::AssociateModels() {
abort_association_pending_ = false;
}
- sync_api::WriteTransaction trans(sync_service_->GetUserShare());
- sync_api::ReadNode password_root(&trans);
- if (!password_root.InitByTagLookup(kPasswordTag)) {
- LOG(ERROR) << "Server did not create the top-level password node. We "
- << "might be running against an out-of-date server.";
- return false;
- }
-
+ // We must not be holding a transaction when we interact with the password
+ // store, as it can post tasks to the UI thread which can itself be blocked
+ // on our transaction, resulting in deadlock. (http://crbug.com/70658)
std::vector<webkit_glue::PasswordForm*> passwords;
if (!password_store_->FillAutofillableLogins(&passwords) ||
!password_store_->FillBlacklistLogins(&passwords)) {
@@ -64,76 +59,89 @@ bool PasswordModelAssociator::AssociateModels() {
std::set<std::string> current_passwords;
PasswordVector new_passwords;
PasswordVector updated_passwords;
-
- for (std::vector<webkit_glue::PasswordForm*>::iterator ix = passwords.begin();
- ix != passwords.end(); ++ix) {
- if (IsAbortPending())
+ {
+ sync_api::WriteTransaction trans(sync_service_->GetUserShare());
+ sync_api::ReadNode password_root(&trans);
+ if (!password_root.InitByTagLookup(kPasswordTag)) {
+ LOG(ERROR) << "Server did not create the top-level password node. We "
+ << "might be running against an out-of-date server.";
return false;
- std::string tag = MakeTag(**ix);
-
- sync_api::ReadNode node(&trans);
- if (node.InitByClientTagLookup(syncable::PASSWORDS, tag)) {
- const sync_pb::PasswordSpecificsData& password =
- node.GetPasswordSpecifics();
- DCHECK_EQ(tag, MakeTag(password));
+ }
- webkit_glue::PasswordForm new_password;
+ for (std::vector<webkit_glue::PasswordForm*>::iterator ix =
+ passwords.begin();
+ ix != passwords.end(); ++ix) {
+ if (IsAbortPending())
+ return false;
+ std::string tag = MakeTag(**ix);
+
+ sync_api::ReadNode node(&trans);
+ if (node.InitByClientTagLookup(syncable::PASSWORDS, tag)) {
+ const sync_pb::PasswordSpecificsData& password =
+ node.GetPasswordSpecifics();
+ DCHECK_EQ(tag, MakeTag(password));
+
+ webkit_glue::PasswordForm new_password;
+
+ if (MergePasswords(password, **ix, &new_password)) {
+ sync_api::WriteNode write_node(&trans);
+ if (!write_node.InitByClientTagLookup(syncable::PASSWORDS, tag)) {
+ STLDeleteElements(&passwords);
+ LOG(ERROR) << "Failed to edit password sync node.";
+ return false;
+ }
+ WriteToSyncNode(new_password, &write_node);
+ updated_passwords.push_back(new_password);
+ }
- if (MergePasswords(password, **ix, &new_password)) {
- sync_api::WriteNode write_node(&trans);
- if (!write_node.InitByClientTagLookup(syncable::PASSWORDS, tag)) {
+ Associate(&tag, node.GetId());
+ } else {
+ sync_api::WriteNode node(&trans);
+ if (!node.InitUniqueByCreation(syncable::PASSWORDS,
+ password_root, tag)) {
STLDeleteElements(&passwords);
- LOG(ERROR) << "Failed to edit password sync node.";
+ LOG(ERROR) << "Failed to create password sync node.";
return false;
}
- WriteToSyncNode(new_password, &write_node);
- updated_passwords.push_back(new_password);
- }
- Associate(&tag, node.GetId());
- } else {
- sync_api::WriteNode node(&trans);
- if (!node.InitUniqueByCreation(syncable::PASSWORDS,
- password_root, tag)) {
- STLDeleteElements(&passwords);
- LOG(ERROR) << "Failed to create password sync node.";
- return false;
- }
+ WriteToSyncNode(**ix, &node);
- WriteToSyncNode(**ix, &node);
+ Associate(&tag, node.GetId());
+ }
- Associate(&tag, node.GetId());
+ current_passwords.insert(tag);
}
- current_passwords.insert(tag);
- }
+ STLDeleteElements(&passwords);
- STLDeleteElements(&passwords);
+ int64 sync_child_id = password_root.GetFirstChildId();
+ while (sync_child_id != sync_api::kInvalidId) {
+ sync_api::ReadNode sync_child_node(&trans);
+ if (!sync_child_node.InitByIdLookup(sync_child_id)) {
+ LOG(ERROR) << "Failed to fetch child node.";
+ return false;
+ }
+ const sync_pb::PasswordSpecificsData& password =
+ sync_child_node.GetPasswordSpecifics();
+ std::string tag = MakeTag(password);
- int64 sync_child_id = password_root.GetFirstChildId();
- while (sync_child_id != sync_api::kInvalidId) {
- sync_api::ReadNode sync_child_node(&trans);
- if (!sync_child_node.InitByIdLookup(sync_child_id)) {
- LOG(ERROR) << "Failed to fetch child node.";
- return false;
- }
- const sync_pb::PasswordSpecificsData& password =
- sync_child_node.GetPasswordSpecifics();
- std::string tag = MakeTag(password);
-
- // The password only exists on the server. Add it to the local
- // model.
- if (current_passwords.find(tag) == current_passwords.end()) {
- webkit_glue::PasswordForm new_password;
-
- CopyPassword(password, &new_password);
- Associate(&tag, sync_child_node.GetId());
- new_passwords.push_back(new_password);
- }
+ // The password only exists on the server. Add it to the local
+ // model.
+ if (current_passwords.find(tag) == current_passwords.end()) {
+ webkit_glue::PasswordForm new_password;
- sync_child_id = sync_child_node.GetSuccessorId();
+ CopyPassword(password, &new_password);
+ Associate(&tag, sync_child_node.GetId());
+ new_passwords.push_back(new_password);
+ }
+
+ sync_child_id = sync_child_node.GetSuccessorId();
+ }
}
+ // We must not be holding a transaction when we interact with the password
+ // store, as it can post tasks to the UI thread which can itself be blocked
+ // on our transaction, resulting in deadlock. (http://crbug.com/70658)
if (!WriteToPasswordStore(&new_passwords, &updated_passwords, NULL)) {
LOG(ERROR) << "Failed to write passwords.";
return false;
@@ -196,6 +204,13 @@ void PasswordModelAssociator::AbortAssociation() {
abort_association_pending_ = true;
}
+bool PasswordModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ // We always encrypt passwords, so no need to check if encryption is enabled.
+ return sync_service_->IsCryptographerReady(&trans);
+}
+
const std::string* PasswordModelAssociator::GetChromeNodeFromSyncId(
int64 sync_id) {
return NULL;
@@ -271,6 +286,12 @@ bool PasswordModelAssociator::WriteToPasswordStore(
password_store_->RemoveLoginImpl(*password);
}
}
+
+ if (new_passwords || updated_passwords || deleted_passwords) {
+ // We have to notify password store observers of the change by hand since
+ // we use internal password store interfaces to make changes synchronously.
+ password_store_->PostNotifyLoginsChanged();
+ }
return true;
}
diff --git a/chrome/browser/sync/glue/password_model_associator.h b/chrome/browser/sync/glue/password_model_associator.h
index 449bde7..0b3bac5 100644
--- a/chrome/browser/sync/glue/password_model_associator.h
+++ b/chrome/browser/sync/glue/password_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -71,6 +71,9 @@ class PasswordModelAssociator
// See ModelAssociator interface.
virtual void AbortAssociation();
+ // See ModelAssociator interface.
+ virtual bool CryptoReadyIfNecessary();
+
// Not implemented.
virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id);
diff --git a/chrome/browser/sync/glue/password_model_worker.cc b/chrome/browser/sync/glue/password_model_worker.cc
index 6132a03..1c208f5 100644
--- a/chrome/browser/sync/glue/password_model_worker.cc
+++ b/chrome/browser/sync/glue/password_model_worker.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/password_model_worker.h"
#include "base/callback.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "base/task.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/password_manager/password_store.h"
diff --git a/chrome/browser/sync/glue/password_model_worker.h b/chrome/browser/sync/glue/password_model_worker.h
index 7ef537a..6c3f4b0 100644
--- a/chrome/browser/sync/glue/password_model_worker.h
+++ b/chrome/browser/sync/glue/password_model_worker.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,7 +10,7 @@
#include "base/basictypes.h"
#include "base/callback.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
class PasswordStore;
diff --git a/chrome/browser/sync/glue/preference_change_processor.cc b/chrome/browser/sync/glue/preference_change_processor.cc
index b8f0d20..6a6840b 100644
--- a/chrome/browser/sync/glue/preference_change_processor.cc
+++ b/chrome/browser/sync/glue/preference_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -14,11 +14,11 @@
#include "chrome/browser/sync/glue/preference_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/preference_specifics.pb.h"
-#include "chrome/common/json_value_serializer.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_source.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
+#include "content/common/json_value_serializer.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
namespace browser_sync {
@@ -143,7 +143,7 @@ void PreferenceChangeProcessor::ApplyChangesFromSyncModel(
// It is possible that we may receive a change to a preference we
// do not want to sync. For example if the user is syncing a Mac
// client and a Windows client, the Windows client does not
- // support kShowPageOptionsButtons. Ignore updates from these
+ // support kConfirmToQuitEnabled. Ignore updates from these
// preferences.
const char* pref_name = name.c_str();
if (model_associator_->synced_preferences().count(pref_name) == 0)
diff --git a/chrome/browser/sync/glue/preference_change_processor.h b/chrome/browser/sync/glue/preference_change_processor.h
index 1ef88da..8111775 100644
--- a/chrome/browser/sync/glue/preference_change_processor.h
+++ b/chrome/browser/sync/glue/preference_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,7 +13,7 @@
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/common/notification_observer.h"
+#include "content/common/notification_observer.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/glue/preference_data_type_controller.cc b/chrome/browser/sync/glue/preference_data_type_controller.cc
index f1ddd51..3331bd3 100644
--- a/chrome/browser/sync/glue/preference_data_type_controller.cc
+++ b/chrome/browser/sync/glue/preference_data_type_controller.cc
@@ -1,132 +1,47 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/preference_data_type_controller.h"
-#include "base/logging.h"
#include "base/metrics/histogram.h"
-#include "base/time.h"
-#include "chrome/browser/sync/glue/preference_change_processor.h"
-#include "chrome/browser/sync/glue/preference_model_associator.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/browser/sync/unrecoverable_error_handler.h"
-#include "content/browser/browser_thread.h"
namespace browser_sync {
PreferenceDataTypeController::PreferenceDataTypeController(
ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
ProfileSyncService* sync_service)
- : profile_sync_factory_(profile_sync_factory),
- sync_service_(sync_service),
- state_(NOT_RUNNING) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(profile_sync_factory);
- DCHECK(sync_service);
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
}
-PreferenceDataTypeController::~PreferenceDataTypeController() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-}
-
-void PreferenceDataTypeController::Start(StartCallback* start_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(start_callback);
- if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
- delete start_callback;
- return;
- }
-
- start_callback_.reset(start_callback);
-
- // No additional services need to be started before we can proceed
- // with model association.
- ProfileSyncFactory::SyncComponents sync_components =
- profile_sync_factory_->CreatePreferenceSyncComponents(sync_service_,
- this);
- model_associator_.reset(sync_components.model_associator);
- change_processor_.reset(sync_components.change_processor);
-
- bool sync_has_nodes = false;
- if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
- StartFailed(UNRECOVERABLE_ERROR);
- return;
- }
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- bool merge_success = model_associator_->AssociateModels();
- UMA_HISTOGRAM_TIMES("Sync.PreferenceAssociationTime",
- base::TimeTicks::Now() - start_time);
- if (!merge_success) {
- StartFailed(ASSOCIATION_FAILED);
- return;
- }
-
- sync_service_->ActivateDataType(this, change_processor_.get());
- state_ = RUNNING;
- FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK);
-}
-
-void PreferenceDataTypeController::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (change_processor_ != NULL)
- sync_service_->DeactivateDataType(this, change_processor_.get());
+PreferenceDataTypeController::~PreferenceDataTypeController() {}
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
- change_processor_.reset();
- model_associator_.reset();
- start_callback_.reset();
-
- state_ = NOT_RUNNING;
-}
-
-bool PreferenceDataTypeController::enabled() {
- return true;
-}
-
-syncable::ModelType PreferenceDataTypeController::type() {
+syncable::ModelType PreferenceDataTypeController::type() const {
return syncable::PREFERENCES;
}
-browser_sync::ModelSafeGroup PreferenceDataTypeController::model_safe_group() {
- return browser_sync::GROUP_UI;
-}
-
-const char* PreferenceDataTypeController::name() const {
- // For logging only.
- return "preference";
-}
-
-DataTypeController::State PreferenceDataTypeController::state() {
- return state_;
+void PreferenceDataTypeController::CreateSyncComponents() {
+ ProfileSyncFactory::SyncComponents sync_components = profile_sync_factory_->
+ CreatePreferenceSyncComponents(sync_service_, this);
+ model_associator_.reset(sync_components.model_associator);
+ change_processor_.reset(sync_components.change_processor);
}
-void PreferenceDataTypeController::OnUnrecoverableError(
+void PreferenceDataTypeController::RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
UMA_HISTOGRAM_COUNTS("Sync.PreferenceRunFailures", 1);
- sync_service_->OnUnrecoverableError(from_here, message);
}
-void PreferenceDataTypeController::FinishStart(StartResult result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- start_callback_->Run(result);
- start_callback_.reset();
+void PreferenceDataTypeController::RecordAssociationTime(base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.PreferenceAssociationTime", time);
}
-void PreferenceDataTypeController::StartFailed(StartResult result) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- model_associator_.reset();
- change_processor_.reset();
- start_callback_->Run(result);
- start_callback_.reset();
+void PreferenceDataTypeController::RecordStartFailure(StartResult result) {
UMA_HISTOGRAM_ENUMERATION("Sync.PreferenceStartFailures",
result,
MAX_START_RESULT);
diff --git a/chrome/browser/sync/glue/preference_data_type_controller.h b/chrome/browser/sync/glue/preference_data_type_controller.h
index 4fc177d..3f375b1 100644
--- a/chrome/browser/sync/glue/preference_data_type_controller.h
+++ b/chrome/browser/sync/glue/preference_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,58 +6,31 @@
#define CHROME_BROWSER_SYNC_GLUE_PREFERENCE_DATA_TYPE_CONTROLLER_H__
#pragma once
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
+#include <string>
-class ProfileSyncFactory;
-class ProfileSyncService;
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
namespace browser_sync {
-class AssociatorInterface;
-class ChangeProcessor;
-
-class PreferenceDataTypeController : public DataTypeController {
+class PreferenceDataTypeController : public FrontendDataTypeController {
public:
PreferenceDataTypeController(
ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
ProfileSyncService* sync_service);
virtual ~PreferenceDataTypeController();
- virtual void Start(StartCallback* start_callback);
-
- virtual void Stop();
-
- virtual bool enabled();
-
- virtual syncable::ModelType type();
-
- virtual browser_sync::ModelSafeGroup model_safe_group();
-
- virtual const char* name() const;
-
- virtual State state();
-
- // UnrecoverableErrorHandler interface.
- virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
- const std::string& message);
+ // FrontendDataTypeController implementation.
+ virtual syncable::ModelType type() const;
private:
- // Helper method to run the stashed start callback with a given result.
- void FinishStart(StartResult result);
-
- // Cleans up state and calls callback when start fails.
- void StartFailed(StartResult result);
-
- ProfileSyncFactory* profile_sync_factory_;
- ProfileSyncService* sync_service_;
-
- State state_;
-
- scoped_ptr<StartCallback> start_callback_;
- scoped_ptr<AssociatorInterface> model_associator_;
- scoped_ptr<ChangeProcessor> change_processor_;
+ // FrontendDataTypeController implementations.
+ virtual void CreateSyncComponents();
+ virtual void RecordUnrecoverableError(
+ const tracked_objects::Location& from_here,
+ const std::string& message);
+ virtual void RecordAssociationTime(base::TimeDelta time);
+ virtual void RecordStartFailure(StartResult result);
DISALLOW_COPY_AND_ASSIGN(PreferenceDataTypeController);
};
diff --git a/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc b/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc
index 7103cbf..4df8b3f 100644
--- a/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/preference_data_type_controller_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/task.h"
#include "base/tracked_objects.h"
#include "chrome/browser/sync/glue/preference_data_type_controller.h"
@@ -14,6 +14,7 @@
#include "chrome/browser/sync/glue/model_associator_mock.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
#include "chrome/browser/sync/profile_sync_service_mock.h"
+#include "chrome/test/profile_mock.h"
#include "content/browser/browser_thread.h"
using browser_sync::PreferenceDataTypeController;
@@ -28,7 +29,8 @@ using testing::SetArgumentPointee;
class StartCallback {
public:
- MOCK_METHOD1(Run, void(DataTypeController::StartResult result));
+ MOCK_METHOD2(Run, void(DataTypeController::StartResult result,
+ const tracked_objects::Location& location));
};
class PreferenceDataTypeControllerTest : public testing::Test {
@@ -40,6 +42,7 @@ class PreferenceDataTypeControllerTest : public testing::Test {
profile_sync_factory_.reset(new ProfileSyncFactoryMock());
preference_dtc_ =
new PreferenceDataTypeController(profile_sync_factory_.get(),
+ &profile_,
&service_);
}
@@ -53,6 +56,8 @@ class PreferenceDataTypeControllerTest : public testing::Test {
}
void SetAssociateExpectations() {
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
EXPECT_CALL(*model_associator_, AssociateModels()).
@@ -72,6 +77,7 @@ class PreferenceDataTypeControllerTest : public testing::Test {
BrowserThread ui_thread_;
scoped_refptr<PreferenceDataTypeController> preference_dtc_;
scoped_ptr<ProfileSyncFactoryMock> profile_sync_factory_;
+ ProfileMock profile_;
ProfileSyncServiceMock service_;
ModelAssociatorMock* model_associator_;
ChangeProcessorMock* change_processor_;
@@ -83,7 +89,7 @@ TEST_F(PreferenceDataTypeControllerTest, Start) {
SetAssociateExpectations();
SetActivateExpectations();
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, preference_dtc_->state());
}
@@ -94,7 +100,7 @@ TEST_F(PreferenceDataTypeControllerTest, StartFirstRun) {
SetActivateExpectations();
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -105,7 +111,7 @@ TEST_F(PreferenceDataTypeControllerTest, StartOk) {
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -115,7 +121,7 @@ TEST_F(PreferenceDataTypeControllerTest, StartAssociationFailed) {
EXPECT_CALL(*model_associator_, AssociateModels()).
WillRepeatedly(Return(false));
- EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
}
@@ -124,9 +130,11 @@ TEST_F(PreferenceDataTypeControllerTest,
StartAssociationTriggersUnrecoverableError) {
SetStartExpectations();
// Set up association to fail with an unrecoverable error.
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
}
@@ -139,7 +147,7 @@ TEST_F(PreferenceDataTypeControllerTest, Stop) {
EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, preference_dtc_->state());
preference_dtc_->Stop();
@@ -157,7 +165,7 @@ TEST_F(PreferenceDataTypeControllerTest, OnUnrecoverableError) {
&PreferenceDataTypeController::Stop));
SetStopExpectations();
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
preference_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
// This should cause preference_dtc_->Stop() to be called.
preference_dtc_->OnUnrecoverableError(FROM_HERE, "Test");
diff --git a/chrome/browser/sync/glue/preference_model_associator.cc b/chrome/browser/sync/glue/preference_model_associator.cc
index 63a058c..7d75c38 100644
--- a/chrome/browser/sync/glue/preference_model_associator.cc
+++ b/chrome/browser/sync/glue/preference_model_associator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,18 +6,18 @@
#include "base/json/json_reader.h"
#include "base/logging.h"
-#include "base/values.h"
#include "base/utf_string_conversions.h"
+#include "base/values.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/synchronized_preferences.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/preference_specifics.pb.h"
-#include "chrome/common/json_value_serializer.h"
-#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
+#include "content/common/json_value_serializer.h"
+#include "content/common/notification_service.h"
namespace browser_sync {
@@ -134,6 +134,7 @@ bool PreferenceModelAssociator::AssociateModels() {
it != synced_preferences_.end(); ++it) {
const PrefService::Preference* pref =
pref_service->FindPreference((*it).c_str());
+ DCHECK(pref);
InitPrefNodeAndAssociate(&trans, root, pref);
}
return true;
@@ -271,8 +272,7 @@ Value* PreferenceModelAssociator::MergeListValues(const Value& from_value,
for (ListValue::const_iterator i = from_list_value.begin();
i != from_list_value.end(); ++i) {
Value* value = (*i)->DeepCopy();
- if (!result->AppendIfNotPresent(value))
- delete value;
+ result->AppendIfNotPresent(value);
}
return result;
}
@@ -326,4 +326,13 @@ void PreferenceModelAssociator::AfterUpdateOperations(
}
}
+bool PreferenceModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ syncable::ModelTypeSet encrypted_types;
+ sync_service_->GetEncryptedDataTypes(&encrypted_types);
+ return encrypted_types.count(syncable::PREFERENCES) == 0 ||
+ sync_service_->IsCryptographerReady(&trans);
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/preference_model_associator.h b/chrome/browser/sync/glue/preference_model_associator.h
index 5924e72..5c661b2 100644
--- a/chrome/browser/sync/glue/preference_model_associator.h
+++ b/chrome/browser/sync/glue/preference_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -69,6 +69,9 @@ class PreferenceModelAssociator
// thread.
}
+ // See ModelAssociator interface.
+ virtual bool CryptoReadyIfNecessary();
+
// Not implemented.
virtual const PrefService::Preference* GetChromeNodeFromSyncId(int64 sync_id);
diff --git a/chrome/browser/sync/glue/preference_model_associator_unittest.cc b/chrome/browser/sync/glue/preference_model_associator_unittest.cc
index 394720a..7ed60b0 100644
--- a/chrome/browser/sync/glue/preference_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/preference_model_associator_unittest.cc
@@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/values.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/sync/glue/preference_model_associator.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_profile.h"
@@ -95,35 +96,45 @@ TEST_F(ListPreferenceMergeTest, LocalEmpty) {
TEST_F(ListPreferenceMergeTest, ServerNull) {
scoped_ptr<Value> null_value(Value::CreateNullValue());
- ListValue* local_list_value =
- pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- local_list_value->Append(Value::CreateStringValue(local_url0_));
+ {
+ ListPrefUpdate update(pref_service_, prefs::kURLsToRestoreOnStartup);
+ ListValue* local_list_value = update.Get();
+ local_list_value->Append(Value::CreateStringValue(local_url0_));
+ }
const PrefService::Preference* pref =
pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup);
scoped_ptr<Value> merged_value(
PreferenceModelAssociator::MergePreference(*pref, *null_value));
+ const ListValue* local_list_value =
+ pref_service_->GetList(prefs::kURLsToRestoreOnStartup);
EXPECT_TRUE(merged_value->Equals(local_list_value));
}
TEST_F(ListPreferenceMergeTest, ServerEmpty) {
scoped_ptr<Value> empty_value(new ListValue);
- ListValue* local_list_value =
- pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- local_list_value->Append(Value::CreateStringValue(local_url0_));
+ {
+ ListPrefUpdate update(pref_service_, prefs::kURLsToRestoreOnStartup);
+ ListValue* local_list_value = update.Get();
+ local_list_value->Append(Value::CreateStringValue(local_url0_));
+ }
const PrefService::Preference* pref =
pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup);
scoped_ptr<Value> merged_value(
PreferenceModelAssociator::MergePreference(*pref, *empty_value));
+ const ListValue* local_list_value =
+ pref_service_->GetList(prefs::kURLsToRestoreOnStartup);
EXPECT_TRUE(merged_value->Equals(local_list_value));
}
TEST_F(ListPreferenceMergeTest, Merge) {
- ListValue* local_list_value =
- pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- local_list_value->Append(Value::CreateStringValue(local_url0_));
- local_list_value->Append(Value::CreateStringValue(local_url1_));
+ {
+ ListPrefUpdate update(pref_service_, prefs::kURLsToRestoreOnStartup);
+ ListValue* local_list_value = update.Get();
+ local_list_value->Append(Value::CreateStringValue(local_url0_));
+ local_list_value->Append(Value::CreateStringValue(local_url1_));
+ }
const PrefService::Preference* pref =
pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup);
@@ -139,11 +150,13 @@ TEST_F(ListPreferenceMergeTest, Merge) {
}
TEST_F(ListPreferenceMergeTest, Duplicates) {
- ListValue* local_list_value =
- pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- local_list_value->Append(Value::CreateStringValue(local_url0_));
- local_list_value->Append(Value::CreateStringValue(server_url0_));
- local_list_value->Append(Value::CreateStringValue(server_url1_));
+ {
+ ListPrefUpdate update(pref_service_, prefs::kURLsToRestoreOnStartup);
+ ListValue* local_list_value = update.Get();
+ local_list_value->Append(Value::CreateStringValue(local_url0_));
+ local_list_value->Append(Value::CreateStringValue(server_url0_));
+ local_list_value->Append(Value::CreateStringValue(server_url1_));
+ }
const PrefService::Preference* pref =
pref_service_->FindPreference(prefs::kURLsToRestoreOnStartup);
@@ -158,10 +171,12 @@ TEST_F(ListPreferenceMergeTest, Duplicates) {
}
TEST_F(ListPreferenceMergeTest, Equals) {
- ListValue* local_list_value =
- pref_service_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- local_list_value->Append(Value::CreateStringValue(server_url0_));
- local_list_value->Append(Value::CreateStringValue(server_url1_));
+ {
+ ListPrefUpdate update(pref_service_, prefs::kURLsToRestoreOnStartup);
+ ListValue* local_list_value = update.Get();
+ local_list_value->Append(Value::CreateStringValue(server_url0_));
+ local_list_value->Append(Value::CreateStringValue(server_url1_));
+ }
scoped_ptr<Value> original(server_url_list_.DeepCopy());
const PrefService::Preference* pref =
@@ -206,34 +221,44 @@ TEST_F(DictionaryPreferenceMergeTest, LocalEmpty) {
TEST_F(DictionaryPreferenceMergeTest, ServerNull) {
scoped_ptr<Value> null_value(Value::CreateNullValue());
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns);
- SetContentPattern(local_dict_value, expression2_, content_type0_, 1);
+ {
+ DictionaryPrefUpdate update(pref_service_, prefs::kContentSettingsPatterns);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression2_, content_type0_, 1);
+ }
const PrefService::Preference* pref =
pref_service_->FindPreference(prefs::kContentSettingsPatterns);
scoped_ptr<Value> merged_value(
PreferenceModelAssociator::MergePreference(*pref, *null_value));
+ const DictionaryValue* local_dict_value =
+ pref_service_->GetDictionary(prefs::kContentSettingsPatterns);
EXPECT_TRUE(merged_value->Equals(local_dict_value));
}
TEST_F(DictionaryPreferenceMergeTest, ServerEmpty) {
scoped_ptr<Value> empty_value(new DictionaryValue);
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns);
- SetContentPattern(local_dict_value, expression2_, content_type0_, 1);
+ {
+ DictionaryPrefUpdate update(pref_service_, prefs::kContentSettingsPatterns);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression2_, content_type0_, 1);
+ }
const PrefService::Preference* pref =
pref_service_->FindPreference(prefs::kContentSettingsPatterns);
scoped_ptr<Value> merged_value(
PreferenceModelAssociator::MergePreference(*pref, *empty_value));
+ const DictionaryValue* local_dict_value =
+ pref_service_->GetDictionary(prefs::kContentSettingsPatterns);
EXPECT_TRUE(merged_value->Equals(local_dict_value));
}
TEST_F(DictionaryPreferenceMergeTest, MergeNoConflicts) {
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns);
- SetContentPattern(local_dict_value, expression2_, content_type0_, 1);
+ {
+ DictionaryPrefUpdate update(pref_service_, prefs::kContentSettingsPatterns);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression2_, content_type0_, 1);
+ }
scoped_ptr<Value> merged_value(PreferenceModelAssociator::MergePreference(
*pref_service_->FindPreference(prefs::kContentSettingsPatterns),
@@ -248,12 +273,14 @@ TEST_F(DictionaryPreferenceMergeTest, MergeNoConflicts) {
}
TEST_F(DictionaryPreferenceMergeTest, MergeConflicts) {
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns);
- SetContentPattern(local_dict_value, expression0_, content_type0_, 2);
- SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
- SetContentPattern(local_dict_value, expression1_, content_type1_, 1);
- SetContentPattern(local_dict_value, expression2_, content_type0_, 2);
+ {
+ DictionaryPrefUpdate update(pref_service_, prefs::kContentSettingsPatterns);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression0_, content_type0_, 2);
+ SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ SetContentPattern(local_dict_value, expression1_, content_type1_, 1);
+ SetContentPattern(local_dict_value, expression2_, content_type0_, 2);
+ }
scoped_ptr<Value> merged_value(PreferenceModelAssociator::MergePreference(
*pref_service_->FindPreference(prefs::kContentSettingsPatterns),
@@ -269,11 +296,13 @@ TEST_F(DictionaryPreferenceMergeTest, MergeConflicts) {
}
TEST_F(DictionaryPreferenceMergeTest, Equal) {
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns);
- SetContentPattern(local_dict_value, expression0_, content_type0_, 1);
- SetContentPattern(local_dict_value, expression0_, content_type1_, 2);
- SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ {
+ DictionaryPrefUpdate update(pref_service_, prefs::kContentSettingsPatterns);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression0_, content_type0_, 1);
+ SetContentPattern(local_dict_value, expression0_, content_type1_, 2);
+ SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ }
scoped_ptr<Value> merged_value(PreferenceModelAssociator::MergePreference(
*pref_service_->FindPreference(prefs::kContentSettingsPatterns),
@@ -282,11 +311,13 @@ TEST_F(DictionaryPreferenceMergeTest, Equal) {
}
TEST_F(DictionaryPreferenceMergeTest, ConflictButServerWins) {
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(prefs::kContentSettingsPatterns);
- SetContentPattern(local_dict_value, expression0_, content_type0_, 2);
- SetContentPattern(local_dict_value, expression0_, content_type1_, 2);
- SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ {
+ DictionaryPrefUpdate update(pref_service_, prefs::kContentSettingsPatterns);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression0_, content_type0_, 2);
+ SetContentPattern(local_dict_value, expression0_, content_type1_, 2);
+ SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ }
scoped_ptr<Value> merged_value(PreferenceModelAssociator::MergePreference(
*pref_service_->FindPreference(prefs::kContentSettingsPatterns),
@@ -310,8 +341,11 @@ class IndividualPreferenceMergeTest : public AbstractPreferenceMergeTest {
}
bool MergeListPreference(const char* pref) {
- ListValue* local_list_value = pref_service_->GetMutableList(pref);
- local_list_value->Append(Value::CreateStringValue(url1_));
+ {
+ ListPrefUpdate update(pref_service_, pref);
+ ListValue* local_list_value = update.Get();
+ local_list_value->Append(Value::CreateStringValue(url1_));
+ }
scoped_ptr<Value> merged_value(PreferenceModelAssociator::MergePreference(
*pref_service_->FindPreference(pref),
@@ -324,9 +358,11 @@ class IndividualPreferenceMergeTest : public AbstractPreferenceMergeTest {
}
bool MergeDictionaryPreference(const char* pref) {
- DictionaryValue* local_dict_value =
- pref_service_->GetMutableDictionary(pref);
- SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ {
+ DictionaryPrefUpdate update(pref_service_, pref);
+ DictionaryValue* local_dict_value = update.Get();
+ SetContentPattern(local_dict_value, expression1_, content_type0_, 1);
+ }
scoped_ptr<Value> merged_value(PreferenceModelAssociator::MergePreference(
*pref_service_->FindPreference(pref),
diff --git a/chrome/browser/sync/glue/session_change_processor.cc b/chrome/browser/sync/glue/session_change_processor.cc
index 7bc7a96..8c8a452 100644
--- a/chrome/browser/sync/glue/session_change_processor.cc
+++ b/chrome/browser/sync/glue/session_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,16 +9,17 @@
#include <vector>
#include "base/logging.h"
-#include "base/scoped_vector.h"
+#include "base/memory/scoped_vector.h"
+#include "chrome/browser/extensions/extension_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_source.h"
#include "content/browser/tab_contents/navigation_controller.h"
#include "content/browser/tab_contents/tab_contents.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_source.h"
namespace browser_sync {
@@ -27,7 +28,8 @@ SessionChangeProcessor::SessionChangeProcessor(
SessionModelAssociator* session_model_associator)
: ChangeProcessor(error_handler),
session_model_associator_(session_model_associator),
- profile_(NULL) {
+ profile_(NULL),
+ setup_for_test_(false) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(error_handler);
DCHECK(session_model_associator_);
@@ -124,13 +126,13 @@ void SessionChangeProcessor::Observe(NotificationType type,
}
case NotificationType::TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
- TabContents* tab_contents = Source<TabContents>(source).ptr();
- DCHECK(tab_contents);
- if (tab_contents->profile() != profile_) {
+ ExtensionTabHelper* extension_tab_helper =
+ Source<ExtensionTabHelper>(source).ptr();
+ if (extension_tab_helper->tab_contents()->profile() != profile_) {
return;
}
- if (tab_contents->extension_app()) {
- modified_tabs.push_back(tab_contents);
+ if (extension_tab_helper->extension_app()) {
+ modified_tabs.push_back(extension_tab_helper->tab_contents());
}
break;
}
diff --git a/chrome/browser/sync/glue/session_change_processor.h b/chrome/browser/sync/glue/session_change_processor.h
index b8bcc8f..6213443 100644
--- a/chrome/browser/sync/glue/session_change_processor.h
+++ b/chrome/browser/sync/glue/session_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,9 +11,9 @@
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/change_processor.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_type.h"
-#include "chrome/common/notification_registrar.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
class NotificationDetails;
class NotificationSource;
diff --git a/chrome/browser/sync/glue/session_data_type_controller.cc b/chrome/browser/sync/glue/session_data_type_controller.cc
index c4ca5b8..68186cb 100644
--- a/chrome/browser/sync/glue/session_data_type_controller.cc
+++ b/chrome/browser/sync/glue/session_data_type_controller.cc
@@ -1,131 +1,51 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/session_data_type_controller.h"
#include "base/metrics/histogram.h"
-#include "base/logging.h"
-#include "base/time.h"
-#include "chrome/browser/sync/glue/session_change_processor.h"
-#include "chrome/browser/sync/glue/session_model_associator.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/browser/sync/unrecoverable_error_handler.h"
-#include "content/browser/browser_thread.h"
namespace browser_sync {
SessionDataTypeController::SessionDataTypeController(
ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
ProfileSyncService* sync_service)
- : profile_sync_factory_(profile_sync_factory),
- sync_service_(sync_service),
- state_(NOT_RUNNING) {
- DCHECK(profile_sync_factory);
- DCHECK(sync_service);
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
}
-SessionDataTypeController::~SessionDataTypeController() {
-}
-
-void SessionDataTypeController::Start(StartCallback* start_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(start_callback);
- if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
- delete start_callback;
- return;
- }
-
- start_callback_.reset(start_callback);
-
- ProfileSyncFactory::SyncComponents sync_components =
- profile_sync_factory_->CreateSessionSyncComponents(sync_service_,
- this);
- model_associator_.reset(sync_components.model_associator);
- change_processor_.reset(sync_components.change_processor);
-
- bool sync_has_nodes = false;
- if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
- StartFailed(UNRECOVERABLE_ERROR);
- return;
- }
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- bool success = model_associator_->AssociateModels();
- UMA_HISTOGRAM_TIMES("Sync.SessionAssociationTime",
- base::TimeTicks::Now() - start_time);
- if (!success) {
- StartFailed(ASSOCIATION_FAILED);
- return;
- }
-
- sync_service_->ActivateDataType(this, change_processor_.get());
- state_ = RUNNING;
- FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK);
-}
-
-void SessionDataTypeController::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (change_processor_ != NULL)
- sync_service_->DeactivateDataType(this, change_processor_.get());
+SessionDataTypeController::~SessionDataTypeController() {}
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
- change_processor_.reset();
- model_associator_.reset();
- start_callback_.reset();
-
- state_ = NOT_RUNNING;
-}
-
-bool SessionDataTypeController::enabled() {
- return true;
+SessionModelAssociator* SessionDataTypeController::GetModelAssociator() {
+ return reinterpret_cast<SessionModelAssociator*>(model_associator_.get());
}
-syncable::ModelType SessionDataTypeController::type() {
+syncable::ModelType SessionDataTypeController::type() const {
return syncable::SESSIONS;
}
-browser_sync::ModelSafeGroup SessionDataTypeController::model_safe_group() {
- return browser_sync::GROUP_UI;
-}
-
-const char* SessionDataTypeController::name() const {
- // For logging only.
- return "session";
-}
-
-DataTypeController::State SessionDataTypeController::state() {
- return state_;
+void SessionDataTypeController::CreateSyncComponents() {
+ ProfileSyncFactory::SyncComponents sync_components = profile_sync_factory_->
+ CreateSessionSyncComponents(sync_service_, this);
+ model_associator_.reset(sync_components.model_associator);
+ change_processor_.reset(sync_components.change_processor);
}
-void SessionDataTypeController::OnUnrecoverableError(
+void SessionDataTypeController::RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
UMA_HISTOGRAM_COUNTS("Sync.SessionRunFailures", 1);
- sync_service_->OnUnrecoverableError(from_here, message);
-}
-
-browser_sync::SessionModelAssociator*
- SessionDataTypeController::GetModelAssociator() {
- return static_cast<SessionModelAssociator*>(model_associator_.get());
}
-void SessionDataTypeController::FinishStart(StartResult result) {
- start_callback_->Run(result);
- start_callback_.reset();
+void SessionDataTypeController::RecordAssociationTime(base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.SessionAssociationTime", time);
}
-void SessionDataTypeController::StartFailed(StartResult result) {
- model_associator_.reset();
- change_processor_.reset();
- start_callback_->Run(result);
- start_callback_.reset();
+void SessionDataTypeController::RecordStartFailure(StartResult result) {
UMA_HISTOGRAM_ENUMERATION("Sync.SessionStartFailures",
result,
MAX_START_RESULT);
diff --git a/chrome/browser/sync/glue/session_data_type_controller.h b/chrome/browser/sync/glue/session_data_type_controller.h
index 8736789..4ee284a 100644
--- a/chrome/browser/sync/glue/session_data_type_controller.h
+++ b/chrome/browser/sync/glue/session_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,63 +8,37 @@
#include <string>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/browser/sync/glue/session_model_associator.h"
-
-class ProfileSyncFactory;
-class ProfileSyncService;
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
namespace browser_sync {
-class AssociatorInterface;
-class ChangeProcessor;
+class SessionModelAssociator;
-class SessionDataTypeController : public DataTypeController {
+class SessionDataTypeController : public FrontendDataTypeController {
public:
SessionDataTypeController(
ProfileSyncFactory* profile_sync_factory,
+ Profile* profile,
ProfileSyncService* sync_service);
virtual ~SessionDataTypeController();
- // DataTypeController implementation.
- virtual void Start(StartCallback* start_callback);
-
- virtual void Stop();
-
- virtual bool enabled();
-
- virtual syncable::ModelType type();
-
- virtual browser_sync::ModelSafeGroup model_safe_group();
-
- virtual const char* name() const;
+ SessionModelAssociator* GetModelAssociator();
- virtual State state();
+ // FrontendDataTypeController implementation.
+ virtual syncable::ModelType type() const;
- // UnrecoverableErrorHandler interface.
- virtual void OnUnrecoverableError(
+ private:
+ // FrontendDataTypeController implementations.
+ // Datatype specific creation of sync components.
+ virtual void CreateSyncComponents();
+ // Record unrecoverable errors.
+ virtual void RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message);
-
- SessionModelAssociator* GetModelAssociator();
-
- private:
- // Helper method to run the stashed start callback with a given result.
- void FinishStart(StartResult result);
-
- // Cleans up state and calls callback when start fails.
- void StartFailed(StartResult result);
-
- ProfileSyncFactory* profile_sync_factory_;
- ProfileSyncService* sync_service_;
-
- State state_;
-
- scoped_ptr<StartCallback> start_callback_;
- scoped_ptr<AssociatorInterface> model_associator_;
- scoped_ptr<ChangeProcessor> change_processor_;
+ // Record association time.
+ virtual void RecordAssociationTime(base::TimeDelta time);
+ // Record causes of start failure.
+ virtual void RecordStartFailure(StartResult result);
DISALLOW_COPY_AND_ASSIGN(SessionDataTypeController);
};
diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc
index 55caa37..d737d99 100644
--- a/chrome/browser/sync/glue/session_model_associator.cc
+++ b/chrome/browser/sync/glue/session_model_associator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,18 +8,20 @@
#include <utility>
#include "base/logging.h"
-#include "chrome/browser/browser_list.h"
-#include "chrome/browser/browser_window.h"
+#include "chrome/browser/extensions/extension_tab_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/browser_list.h"
+#include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/common/extensions/extension.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_service.h"
#include "chrome/common/url_constants.h"
#include "content/browser/tab_contents/navigation_controller.h"
#include "content/browser/tab_contents/navigation_entry.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_service.h"
namespace browser_sync {
@@ -35,7 +37,8 @@ static const int max_sync_navigation_count = 6;
SessionModelAssociator::SessionModelAssociator(ProfileSyncService* sync_service)
: tab_pool_(sync_service),
local_session_syncid_(sync_api::kInvalidId),
- sync_service_(sync_service) {
+ sync_service_(sync_service),
+ setup_for_test_(false) {
DCHECK(CalledOnValidThread());
DCHECK(sync_service_);
}
@@ -125,7 +128,7 @@ void SessionModelAssociator::ReassociateWindows(bool reload_tabs) {
VLOG(1) << "Reassociating window " << window_id << " with " <<
(*i)->tab_count() << " tabs.";
window_s.set_window_id(window_id);
- window_s.set_selected_tab_index((*i)->selected_index());
+ window_s.set_selected_tab_index((*i)->active_index());
if ((*i)->type() ==
Browser::TYPE_NORMAL) {
window_s.set_browser_type(
@@ -267,8 +270,13 @@ bool SessionModelAssociator::WriteTabContentsToSyncModel(
int index_in_window = browser.tabstrip_model()->GetWrapperIndex(&tab);
DCHECK(index_in_window != TabStripModel::kNoTab);
tab_s->set_pinned(browser.tabstrip_model()->IsTabPinned(index_in_window));
- if (tab.extension_app())
- tab_s->set_extension_app_id(tab.extension_app()->id());
+ TabContentsWrapper* wrapper =
+ TabContentsWrapper::GetCurrentWrapperForContents(
+ const_cast<TabContents*>(&tab));
+ if (wrapper->extension_tab_helper()->extension_app()) {
+ tab_s->set_extension_app_id(
+ wrapper->extension_tab_helper()->extension_app()->id());
+ }
for (int i = min_index; i < max_index; ++i) {
const NavigationEntry* entry = (i == pending_index) ?
tab.controller().pending_entry() : tab.controller().GetEntryAtIndex(i);
@@ -562,7 +570,7 @@ void SessionModelAssociator::DisassociateForeignSession(
// Static
void SessionModelAssociator::PopulateSessionWindowFromSpecifics(
- std::string foreign_session_tag,
+ const std::string& foreign_session_tag,
const sync_pb::SessionWindow& specifics,
int64 mtime,
SessionWindow* session_window,
@@ -984,4 +992,13 @@ void SessionModelAssociator::PopulateSessionSpecificsTab(
}
}
+bool SessionModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ syncable::ModelTypeSet encrypted_types;
+ sync_service_->GetEncryptedDataTypes(&encrypted_types);
+ return encrypted_types.count(syncable::SESSIONS) == 0 ||
+ sync_service_->IsCryptographerReady(&trans);
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/session_model_associator.h b/chrome/browser/sync/glue/session_model_associator.h
index d8b16a4..28582e0 100644
--- a/chrome/browser/sync/glue/session_model_associator.h
+++ b/chrome/browser/sync/glue/session_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,12 +13,11 @@
#include "base/basictypes.h"
#include "base/format_macros.h"
#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_vector.h"
#include "base/observer_list.h"
-#include "base/scoped_vector.h"
#include "base/string_util.h"
#include "base/threading/non_thread_safe.h"
#include "base/utf_string_conversions.h"
-#include "chrome/browser/browser_window.h"
#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/sessions/session_service.h"
#include "chrome/browser/sessions/session_types.h"
@@ -27,6 +26,7 @@
#include "chrome/browser/sync/glue/model_associator.h"
#include "chrome/browser/sync/protocol/session_specifics.pb.h"
#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/ui/browser_window.h"
#include "content/browser/tab_contents/tab_contents.h"
class Profile;
@@ -68,6 +68,9 @@ class SessionModelAssociator
// No implementation needed, this associator runs on the main thread.
}
+ // See ModelAssociator interface.
+ virtual bool CryptoReadyIfNecessary();
+
// Returns sync id for the given chrome model id.
// Returns sync_api::kInvalidId if the sync node is not found for the given
// chrome id.
@@ -331,7 +334,7 @@ class SessionModelAssociator
// Used to populate a session window from the session specifics window
// provided. Tracks any foreign session data created through |tracker|.
static void PopulateSessionWindowFromSpecifics(
- std::string foreign_session_tag,
+ const std::string& foreign_session_tag,
const sync_pb::SessionWindow& window,
const int64 mtime,
SessionWindow* session_window,
diff --git a/chrome/browser/sync/glue/session_model_associator_unittest.cc b/chrome/browser/sync/glue/session_model_associator_unittest.cc
index 9f0ce3c..693e65f 100644
--- a/chrome/browser/sync/glue/session_model_associator_unittest.cc
+++ b/chrome/browser/sync/glue/session_model_associator_unittest.cc
@@ -1,16 +1,16 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include <string>
#include <vector>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/browser/sessions/session_types.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
-#include "chrome/common/page_transition_types.h"
#include "chrome/common/url_constants.h"
+#include "content/common/page_transition_types.h"
#include "testing/gtest/include/gtest/gtest.h"
using browser_sync::SessionModelAssociator;
diff --git a/chrome/browser/sync/glue/sync_backend_host.cc b/chrome/browser/sync/glue/sync_backend_host.cc
index e079f8e..59e4f7e 100644
--- a/chrome/browser/sync/glue/sync_backend_host.cc
+++ b/chrome/browser/sync/glue/sync_backend_host.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,8 +6,8 @@
#include <algorithm>
-#include "base/compiler_specific.h"
#include "base/command_line.h"
+#include "base/compiler_specific.h"
#include "base/file_util.h"
#include "base/task.h"
#include "base/threading/thread_restrictions.h"
@@ -21,10 +21,12 @@
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/database_model_worker.h"
#include "chrome/browser/sync/glue/history_model_worker.h"
-#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/glue/http_bridge.h"
#include "chrome/browser/sync/glue/password_model_worker.h"
+#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/js_arg_list.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/browser/sync/notifier/sync_notifier_factory.h"
#include "chrome/browser/sync/sessions/session_state.h"
// TODO(tim): Remove this! We should have a syncapi pass-thru instead.
#include "chrome/browser/sync/syncable/directory_manager.h" // Cryptographer.
@@ -33,10 +35,11 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/chrome_version_info.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_type.h"
+#include "googleurl/src/gurl.h"
#include "webkit/glue/webkit_glue.h"
static const int kSaveChangesIntervalSeconds = 10;
@@ -44,6 +47,7 @@ static const FilePath::CharType kSyncDataFolderName[] =
FILE_PATH_LITERAL("Sync Data");
using browser_sync::DataTypeController;
+using sync_notifier::SyncNotifierFactory;
typedef TokenService::TokenAvailableDetails TokenAvailableDetails;
typedef GoogleServiceAuthError AuthError;
@@ -62,9 +66,6 @@ SyncBackendHost::SyncBackendHost(Profile* profile)
sync_data_folder_path_(
profile_->GetPath().Append(kSyncDataFolderName)),
last_auth_error_(AuthError::None()),
- using_new_syncer_thread_(
- CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kNewSyncerThread)),
syncapi_initialized_(false) {
}
@@ -74,9 +75,6 @@ SyncBackendHost::SyncBackendHost()
profile_(NULL),
frontend_(NULL),
last_auth_error_(AuthError::None()),
- using_new_syncer_thread_(
- CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kNewSyncerThread)),
syncapi_initialized_(false) {
}
@@ -89,10 +87,9 @@ void SyncBackendHost::Initialize(
SyncFrontend* frontend,
const GURL& sync_service_url,
const syncable::ModelTypeSet& types,
- URLRequestContextGetter* baseline_context_getter,
+ net::URLRequestContextGetter* baseline_context_getter,
const SyncCredentials& credentials,
- bool delete_sync_data_folder,
- const notifier::NotifierOptions& notifier_options) {
+ bool delete_sync_data_folder) {
if (!core_thread_.Start())
return;
@@ -106,7 +103,7 @@ void SyncBackendHost::Initialize(
// when a new type is synced as the worker may already exist and you just
// need to update routing_info_.
registrar_.workers[GROUP_DB] = new DatabaseModelWorker();
- registrar_.workers[GROUP_UI] = new UIModelWorker(frontend_loop_);
+ registrar_.workers[GROUP_UI] = new UIModelWorker();
registrar_.workers[GROUP_PASSIVE] = new ModelSafeWorker();
if (CommandLine::ForCurrentProcess()->HasSwitch(
@@ -140,12 +137,15 @@ void SyncBackendHost::Initialize(
// Nigori is populated by default now.
registrar_.routing_info[syncable::NIGORI] = GROUP_PASSIVE;
+ // TODO(akalin): Create SyncNotifier here and pass it in as part of
+ // DoInitializeOptions.
+ core_->CreateSyncNotifier(baseline_context_getter);
+
InitCore(Core::DoInitializeOptions(
sync_service_url,
MakeHttpBridgeFactory(baseline_context_getter),
credentials,
delete_sync_data_folder,
- notifier_options,
RestoreEncryptionBootstrapToken(),
false));
}
@@ -179,9 +179,9 @@ bool SyncBackendHost::IsUsingExplicitPassphrase() {
core_->syncapi()->IsUsingExplicitPassphrase();
}
-bool SyncBackendHost::IsCryptographerReady() const {
- return syncapi_initialized_ &&
- GetUserShare()->dir_manager->cryptographer()->is_ready();
+bool SyncBackendHost::IsCryptographerReady(
+ const sync_api::BaseTransaction* trans) const {
+ return syncapi_initialized_ && trans->GetCryptographer()->is_ready();
}
JsBackend* SyncBackendHost::GetJsBackend() {
@@ -194,7 +194,7 @@ JsBackend* SyncBackendHost::GetJsBackend() {
}
sync_api::HttpPostProviderFactory* SyncBackendHost::MakeHttpBridgeFactory(
- URLRequestContextGetter* getter) {
+ net::URLRequestContextGetter* getter) {
return new HttpBridgeFactory(getter);
}
@@ -211,14 +211,6 @@ void SyncBackendHost::UpdateCredentials(const SyncCredentials& credentials) {
credentials));
}
-void SyncBackendHost::UpdateEnabledTypes(
- const syncable::ModelTypeSet& types) {
- core_thread_.message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(core_.get(),
- &SyncBackendHost::Core::DoUpdateEnabledTypes,
- types));
-}
-
void SyncBackendHost::StartSyncingWithServer() {
core_thread_.message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoStartSyncing));
@@ -353,57 +345,77 @@ void SyncBackendHost::ConfigureAutofillMigration() {
}
}
+SyncBackendHost::PendingConfigureDataTypesState::
+PendingConfigureDataTypesState() : deleted_type(false) {}
+
+SyncBackendHost::PendingConfigureDataTypesState::
+~PendingConfigureDataTypesState() {}
+
+// static
+SyncBackendHost::PendingConfigureDataTypesState*
+ SyncBackendHost::MakePendingConfigModeState(
+ const DataTypeController::TypeMap& data_type_controllers,
+ const syncable::ModelTypeSet& types,
+ CancelableTask* ready_task,
+ ModelSafeRoutingInfo* routing_info) {
+ PendingConfigureDataTypesState* state = new PendingConfigureDataTypesState();
+ for (DataTypeController::TypeMap::const_iterator it =
+ data_type_controllers.begin();
+ it != data_type_controllers.end(); ++it) {
+ syncable::ModelType type = it->first;
+ // If a type is not specified, remove it from the routing_info.
+ if (types.count(type) == 0) {
+ state->deleted_type = true;
+ routing_info->erase(type);
+ } else {
+ // Add a newly specified data type as GROUP_PASSIVE into the
+ // routing_info, if it does not already exist.
+ if (routing_info->count(type) == 0) {
+ (*routing_info)[type] = GROUP_PASSIVE;
+ state->added_types.set(type);
+ }
+ }
+ }
+
+ state->ready_task.reset(ready_task);
+ state->initial_types = types;
+ return state;
+}
+
void SyncBackendHost::ConfigureDataTypes(
const DataTypeController::TypeMap& data_type_controllers,
const syncable::ModelTypeSet& types,
CancelableTask* ready_task) {
// Only one configure is allowed at a time.
- DCHECK(!configure_ready_task_.get());
+ DCHECK(!pending_config_mode_state_.get());
+ DCHECK(!pending_download_state_.get());
DCHECK(syncapi_initialized_);
if (types.count(syncable::AUTOFILL_PROFILE) != 0) {
ConfigureAutofillMigration();
}
- bool deleted_type = false;
- syncable::ModelTypeBitSet added_types;
-
{
base::AutoLock lock(registrar_lock_);
- for (DataTypeController::TypeMap::const_iterator it =
- data_type_controllers.begin();
- it != data_type_controllers.end(); ++it) {
- syncable::ModelType type = (*it).first;
-
- // If a type is not specified, remove it from the routing_info.
- if (types.count(type) == 0) {
- registrar_.routing_info.erase(type);
- deleted_type = true;
- } else {
- // Add a newly specified data type as GROUP_PASSIVE into the
- // routing_info, if it does not already exist.
- if (registrar_.routing_info.count(type) == 0) {
- registrar_.routing_info[type] = GROUP_PASSIVE;
- added_types.set(type);
- }
- }
- }
+ pending_config_mode_state_.reset(
+ MakePendingConfigModeState(data_type_controllers, types, ready_task,
+ &registrar_.routing_info));
}
- // If no new data types were added to the passive group, no need to
- // wait for the syncer.
- if (core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) {
- ready_task->Run();
- delete ready_task;
- } else {
- // Save the task here so we can run it when the syncer finishes
- // initializing the new data types. It will be run only when the
- // set of initially synced data types matches the types requested in
- // this configure.
- configure_ready_task_.reset(ready_task);
- configure_initial_sync_types_ = types;
- }
+ StartConfiguration(NewCallback(core_.get(),
+ &SyncBackendHost::Core::FinishConfigureDataTypes));
+}
+void SyncBackendHost::StartConfiguration(Callback0::Type* callback) {
+ // Put syncer in the config mode. DTM will put us in normal mode once it is.
+ // done. This is to ensure we dont do a normal sync when we are doing model
+ // association.
+ core_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(
+ core_.get(),&SyncBackendHost::Core::DoStartConfiguration, callback));
+}
+
+void SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop() {
+ DCHECK_EQ(MessageLoop::current(), frontend_loop_);
// Nudge the syncer. This is necessary for both datatype addition/deletion.
//
// Deletions need a nudge in order to ensure the deletion occurs in a timely
@@ -412,30 +424,51 @@ void SyncBackendHost::ConfigureDataTypes(
// In the case of additions, on the next sync cycle, the syncer should
// notice that the routing info has changed and start the process of
// downloading updates for newly added data types. Once this is
- // complete, the configure_ready_task_ is run via an
+ // complete, the configure_state_.ready_task_ is run via an
// OnInitializationComplete notification.
- ScheduleSyncEventForConfigChange(deleted_type, added_types);
-}
-void SyncBackendHost::ScheduleSyncEventForConfigChange(bool deleted_type,
- const syncable::ModelTypeBitSet& added_types) {
- // We can only nudge when we've either deleted a dataype or added one, else
- // we can cause unnecessary syncs. Unit tests cover this.
- if (using_new_syncer_thread_) {
- if (added_types.size() > 0) {
- RequestConfig(added_types);
- // TODO(tim): If we've added and deleted types, because we don't want to
- // nudge until association finishes, circumstances of bug 56416 exist. We
- // may need a way to nudge only for data type cleanup. Alternatively, we
- // can chain configure_ready_task_ by appending a task to nudge in this
- // case.
- } else if (deleted_type) {
- RequestNudge();
- }
- } else if (deleted_type ||
- !core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) {
- RequestNudge();
+ if (pending_config_mode_state_->deleted_type) {
+ core_thread_.message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(core_.get(),
+ &SyncBackendHost::Core::DeferNudgeForCleanup));
+ }
+
+ if (pending_config_mode_state_->added_types.none() &&
+ !core_->syncapi()->InitialSyncEndedForAllEnabledTypes()) {
+ LOG(WARNING) << "No new types, but initial sync not finished."
+ << "Possible sync db corruption / removal.";
+ // TODO(tim): Log / UMA / count this somehow?
+ // TODO(tim): If no added types, we could (should?) config only for
+ // types that are needed... but this is a rare corruption edge case or
+ // implies the user mucked around with their syncdb, so for now do all.
+ pending_config_mode_state_->added_types =
+ syncable::ModelTypeBitSetFromSet(
+ pending_config_mode_state_->initial_types);
}
+
+ // If we've added types, we always want to request a nudge/config (even if
+ // the initial sync is ended), in case we could not decrypt the data.
+ if (pending_config_mode_state_->added_types.none()) {
+ // No new types - just notify the caller that the types are available.
+ pending_config_mode_state_->ready_task->Run();
+ } else {
+ pending_download_state_.reset(pending_config_mode_state_.release());
+
+ syncable::ModelTypeBitSet types_copy(pending_download_state_->added_types);
+ if (IsNigoriEnabled())
+ types_copy.set(syncable::NIGORI);
+ core_thread_.message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(core_.get(),
+ &SyncBackendHost::Core::DoRequestConfig,
+ types_copy));
+ }
+
+ pending_config_mode_state_.reset();
+
+ // Notify the SyncManager about the new types.
+ core_thread_.message_loop()->PostTask(FROM_HERE,
+ NewRunnableMethod(core_.get(),
+ &SyncBackendHost::Core::DoUpdateEnabledTypes));
}
void SyncBackendHost::EncryptDataTypes(
@@ -446,15 +479,10 @@ void SyncBackendHost::EncryptDataTypes(
encrypted_types));
}
-void SyncBackendHost::RequestNudge() {
+void SyncBackendHost::RequestNudge(const tracked_objects::Location& location) {
core_thread_.message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge));
-}
-
-void SyncBackendHost::RequestConfig(
- const syncable::ModelTypeBitSet& added_types) {
- DCHECK(core_->syncapi());
- core_->syncapi()->RequestConfig(added_types);
+ NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestNudge,
+ location));
}
void SyncBackendHost::ActivateDataType(
@@ -486,23 +514,6 @@ void SyncBackendHost::DeactivateDataType(
std::map<syncable::ModelType, ChangeProcessor*>::size_type erased =
processors_.erase(data_type_controller->type());
DCHECK_EQ(erased, 1U);
-
- // TODO(sync): At this point we need to purge the data associated
- // with this data type from the sync db.
-}
-
-bool SyncBackendHost::RequestPause() {
- DCHECK(!using_new_syncer_thread_);
- core_thread_.message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestPause));
- return true;
-}
-
-bool SyncBackendHost::RequestResume() {
- DCHECK(!using_new_syncer_thread_);
- core_thread_.message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(core_.get(), &SyncBackendHost::Core::DoRequestResume));
- return true;
}
bool SyncBackendHost::RequestClearServerData() {
@@ -515,20 +526,6 @@ bool SyncBackendHost::RequestClearServerData() {
SyncBackendHost::Core::~Core() {
}
-void SyncBackendHost::Core::NotifyPaused() {
- DCHECK(!host_->using_new_syncer_thread_);
- NotificationService::current()->Notify(NotificationType::SYNC_PAUSED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
-}
-
-void SyncBackendHost::Core::NotifyResumed() {
- DCHECK(!host_->using_new_syncer_thread_);
- NotificationService::current()->Notify(NotificationType::SYNC_RESUMED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
-}
-
void SyncBackendHost::Core::NotifyPassphraseRequired(bool for_decryption) {
if (!host_ || !host_->frontend_)
return;
@@ -586,19 +583,36 @@ void SyncBackendHost::Core::NotifyEncryptionComplete(
host_->frontend_->OnEncryptionComplete(encrypted_types);
}
+void SyncBackendHost::Core::FinishConfigureDataTypes() {
+ host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop));
+}
+
+void SyncBackendHost::Core::FinishConfigureDataTypesOnFrontendLoop() {
+ host_->FinishConfigureDataTypesOnFrontendLoop();
+}
+
+
+void SyncBackendHost::Core::CreateSyncNotifier(
+ const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) {
+ const std::string& client_info = webkit_glue::GetUserAgent(GURL());
+ SyncNotifierFactory sync_notifier_factory(client_info);
+ sync_notifier_.reset(sync_notifier_factory.CreateSyncNotifier(
+ *CommandLine::ForCurrentProcess(),
+ request_context_getter));
+}
+
SyncBackendHost::Core::DoInitializeOptions::DoInitializeOptions(
const GURL& service_url,
sync_api::HttpPostProviderFactory* http_bridge_factory,
const sync_api::SyncCredentials& credentials,
bool delete_sync_data_folder,
- const notifier::NotifierOptions& notifier_options,
- std::string restored_key_for_bootstrapping,
+ const std::string& restored_key_for_bootstrapping,
bool setup_for_test_mode)
: service_url(service_url),
http_bridge_factory(http_bridge_factory),
credentials(credentials),
delete_sync_data_folder(delete_sync_data_folder),
- notifier_options(notifier_options),
restored_key_for_bootstrapping(restored_key_for_bootstrapping),
setup_for_test_mode(setup_for_test_mode) {
}
@@ -657,7 +671,9 @@ SyncBackendHost::Core::Core(SyncBackendHost* backend)
: host_(backend),
syncapi_(new sync_api::SyncManager()),
sync_manager_observer_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
- parent_router_(NULL) {
+ parent_router_(NULL),
+ processing_passphrase_(false),
+ deferred_nudge_for_cleanup_requested_(false) {
}
// Helper to construct a user agent string (ASCII) suitable for use by
@@ -716,7 +732,7 @@ void SyncBackendHost::Core::DoInitialize(const DoInitializeOptions& options) {
host_, // ModelSafeWorkerRegistrar.
MakeUserAgentForSyncapi().c_str(),
options.credentials,
- options.notifier_options,
+ sync_notifier_.get(),
options.restored_key_for_bootstrapping,
options.setup_for_test_mode);
DCHECK(success) << "Syncapi initialization failed!";
@@ -728,15 +744,17 @@ void SyncBackendHost::Core::DoUpdateCredentials(
syncapi_->UpdateCredentials(credentials);
}
-void SyncBackendHost::Core::DoUpdateEnabledTypes(
- const syncable::ModelTypeSet& types) {
+void SyncBackendHost::Core::DoUpdateEnabledTypes() {
DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
- syncapi_->UpdateEnabledTypes(types);
+ syncapi_->UpdateEnabledTypes();
}
void SyncBackendHost::Core::DoStartSyncing() {
DCHECK(MessageLoop::current() == host_->core_thread_.message_loop());
syncapi_->StartSyncing();
+ if (deferred_nudge_for_cleanup_requested_)
+ syncapi_->RequestNudge(FROM_HERE);
+ deferred_nudge_for_cleanup_requested_ = false;
}
void SyncBackendHost::Core::DoSetPassphrase(const std::string& passphrase,
@@ -761,6 +779,15 @@ void SyncBackendHost::Core::DoEncryptDataTypes(
syncapi_->EncryptDataTypes(encrypted_types);
}
+void SyncBackendHost::Core::DoRequestConfig(
+ const syncable::ModelTypeBitSet& added_types) {
+ syncapi_->RequestConfig(added_types);
+}
+
+void SyncBackendHost::Core::DoStartConfiguration(Callback0::Type* callback) {
+ syncapi_->StartConfigurationMode(callback);
+}
+
UIModelWorker* SyncBackendHost::ui_worker() {
ModelSafeWorker* w = registrar_.workers[GROUP_UI];
if (w == NULL)
@@ -864,22 +891,29 @@ void SyncBackendHost::Core::HandleSyncCycleCompletedOnFrontendLoop(
host_->last_snapshot_.reset(snapshot);
+ const syncable::ModelTypeSet& to_migrate =
+ snapshot->syncer_status.types_needing_local_migration;
+ if (!to_migrate.empty())
+ host_->frontend_->OnMigrationNeededForTypes(to_migrate);
+
// If we are waiting for a configuration change, check here to see
// if this sync cycle has initialized all of the types we've been
// waiting for.
- if (host_->configure_ready_task_.get()) {
- bool found_all = true;
+ if (host_->pending_download_state_.get()) {
+ bool found_all_added = true;
for (syncable::ModelTypeSet::const_iterator it =
- host_->configure_initial_sync_types_.begin();
- it != host_->configure_initial_sync_types_.end(); ++it) {
- found_all &= snapshot->initial_sync_ended.test(*it);
+ host_->pending_download_state_->initial_types.begin();
+ it != host_->pending_download_state_->initial_types.end();
+ ++it) {
+ if (host_->pending_download_state_->added_types.test(*it))
+ found_all_added &= snapshot->initial_sync_ended.test(*it);
}
-
- if (found_all) {
- host_->configure_ready_task_->Run();
- host_->configure_ready_task_.reset();
- host_->configure_initial_sync_types_.clear();
+ if (!found_all_added) {
+ NOTREACHED() << "Update didn't return updates for all types requested.";
+ } else {
+ host_->pending_download_state_->ready_task->Run();
}
+ host_->pending_download_state_.reset();
}
host_->frontend_->OnSyncCycleCompleted();
}
@@ -953,18 +987,6 @@ void SyncBackendHost::Core::OnPassphraseAccepted(
bootstrap_token));
}
-void SyncBackendHost::Core::OnPaused() {
- host_->frontend_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &Core::NotifyPaused));
-}
-
-void SyncBackendHost::Core::OnResumed() {
- host_->frontend_loop_->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &Core::NotifyResumed));
-}
-
void SyncBackendHost::Core::OnStopSyncingPermanently() {
host_->frontend_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
&Core::HandleStopSyncingPermanentlyOnFrontendLoop));
@@ -1047,22 +1069,15 @@ void SyncBackendHost::Core::StartSavingChanges() {
this, &Core::SaveChanges);
}
-void SyncBackendHost::Core::DoRequestNudge() {
- syncapi_->RequestNudge();
+void SyncBackendHost::Core::DoRequestNudge(
+ const tracked_objects::Location& nudge_location) {
+ syncapi_->RequestNudge(nudge_location);
}
void SyncBackendHost::Core::DoRequestClearServerData() {
syncapi_->RequestClearServerData();
}
-void SyncBackendHost::Core::DoRequestResume() {
- syncapi_->RequestResume();
-}
-
-void SyncBackendHost::Core::DoRequestPause() {
- syncapi()->RequestPause();
-}
-
void SyncBackendHost::Core::SaveChanges() {
syncapi_->SaveChanges();
}
@@ -1138,4 +1153,9 @@ void SyncBackendHost::Core::DoProcessMessage(
syncapi_->GetJsBackend()->ProcessMessage(name, args, sender);
}
+void SyncBackendHost::Core::DeferNudgeForCleanup() {
+ DCHECK_EQ(MessageLoop::current(), host_->core_thread_.message_loop());
+ deferred_nudge_for_cleanup_requested_ = true;
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index a822994..b5c2b70 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,8 +11,9 @@
#include <vector>
#include "base/file_path.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
-#include "base/ref_counted.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/timer.h"
@@ -26,17 +27,20 @@
#include "chrome/browser/sync/js_event_router.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
-#include "chrome/common/net/url_request_context_getter.h"
#include "googleurl/src/gurl.h"
-#include "jingle/notifier/base/notifier_options.h"
+#include "net/url_request/url_request_context_getter.h"
class CancelableTask;
class Profile;
-namespace notifier {
-struct NotifierOptions;
+namespace net {
+class URLRequestContextGetter;
}
+namespace sync_notifier {
+class SyncNotifier;
+} // namespace sync_notifier
+
namespace browser_sync {
namespace sessions {
@@ -91,6 +95,10 @@ class SyncFrontend {
virtual void OnEncryptionComplete(
const syncable::ModelTypeSet& encrypted_types) = 0;
+ // Called to perform migration of |types|.
+ virtual void OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) = 0;
+
protected:
// Don't delete through SyncFrontend interface.
virtual ~SyncFrontend() {
@@ -126,16 +134,13 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
void Initialize(SyncFrontend* frontend,
const GURL& service_url,
const syncable::ModelTypeSet& types,
- URLRequestContextGetter* baseline_context_getter,
+ net::URLRequestContextGetter* baseline_context_getter,
const sync_api::SyncCredentials& credentials,
- bool delete_sync_data_folder,
- const notifier::NotifierOptions& notifier_options);
+ bool delete_sync_data_folder);
// Called from |frontend_loop| to update SyncCredentials.
void UpdateCredentials(const sync_api::SyncCredentials& credentials);
- virtual void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
-
// This starts the SyncerThread running a Syncer object to communicate with
// sync servers. Until this is called, no changes will leave or enter this
// browser from the cloud / sync servers.
@@ -163,6 +168,10 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
const syncable::ModelTypeSet& types,
CancelableTask* ready_task);
+ // Makes an asynchronous call to syncer to switch to config mode. When done
+ // syncer will call us back on FinishConfigureDataTypes.
+ virtual void StartConfiguration(Callback0::Type* callback);
+
// Encrypts the specified datatypes and marks them as needing encryption on
// other machines. This affects all machines synced to this account and all
// data belonging to the specified types.
@@ -194,16 +203,6 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
void DeactivateDataType(DataTypeController* data_type_controller,
ChangeProcessor* change_processor);
- // Requests the backend to pause. Returns true if the request is
- // sent sucessfully. When the backend does pause, a SYNC_PAUSED
- // notification is sent to the notification service.
- virtual bool RequestPause();
-
- // Requests the backend to resume. Returns true if the request is
- // sent sucessfully. When the backend does resume, a SYNC_RESUMED
- // notification is sent to the notification service.
- virtual bool RequestResume();
-
// Asks the server to clear all data associated with ChromeSync.
virtual bool RequestClearServerData();
@@ -245,7 +244,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// True if the cryptographer has any keys available to attempt decryption.
// Could mean we've downloaded and loaded Nigori objects, or we bootstrapped
// using a token previously received.
- bool IsCryptographerReady() const;
+ bool IsCryptographerReady(const sync_api::BaseTransaction* trans) const;
// Returns a pointer to the JsBackend (which is owned by the
// service). Must be called only after the sync backend has been
@@ -281,8 +280,6 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
virtual void OnPassphraseRequired(bool for_decryption);
virtual void OnPassphraseFailed();
virtual void OnPassphraseAccepted(const std::string& bootstrap_token);
- virtual void OnPaused();
- virtual void OnResumed();
virtual void OnStopSyncingPermanently();
virtual void OnUpdatedToken(const std::string& token);
virtual void OnClearServerDataFailed();
@@ -308,8 +305,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
sync_api::HttpPostProviderFactory* http_bridge_factory,
const sync_api::SyncCredentials& credentials,
bool delete_sync_data_folder,
- const notifier::NotifierOptions& notifier_options,
- std::string restored_key_for_bootstrapping,
+ const std::string& restored_key_for_bootstrapping,
bool setup_for_test_mode);
~DoInitializeOptions();
@@ -318,11 +314,14 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
sync_api::SyncCredentials credentials;
std::string lsid;
bool delete_sync_data_folder;
- notifier::NotifierOptions notifier_options;
std::string restored_key_for_bootstrapping;
bool setup_for_test_mode;
};
+ // Called on |frontend_loop_|.
+ void CreateSyncNotifier(const scoped_refptr<net::URLRequestContextGetter>&
+ request_context_getter);
+
// Note:
//
// The Do* methods are the various entry points from our SyncBackendHost.
@@ -337,9 +336,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// update on behalf of SyncBackendHost::UpdateCredentials
void DoUpdateCredentials(const sync_api::SyncCredentials& credentials);
- // Update the set of enabled sync types. Usually called when the user disables
- // or enables a sync type.
- void DoUpdateEnabledTypes(const syncable::ModelTypeSet& types);
+ // Called when the user disables or enables a sync type.
+ void DoUpdateEnabledTypes();
// Called on the SyncBackendHost core_thread_ to tell the syncapi to start
// syncing (generally after initialization and authentication).
@@ -347,11 +345,12 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// Called on the SyncBackendHost core_thread_ to nudge/pause/resume the
// syncer.
- void DoRequestNudge();
- void DoRequestPause();
- void DoRequestResume();
+ void DoRequestNudge(const tracked_objects::Location& location);
void DoRequestClearServerData();
+ // Sets |deferred_nudge_for_cleanup_requested_| to true. See comment below.
+ void DeferNudgeForCleanup();
+
// Called on our SyncBackendHost's |core_thread_| to set the passphrase
// on behalf of SyncBackendHost::SupplyPassphrase.
void DoSetPassphrase(const std::string& passphrase, bool is_explicit);
@@ -377,6 +376,12 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// because the thread that was using them has exited (in step 2).
void DoShutdown(bool stopping_sync);
+ // Posts a config request on the core thread.
+ virtual void DoRequestConfig(const syncable::ModelTypeBitSet& added_types);
+
+ // Start the configuration mode.
+ virtual void DoStartConfiguration(Callback0::Type* callback);
+
// Set the base request context to use when making HTTP calls.
// This method will add a reference to the context to persist it
// on the IO thread. Must be removed from IO thread.
@@ -396,6 +401,9 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
const std::string& name, const JsArgList& args,
const JsEventHandler* sender);
+ // A callback from the SyncerThread when it is safe to continue config.
+ void FinishConfigureDataTypes();
+
#if defined(UNIT_TEST)
// Special form of initialization that does not try and authenticate the
// last known user (since it will fail in test mode) and does some extra
@@ -409,7 +417,7 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
credentials.sync_token = "token";
DoInitialize(DoInitializeOptions(GURL(), factory, credentials,
delete_sync_data_folder,
- notifier::NotifierOptions(), "", true));
+ "", true));
}
#endif
@@ -422,15 +430,6 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// Return change processor for a particular model (return NULL on failure).
ChangeProcessor* GetProcessor(syncable::ModelType modeltype);
-
- // Sends a SYNC_PAUSED notification to the notification service on
- // the UI thread.
- void NotifyPaused();
-
- // Sends a SYNC_RESUMED notification to the notification service
- // on the UI thread.
- void NotifyResumed();
-
// Invoked when initialization of syncapi is complete and we can start
// our timer.
// This must be called from the thread on which SaveChanges is intended to
@@ -490,6 +489,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
const std::string& name, const JsArgList& args,
const JsEventHandler* dst);
+ void FinishConfigureDataTypesOnFrontendLoop();
+
// Return true if a model lives on the current thread.
bool IsCurrentThreadSafeForModel(syncable::ModelType model_type);
@@ -502,6 +503,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// The top-level syncapi entry point.
scoped_ptr<sync_api::SyncManager> syncapi_;
+ scoped_ptr<sync_notifier::SyncNotifier> sync_notifier_;
+
JsSyncManagerObserver sync_manager_observer_;
JsEventRouter* parent_router_;
@@ -512,6 +515,10 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// modified from within the frontend_loop_ (UI thread).
bool processing_passphrase_;
+ // True when a datatype has been disabled so that we nudge once sync is
+ // resumed (after configuration is finished).
+ bool deferred_nudge_for_cleanup_requested_;
+
DISALLOW_COPY_AND_ASSIGN(Core);
};
@@ -521,17 +528,19 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
virtual void HandleInitializationCompletedOnFrontendLoop();
// Posts a nudge request on the core thread.
- virtual void RequestNudge();
+ virtual void RequestNudge(const tracked_objects::Location& location);
- // Posts a config request on the core thread.
- virtual void RequestConfig(const syncable::ModelTypeBitSet& added_types);
+ // Called to finish the job of ConfigureDataTypes once the syncer is in
+ // configuration mode.
+ void FinishConfigureDataTypes();
+ void FinishConfigureDataTypesOnFrontendLoop();
// Allows tests to perform alternate core initialization work.
virtual void InitCore(const Core::DoInitializeOptions& options);
// Factory method for HttpPostProviderFactories.
virtual sync_api::HttpPostProviderFactory* MakeHttpBridgeFactory(
- URLRequestContextGetter* getter);
+ net::URLRequestContextGetter* getter);
MessageLoop* core_loop() { return core_thread_.message_loop(); }
@@ -548,15 +557,36 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
scoped_refptr<Core> core_;
private:
+ FRIEND_TEST_ALL_PREFIXES(SyncBackendHostTest, MakePendingConfigModeState);
+
+ struct PendingConfigureDataTypesState {
+ PendingConfigureDataTypesState();
+ ~PendingConfigureDataTypesState();
+
+ // A task that should be called once data type configuration is
+ // complete.
+ scoped_ptr<CancelableTask> ready_task;
+
+ // The set of types that we are waiting to be initially synced in a
+ // configuration cycle.
+ syncable::ModelTypeSet initial_types;
+
+ // Additional details about which types were added / removed.
+ bool deleted_type;
+ syncable::ModelTypeBitSet added_types;
+ };
+
UIModelWorker* ui_worker();
void ConfigureAutofillMigration();
- // Depending on switches::kUseNewSyncerThread, kicks the syncapi to respond
- // to a change in the set of enabled data types.
- void ScheduleSyncEventForConfigChange(
- bool deleted_type,
- const syncable::ModelTypeBitSet& added_types);
+ // Helper function for ConfigureDataTypes(). Caller owns return
+ // value. Takes ownership of |ready_task| (but not |routing_info|).
+ static PendingConfigureDataTypesState* MakePendingConfigModeState(
+ const DataTypeController::TypeMap& data_type_controllers,
+ const syncable::ModelTypeSet& types,
+ CancelableTask* ready_task,
+ ModelSafeRoutingInfo* routing_info);
// A thread we dedicate for use by our Core to perform initialization,
// authentication, handle messages from the syncapi, and periodically tell
@@ -604,13 +634,8 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// Path of the folder that stores the sync data files.
FilePath sync_data_folder_path_;
- // A task that should be called once data type configuration is
- // complete.
- scoped_ptr<CancelableTask> configure_ready_task_;
-
- // The set of types that we are waiting to be initially synced in a
- // configuration cycle.
- syncable::ModelTypeSet configure_initial_sync_types_;
+ scoped_ptr<PendingConfigureDataTypesState> pending_download_state_;
+ scoped_ptr<PendingConfigureDataTypesState> pending_config_mode_state_;
// UI-thread cache of the last AuthErrorState received from syncapi.
GoogleServiceAuthError last_auth_error_;
@@ -618,9 +643,6 @@ class SyncBackendHost : public browser_sync::ModelSafeWorkerRegistrar {
// UI-thread cache of the last SyncSessionSnapshot received from syncapi.
scoped_ptr<sessions::SyncSessionSnapshot> last_snapshot_;
- // While two impls are in flight, using this for sanity checking. Bug 26339.
- const bool using_new_syncer_thread_;
-
// Whether we've processed the initialization complete callback.
bool syncapi_initialized_;
diff --git a/chrome/browser/sync/glue/sync_backend_host_mock.cc b/chrome/browser/sync/glue/sync_backend_host_mock.cc
index 2d618f7..8e16905 100644
--- a/chrome/browser/sync/glue/sync_backend_host_mock.cc
+++ b/chrome/browser/sync/glue/sync_backend_host_mock.cc
@@ -12,15 +12,6 @@ ACTION(InvokeTask) {
}
SyncBackendHostMock::SyncBackendHostMock() {
- // By default, the RequestPause and RequestResume methods will
- // send the confirmation notification and return true.
- ON_CALL(*this, RequestPause()).
- WillByDefault(testing::DoAll(Notify(NotificationType::SYNC_PAUSED),
- testing::Return(true)));
- ON_CALL(*this, RequestResume()).
- WillByDefault(testing::DoAll(Notify(NotificationType::SYNC_RESUMED),
- testing::Return(true)));
-
// By default, invoke the ready callback.
ON_CALL(*this, ConfigureDataTypes(testing::_, testing::_, testing::_)).
WillByDefault(InvokeTask());
diff --git a/chrome/browser/sync/glue/sync_backend_host_mock.h b/chrome/browser/sync/glue/sync_backend_host_mock.h
index 12c6df1..a594674 100644
--- a/chrome/browser/sync/glue/sync_backend_host_mock.h
+++ b/chrome/browser/sync/glue/sync_backend_host_mock.h
@@ -8,10 +8,10 @@
#include <set>
-#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "base/task.h"
+#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace browser_sync {
@@ -24,10 +24,7 @@ class SyncBackendHostMock : public SyncBackendHost {
MOCK_METHOD3(ConfigureDataTypes,
void(const DataTypeController::TypeMap&,
const std::set<syncable::ModelType>&, CancelableTask*));
- MOCK_METHOD0(RequestPause, bool());
- MOCK_METHOD0(RequestResume, bool());
MOCK_METHOD0(StartSyncingWithServer, void());
- MOCK_METHOD1(UpdateEnabledTypes, void(const syncable::ModelTypeSet&));
};
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/sync_backend_host_unittest.cc b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
new file mode 100644
index 0000000..c1b58ae
--- /dev/null
+++ b/chrome/browser/sync/glue/sync_backend_host_unittest.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/glue/sync_backend_host.h"
+
+#include <cstddef>
+
+#include "base/message_loop.h"
+#include "base/scoped_ptr.h"
+#include "chrome/browser/sync/engine/model_safe_worker.h"
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/data_type_controller.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/test/testing_profile.h"
+#include "content/browser/browser_thread.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TODO(akalin): Remove this once we fix the TODO below.
+#include "chrome/browser/prefs/pref_service.h"
+#include "chrome/common/pref_names.h"
+
+namespace browser_sync {
+
+namespace {
+
+class MockSyncFrontend : public SyncFrontend {
+ public:
+ virtual ~MockSyncFrontend() {}
+
+ MOCK_METHOD0(OnBackendInitialized, void());
+ MOCK_METHOD0(OnSyncCycleCompleted, void());
+ MOCK_METHOD0(OnAuthError, void());
+ MOCK_METHOD0(OnStopSyncingPermanently, void());
+ MOCK_METHOD0(OnClearServerDataSucceeded, void());
+ MOCK_METHOD0(OnClearServerDataFailed, void());
+ MOCK_METHOD1(OnPassphraseRequired, void(bool));
+ MOCK_METHOD0(OnPassphraseAccepted, void());
+ MOCK_METHOD1(OnEncryptionComplete, void(const syncable::ModelTypeSet&));
+ MOCK_METHOD1(OnMigrationNeededForTypes, void(const syncable::ModelTypeSet&));
+};
+
+} // namespace
+
+class SyncBackendHostTest : public testing::Test {
+ protected:
+ SyncBackendHostTest()
+ : ui_thread_(BrowserThread::UI, &ui_loop_) {}
+
+ private:
+ MessageLoop ui_loop_;
+ BrowserThread ui_thread_;
+};
+
+TEST_F(SyncBackendHostTest, InitShutdown) {
+ TestingProfile profile;
+ profile.CreateRequestContext();
+
+ SyncBackendHost backend(&profile);
+
+ // TODO(akalin): Handle this in SyncBackendHost instead of in
+ // ProfileSyncService, or maybe figure out a way to share the
+ // "register sync prefs" code.
+ PrefService* pref_service = profile.GetPrefs();
+ pref_service->RegisterStringPref(prefs::kEncryptionBootstrapToken, "");
+
+ MockSyncFrontend mock_frontend;
+ sync_api::SyncCredentials credentials;
+ credentials.email = "user@example.com";
+ credentials.sync_token = "sync_token";
+ backend.Initialize(&mock_frontend,
+ GURL("http://www.example.com"),
+ syncable::ModelTypeSet(),
+ profile.GetRequestContext(),
+ credentials,
+ true);
+ backend.Shutdown(false);
+ // Scoping for io_thread to get destroyed before other locals.
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ // TODO(sanjeevr): Investigate whether we can do this within
+ // ResetRequestContext
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile.ResetRequestContext();
+ }
+ MessageLoop::current()->RunAllPending();
+}
+
+TEST_F(SyncBackendHostTest, MakePendingConfigModeState) {
+ // Empty.
+ {
+ DataTypeController::TypeMap data_type_controllers;
+ syncable::ModelTypeSet types;
+ ModelSafeRoutingInfo routing_info;
+
+ scoped_ptr<SyncBackendHost::PendingConfigureDataTypesState>
+ state(SyncBackendHost::MakePendingConfigModeState(
+ data_type_controllers, types, NULL, &routing_info));
+ EXPECT_TRUE(routing_info.empty());
+ EXPECT_FALSE(state->ready_task.get());
+ EXPECT_EQ(types, state->initial_types);
+ EXPECT_FALSE(state->deleted_type);
+ EXPECT_TRUE(state->added_types.none());
+ }
+
+ // No enabled types.
+ {
+ DataTypeController::TypeMap data_type_controllers;
+ data_type_controllers[syncable::BOOKMARKS] = NULL;
+ syncable::ModelTypeSet types;
+ ModelSafeRoutingInfo routing_info;
+
+ scoped_ptr<SyncBackendHost::PendingConfigureDataTypesState>
+ state(SyncBackendHost::MakePendingConfigModeState(
+ data_type_controllers, types, NULL, &routing_info));
+ EXPECT_TRUE(routing_info.empty());
+ EXPECT_FALSE(state->ready_task.get());
+ EXPECT_EQ(types, state->initial_types);
+ EXPECT_TRUE(state->deleted_type);
+ EXPECT_TRUE(state->added_types.none());
+ }
+
+ // Add type.
+ {
+ DataTypeController::TypeMap data_type_controllers;
+ data_type_controllers[syncable::BOOKMARKS] = NULL;
+ syncable::ModelTypeSet types;
+ types.insert(syncable::BOOKMARKS);
+ ModelSafeRoutingInfo routing_info;
+
+ scoped_ptr<SyncBackendHost::PendingConfigureDataTypesState>
+ state(SyncBackendHost::MakePendingConfigModeState(
+ data_type_controllers, types, NULL, &routing_info));
+
+ ModelSafeRoutingInfo expected_routing_info;
+ expected_routing_info[syncable::BOOKMARKS] = GROUP_PASSIVE;
+ EXPECT_EQ(expected_routing_info, routing_info);
+ EXPECT_FALSE(state->ready_task.get());
+ EXPECT_EQ(types, state->initial_types);
+ EXPECT_FALSE(state->deleted_type);
+
+ syncable::ModelTypeBitSet expected_added_types;
+ expected_added_types.set(syncable::BOOKMARKS);
+ EXPECT_EQ(expected_added_types, state->added_types);
+ }
+
+ // Add existing type.
+ {
+ DataTypeController::TypeMap data_type_controllers;
+ data_type_controllers[syncable::BOOKMARKS] = NULL;
+ syncable::ModelTypeSet types;
+ types.insert(syncable::BOOKMARKS);
+ ModelSafeRoutingInfo routing_info;
+ routing_info[syncable::BOOKMARKS] = GROUP_PASSIVE;
+ ModelSafeRoutingInfo expected_routing_info = routing_info;
+
+ scoped_ptr<SyncBackendHost::PendingConfigureDataTypesState>
+ state(SyncBackendHost::MakePendingConfigModeState(
+ data_type_controllers, types, NULL, &routing_info));
+
+ EXPECT_EQ(expected_routing_info, routing_info);
+ EXPECT_FALSE(state->ready_task.get());
+ EXPECT_EQ(types, state->initial_types);
+ EXPECT_FALSE(state->deleted_type);
+ EXPECT_TRUE(state->added_types.none());
+ }
+
+ // Delete type.
+ {
+ DataTypeController::TypeMap data_type_controllers;
+ data_type_controllers[syncable::BOOKMARKS] = NULL;
+ syncable::ModelTypeSet types;
+ ModelSafeRoutingInfo routing_info;
+ routing_info[syncable::BOOKMARKS] = GROUP_PASSIVE;
+
+ scoped_ptr<SyncBackendHost::PendingConfigureDataTypesState>
+ state(SyncBackendHost::MakePendingConfigModeState(
+ data_type_controllers, types, NULL, &routing_info));
+
+ ModelSafeRoutingInfo expected_routing_info;
+ EXPECT_EQ(expected_routing_info, routing_info);
+ EXPECT_FALSE(state->ready_task.get());
+ EXPECT_EQ(types, state->initial_types);
+ EXPECT_TRUE(state->deleted_type);
+ EXPECT_TRUE(state->added_types.none());
+ }
+}
+
+// TODO(akalin): Write more SyncBackendHost unit tests.
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/synchronized_preferences.h b/chrome/browser/sync/glue/synchronized_preferences.h
index 25135fa..beb8166 100644
--- a/chrome/browser/sync/glue/synchronized_preferences.h
+++ b/chrome/browser/sync/glue/synchronized_preferences.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -22,13 +22,12 @@ static const char* kSynchronizedPreferences[] = {
prefs::kHomePageIsNewTabPage,
prefs::kHomePage,
prefs::kShowHomeButton,
- prefs::kShowPageOptionsButtons,
// Default Search is not synced, needs a new data type. See
// http://crbug.com/40482
// Options dialog: Personal Stuff tab.
prefs::kPasswordManagerEnabled,
- prefs::kAutoFillEnabled,
+ prefs::kAutofillEnabled,
prefs::kUseCustomChromeFrame,
// Options dialog: Under the hood -> Content Settings -> Cookies.
@@ -77,7 +76,7 @@ static const char* kSynchronizedPreferences[] = {
// Options dialog: Under the hood.
prefs::kAlternateErrorPagesEnabled,
prefs::kSearchSuggestEnabled,
- prefs::kDnsPrefetchingEnabled,
+ prefs::kNetworkPredictionEnabled,
prefs::kSafeBrowsingEnabled,
prefs::kEnableTranslate,
// Download directory not synced.
@@ -96,7 +95,7 @@ static const char* kSynchronizedPreferences[] = {
// Autofill dialog.
#if defined(OS_MACOSX)
- prefs::kAutoFillAuxiliaryProfilesEnabled,
+ prefs::kAutofillAuxiliaryProfilesEnabled,
#endif
// Translate preferences.
@@ -110,8 +109,8 @@ static const char* kSynchronizedPreferences[] = {
prefs::kDesktopNotificationAllowedOrigins,
prefs::kDesktopNotificationDeniedOrigins,
- // Cookie prompt dialog.
- prefs::kCookiePromptExpanded,
+ // (Mac) Application menu.
+ prefs::kConfirmToQuitEnabled,
#if defined(OS_CHROMEOS)
// IME prefs
@@ -186,6 +185,18 @@ static const char* kSynchronizedPreferences[] = {
// Whether to show mobile plan notifications.
// Settings -> Internet -> Mobile plan details
prefs::kShowPlanNotifications,
+
+ // Whether to require password to wake up from sleep
+ // Settings -> Personal Stuff -> Account
+ prefs::kEnableScreenLock,
+
+ // Whether to enable tap-to-click
+ // Settings -> System -> Touchpad
+ prefs::kTapToClickEnabled,
+
+ // Whether to use the 24-hour clock format.
+ // Settings -> System -> Date and Time
+ prefs::kUse24HourClock,
#endif
};
diff --git a/chrome/browser/sync/glue/theme_change_processor.cc b/chrome/browser/sync/glue/theme_change_processor.cc
index 58f25c2..a75b7eb 100644
--- a/chrome/browser/sync/glue/theme_change_processor.cc
+++ b/chrome/browser/sync/glue/theme_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,22 +9,14 @@
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/theme_util.h"
#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
-#include "chrome/browser/themes/browser_theme_provider.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/common/extensions/extension.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_source.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
namespace browser_sync {
-namespace {
-std::string GetThemeId(const Extension* current_theme) {
- if (current_theme) {
- DCHECK(current_theme->is_theme());
- }
- return current_theme ? current_theme->id() : "default/system";
-}
-} // namespace
-
ThemeChangeProcessor::ThemeChangeProcessor(
UnrecoverableErrorHandler* error_handler)
: ChangeProcessor(error_handler),
@@ -39,79 +31,7 @@ void ThemeChangeProcessor::Observe(NotificationType type,
const NotificationDetails& details) {
DCHECK(running());
DCHECK(profile_);
- const Extension* extension = NULL;
- if (type == NotificationType::EXTENSION_UNLOADED) {
- extension = Details<UnloadedExtensionInfo>(details)->extension;
- } else {
- extension = Details<const Extension>(details).ptr();
- }
- std::string current_or_future_theme_id =
- profile_->GetThemeProvider()->GetThemeID();
- const Extension* current_theme = profile_->GetTheme();
- switch (type.value) {
- case NotificationType::BROWSER_THEME_CHANGED:
- // We pay attention to this notification only when it signifies
- // that a user changed the theme to the default/system theme, or
- // when it signifies that a user changed the theme to a custom
- // one that was already installed. Otherwise, current_theme
- // still points to the previous theme until it gets installed
- // and loaded (and we get an EXTENSION_LOADED notification).
- VLOG(1) << "Got BROWSER_THEME_CHANGED notification for theme "
- << GetThemeId(extension);
- DCHECK_EQ(Source<BrowserThemeProvider>(source).ptr(),
- profile_->GetThemeProvider());
- if (extension != NULL) {
- DCHECK(extension->is_theme());
- DCHECK_EQ(extension->id(), current_or_future_theme_id);
- if (!current_theme || (current_theme->id() != extension->id())) {
- return;
- }
- }
- break;
- case NotificationType::EXTENSION_LOADED:
- // We pay attention to this notification only when it signifies
- // that a theme extension has been loaded because that means
- // that the user set the current theme to a custom theme that
- // needed to be downloaded and installed and that it was
- // installed successfully.
- DCHECK_EQ(Source<Profile>(source).ptr(), profile_);
- CHECK(extension);
- if (!extension->is_theme()) {
- return;
- }
- VLOG(1) << "Got EXTENSION_LOADED notification for theme "
- << extension->id();
- DCHECK_EQ(extension->id(), current_or_future_theme_id);
- DCHECK_EQ(extension, current_theme);
- break;
- case NotificationType::EXTENSION_UNLOADED:
- // We pay attention to this notification only when it signifies
- // that a theme extension has been unloaded because that means
- // that the user set the current theme to a custom theme and then
- // changed his mind and undid it (reverting to the previous
- // theme).
- DCHECK_EQ(Source<Profile>(source).ptr(), profile_);
- CHECK(extension);
- if (!extension->is_theme()) {
- return;
- }
- VLOG(1) << "Got EXTENSION_UNLOADED notification for theme "
- << extension->id();
- extension = current_theme;
- break;
- default:
- LOG(DFATAL) << "Unexpected notification received: " << type.value;
- break;
- }
-
- DCHECK_EQ(extension, current_theme);
- if (extension) {
- DCHECK(extension->is_theme());
- }
- VLOG(1) << "Theme changed to " << GetThemeId(extension);
-
- // Here, we know that a theme is being set; the theme is a custom
- // theme iff extension is non-NULL.
+ DCHECK(type == NotificationType::BROWSER_THEME_CHANGED);
sync_api::WriteTransaction trans(share_handle());
sync_api::WriteNode node(&trans);
@@ -198,17 +118,11 @@ void ThemeChangeProcessor::StopImpl() {
void ThemeChangeProcessor::StartObserving() {
DCHECK(profile_);
- VLOG(1) << "Observing BROWSER_THEME_CHANGED, EXTENSION_LOADED, and "
- "EXTENSION_UNLOADED";
+ VLOG(1) << "Observing BROWSER_THEME_CHANGED";
notification_registrar_.Add(
this, NotificationType::BROWSER_THEME_CHANGED,
- Source<BrowserThemeProvider>(profile_->GetThemeProvider()));
- notification_registrar_.Add(
- this, NotificationType::EXTENSION_LOADED,
- Source<Profile>(profile_));
- notification_registrar_.Add(
- this, NotificationType::EXTENSION_UNLOADED,
- Source<Profile>(profile_));
+ Source<ThemeService>(
+ ThemeServiceFactory::GetForProfile(profile_)));
}
void ThemeChangeProcessor::StopObserving() {
diff --git a/chrome/browser/sync/glue/theme_change_processor.h b/chrome/browser/sync/glue/theme_change_processor.h
index a694c0b..7a94c92 100644
--- a/chrome/browser/sync/glue/theme_change_processor.h
+++ b/chrome/browser/sync/glue/theme_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,9 +9,9 @@
#include "base/basictypes.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/change_processor.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_type.h"
-#include "chrome/common/notification_registrar.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
class NotificationDetails;
class NotificationSource;
@@ -22,7 +22,7 @@ namespace browser_sync {
class UnrecoverableErrorHandler;
// This class is responsible for taking changes from the
-// BrowserThemeProvider and applying them to the sync_api 'syncable'
+// ThemeService and applying them to the sync_api 'syncable'
// model, and vice versa. All operations and use of this class are
// from the UI thread.
class ThemeChangeProcessor : public ChangeProcessor,
@@ -32,13 +32,13 @@ class ThemeChangeProcessor : public ChangeProcessor,
virtual ~ThemeChangeProcessor();
// NotificationObserver implementation.
- // BrowserThemeProvider -> sync_api model change application.
+ // ThemeService -> sync_api model change application.
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details);
// ChangeProcessor implementation.
- // sync_api model -> BrowserThemeProvider change application.
+ // sync_api model -> ThemeService change application.
virtual void ApplyChangesFromSyncModel(
const sync_api::BaseTransaction* trans,
const sync_api::SyncManager::ChangeRecord* changes,
@@ -54,7 +54,7 @@ class ThemeChangeProcessor : public ChangeProcessor,
void StopObserving();
NotificationRegistrar notification_registrar_;
- // Owner of the BrowserThemeProvider. Non-NULL iff |running()| is
+ // Profile associated with the ThemeService. Non-NULL iff |running()| is
// true.
Profile* profile_;
diff --git a/chrome/browser/sync/glue/theme_data_type_controller.cc b/chrome/browser/sync/glue/theme_data_type_controller.cc
index 6abe990..efcbe11 100644
--- a/chrome/browser/sync/glue/theme_data_type_controller.cc
+++ b/chrome/browser/sync/glue/theme_data_type_controller.cc
@@ -1,19 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/theme_data_type_controller.h"
#include "base/metrics/histogram.h"
-#include "base/logging.h"
-#include "base/time.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/glue/theme_change_processor.h"
-#include "chrome/browser/sync/glue/theme_model_associator.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/browser/sync/unrecoverable_error_handler.h"
-#include "content/browser/browser_thread.h"
namespace browser_sync {
@@ -21,111 +14,42 @@ ThemeDataTypeController::ThemeDataTypeController(
ProfileSyncFactory* profile_sync_factory,
Profile* profile,
ProfileSyncService* sync_service)
- : profile_sync_factory_(profile_sync_factory),
- profile_(profile),
- sync_service_(sync_service),
- state_(NOT_RUNNING) {
- DCHECK(profile_sync_factory);
- DCHECK(sync_service);
+ : FrontendDataTypeController(profile_sync_factory,
+ profile,
+ sync_service) {
}
ThemeDataTypeController::~ThemeDataTypeController() {
}
-void ThemeDataTypeController::Start(StartCallback* start_callback) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- DCHECK(start_callback);
- if (state_ != NOT_RUNNING) {
- start_callback->Run(BUSY);
- delete start_callback;
- return;
- }
+syncable::ModelType ThemeDataTypeController::type() const {
+ return syncable::THEMES;
+}
- start_callback_.reset(start_callback);
+bool ThemeDataTypeController::StartModels() {
+ profile_->InitExtensions(true);
+ return true;
+}
- profile_->InitExtensions();
+void ThemeDataTypeController::CreateSyncComponents() {
ProfileSyncFactory::SyncComponents sync_components =
profile_sync_factory_->CreateThemeSyncComponents(sync_service_,
- this);
+ this);
model_associator_.reset(sync_components.model_associator);
change_processor_.reset(sync_components.change_processor);
-
- bool sync_has_nodes = false;
- if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
- StartFailed(UNRECOVERABLE_ERROR);
- return;
- }
-
- base::TimeTicks start_time = base::TimeTicks::Now();
- bool merge_success = model_associator_->AssociateModels();
- UMA_HISTOGRAM_TIMES("Sync.ThemeAssociationTime",
- base::TimeTicks::Now() - start_time);
- if (!merge_success) {
- StartFailed(ASSOCIATION_FAILED);
- return;
- }
-
- sync_service_->ActivateDataType(this, change_processor_.get());
- state_ = RUNNING;
- FinishStart(!sync_has_nodes ? OK_FIRST_RUN : OK);
-}
-
-void ThemeDataTypeController::Stop() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
-
- if (change_processor_ != NULL)
- sync_service_->DeactivateDataType(this, change_processor_.get());
-
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
- change_processor_.reset();
- model_associator_.reset();
- start_callback_.reset();
-
- state_ = NOT_RUNNING;
}
-bool ThemeDataTypeController::enabled() {
- return true;
- }
-
-syncable::ModelType ThemeDataTypeController::type() {
- return syncable::THEMES;
-}
-
-browser_sync::ModelSafeGroup ThemeDataTypeController::model_safe_group() {
- return browser_sync::GROUP_UI;
-}
-
-const char* ThemeDataTypeController::name() const {
- // For logging only.
- return "theme";
-}
-
-DataTypeController::State ThemeDataTypeController::state() {
- return state_;
-}
-
-
-void ThemeDataTypeController::OnUnrecoverableError(
+void ThemeDataTypeController::RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message) {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
UMA_HISTOGRAM_COUNTS("Sync.ThemeRunFailures", 1);
- sync_service_->OnUnrecoverableError(from_here, message);
}
-void ThemeDataTypeController::FinishStart(StartResult result) {
- start_callback_->Run(result);
- start_callback_.reset();
+void ThemeDataTypeController::RecordAssociationTime(base::TimeDelta time) {
+ UMA_HISTOGRAM_TIMES("Sync.ThemeAssociationTime", time);
}
-void ThemeDataTypeController::StartFailed(StartResult result) {
- model_associator_.reset();
- change_processor_.reset();
- start_callback_->Run(result);
- start_callback_.reset();
+void ThemeDataTypeController::RecordStartFailure(StartResult result) {
UMA_HISTOGRAM_ENUMERATION("Sync.ThemeStartFailures",
result,
MAX_START_RESULT);
diff --git a/chrome/browser/sync/glue/theme_data_type_controller.h b/chrome/browser/sync/glue/theme_data_type_controller.h
index 09bc572..37f5ceb 100644
--- a/chrome/browser/sync/glue/theme_data_type_controller.h
+++ b/chrome/browser/sync/glue/theme_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,20 +8,11 @@
#include <string>
-#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/glue/data_type_controller.h"
-
-class Profile;
-class ProfileSyncFactory;
-class ProfileSyncService;
+#include "chrome/browser/sync/glue/frontend_data_type_controller.h"
namespace browser_sync {
-class AssociatorInterface;
-class ChangeProcessor;
-
-class ThemeDataTypeController : public DataTypeController {
+class ThemeDataTypeController : public FrontendDataTypeController {
public:
ThemeDataTypeController(
ProfileSyncFactory* profile_sync_factory,
@@ -30,42 +21,17 @@ class ThemeDataTypeController : public DataTypeController {
virtual ~ThemeDataTypeController();
// DataTypeController implementation.
- virtual void Start(StartCallback* start_callback);
-
- virtual void Stop();
-
- virtual bool enabled();
-
- virtual syncable::ModelType type();
-
- virtual browser_sync::ModelSafeGroup model_safe_group();
-
- virtual const char* name() const;
-
- virtual State state();
+ virtual syncable::ModelType type() const;
- // UnrecoverableErrorHandler interface.
- virtual void OnUnrecoverableError(
+ private:
+ // DataTypeController implementations.
+ virtual bool StartModels();
+ virtual void CreateSyncComponents();
+ virtual void RecordUnrecoverableError(
const tracked_objects::Location& from_here,
const std::string& message);
-
- private:
- // Helper method to run the stashed start callback with a given result.
- void FinishStart(StartResult result);
-
- // Cleans up state and calls callback when start fails.
- void StartFailed(StartResult result);
-
- ProfileSyncFactory* profile_sync_factory_;
- Profile* profile_;
- ProfileSyncService* sync_service_;
-
- State state_;
-
- scoped_ptr<StartCallback> start_callback_;
- scoped_ptr<AssociatorInterface> model_associator_;
- scoped_ptr<ChangeProcessor> change_processor_;
-
+ virtual void RecordAssociationTime(base::TimeDelta time);
+ virtual void RecordStartFailure(StartResult result);
DISALLOW_COPY_AND_ASSIGN(ThemeDataTypeController);
};
diff --git a/chrome/browser/sync/glue/theme_data_type_controller_unittest.cc b/chrome/browser/sync/glue/theme_data_type_controller_unittest.cc
index 16a91b4..4d2a1ad 100644
--- a/chrome/browser/sync/glue/theme_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/theme_data_type_controller_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/task.h"
#include "chrome/browser/sync/glue/theme_data_type_controller.h"
#include "chrome/browser/sync/glue/change_processor_mock.h"
@@ -28,7 +28,8 @@ using testing::SetArgumentPointee;
class StartCallback {
public:
- MOCK_METHOD1(Run, void(DataTypeController::StartResult result));
+ MOCK_METHOD2(Run, void(DataTypeController::StartResult result,
+ const tracked_objects::Location& location));
};
class ThemeDataTypeControllerTest : public testing::Test {
@@ -53,6 +54,8 @@ class ThemeDataTypeControllerTest : public testing::Test {
}
void SetAssociateExpectations() {
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
EXPECT_CALL(*model_associator_, AssociateModels()).
@@ -84,7 +87,7 @@ TEST_F(ThemeDataTypeControllerTest, Start) {
SetAssociateExpectations();
SetActivateExpectations();
EXPECT_EQ(DataTypeController::NOT_RUNNING, theme_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, theme_dtc_->state());
}
@@ -95,7 +98,7 @@ TEST_F(ThemeDataTypeControllerTest, StartFirstRun) {
SetActivateExpectations();
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK_FIRST_RUN, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -106,7 +109,7 @@ TEST_F(ThemeDataTypeControllerTest, StartOk) {
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(true), Return(true)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
}
@@ -116,7 +119,7 @@ TEST_F(ThemeDataTypeControllerTest, StartAssociationFailed) {
EXPECT_CALL(*model_associator_, AssociateModels()).
WillRepeatedly(Return(false));
- EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::ASSOCIATION_FAILED, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, theme_dtc_->state());
}
@@ -125,9 +128,11 @@ TEST_F(ThemeDataTypeControllerTest,
StartAssociationTriggersUnrecoverableError) {
SetStartExpectations();
// Set up association to fail with an unrecoverable error.
+ EXPECT_CALL(*model_associator_, CryptoReadyIfNecessary()).
+ WillRepeatedly(Return(true));
EXPECT_CALL(*model_associator_, SyncModelHasUserCreatedNodes(_)).
WillRepeatedly(DoAll(SetArgumentPointee<0>(false), Return(false)));
- EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::UNRECOVERABLE_ERROR, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::NOT_RUNNING, theme_dtc_->state());
}
@@ -140,7 +145,7 @@ TEST_F(ThemeDataTypeControllerTest, Stop) {
EXPECT_EQ(DataTypeController::NOT_RUNNING, theme_dtc_->state());
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
EXPECT_EQ(DataTypeController::RUNNING, theme_dtc_->state());
theme_dtc_->Stop();
@@ -159,7 +164,7 @@ TEST_F(ThemeDataTypeControllerTest, OnUnrecoverableError) {
&ThemeDataTypeController::Stop));
SetStopExpectations();
- EXPECT_CALL(start_callback_, Run(DataTypeController::OK));
+ EXPECT_CALL(start_callback_, Run(DataTypeController::OK, _));
theme_dtc_->Start(NewCallback(&start_callback_, &StartCallback::Run));
// This should cause theme_dtc_->Stop() to be called.
theme_dtc_->OnUnrecoverableError(FROM_HERE, "Test");
diff --git a/chrome/browser/sync/glue/theme_model_associator.cc b/chrome/browser/sync/glue/theme_model_associator.cc
index 6b51af2..6fcbf00 100644
--- a/chrome/browser/sync/glue/theme_model_associator.cc
+++ b/chrome/browser/sync/glue/theme_model_associator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,7 +12,6 @@
#include "chrome/browser/sync/glue/theme_util.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
-#include "chrome/browser/themes/browser_theme_provider.h"
namespace browser_sync {
@@ -94,4 +93,13 @@ bool ThemeModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
return true;
}
+bool ThemeModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ syncable::ModelTypeSet encrypted_types;
+ sync_service_->GetEncryptedDataTypes(&encrypted_types);
+ return encrypted_types.count(syncable::THEMES) == 0 ||
+ sync_service_->IsCryptographerReady(&trans);
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/theme_model_associator.h b/chrome/browser/sync/glue/theme_model_associator.h
index 8e17724..e76b5bd 100644
--- a/chrome/browser/sync/glue/theme_model_associator.h
+++ b/chrome/browser/sync/glue/theme_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -34,6 +34,7 @@ class ThemeModelAssociator : public AssociatorInterface {
// No implementation needed, this associator runs on the main
// thread.
}
+ virtual bool CryptoReadyIfNecessary();
private:
ProfileSyncService* sync_service_;
diff --git a/chrome/browser/sync/glue/theme_util.cc b/chrome/browser/sync/glue/theme_util.cc
index 2e91f02..f43257d 100644
--- a/chrome/browser/sync/glue/theme_util.cc
+++ b/chrome/browser/sync/glue/theme_util.cc
@@ -7,15 +7,17 @@
#include <string>
#include "base/logging.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/extension_install_ui.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_updater.h"
#if defined(TOOLKIT_USES_GTK)
-#include "chrome/browser/ui/gtk/gtk_theme_provider.h"
+#include "chrome/browser/ui/gtk/gtk_theme_service.h"
#endif
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "googleurl/src/gurl.h"
@@ -36,7 +38,7 @@ bool IsSystemThemeDistinctFromDefaultTheme() {
bool UseSystemTheme(Profile* profile) {
#if defined(TOOLKIT_USES_GTK)
- return GtkThemeProvider::GetFrom(profile)->UseGtkTheme();
+ return GtkThemeService::GetFrom(profile)->UseGtkTheme();
#else
return false;
#endif
@@ -91,7 +93,8 @@ void SetCurrentThemeFromThemeSpecifics(
std::string id(theme_specifics.custom_theme_id());
GURL update_url(theme_specifics.custom_theme_update_url());
VLOG(1) << "Applying theme " << id << " with update_url " << update_url;
- ExtensionService* extensions_service = profile->GetExtensionService();
+ ExtensionServiceInterface* extensions_service =
+ profile->GetExtensionService();
CHECK(extensions_service);
const Extension* extension = extensions_service->GetExtensionById(id, true);
if (extension) {
@@ -99,21 +102,15 @@ void SetCurrentThemeFromThemeSpecifics(
VLOG(1) << "Extension " << id << " is not a theme; aborting";
return;
}
- ExtensionPrefs* extension_prefs = extensions_service->extension_prefs();
- CHECK(extension_prefs);
- // TODO(akalin): GetExtensionState() isn't very safe as it
- // returns Extension::ENABLED by default; either change it to
- // return something else by default or create a separate
- // function that does so.
- if (extension_prefs->GetExtensionState(extension->id()) !=
- Extension::ENABLED) {
+ if (!extensions_service->IsExtensionEnabled(id)) {
VLOG(1) << "Theme " << id << " is not enabled; aborting";
return;
}
// Get previous theme info before we set the new theme.
std::string previous_theme_id;
{
- const Extension* current_theme = profile->GetTheme();
+ const Extension* current_theme =
+ ThemeServiceFactory::GetThemeForProfile(profile);
if (current_theme) {
DCHECK(current_theme->is_theme());
previous_theme_id = current_theme->id();
@@ -122,7 +119,7 @@ void SetCurrentThemeFromThemeSpecifics(
bool previous_use_system_theme = UseSystemTheme(profile);
// An enabled theme extension with the given id was found, so
// just set the current theme to it.
- profile->SetTheme(extension);
+ ThemeServiceFactory::GetForProfile(profile)->SetTheme(extension);
// Pretend the theme was just installed.
ExtensionInstallUI::ShowThemeInfoBar(
previous_theme_id, previous_use_system_theme,
@@ -137,31 +134,24 @@ void SetCurrentThemeFromThemeSpecifics(
const bool kInstallSilently = false;
const bool kEnableOnInstall = true;
const bool kEnableIncognitoOnInstall = false;
- extensions_service->AddPendingExtensionFromSync(
+ extensions_service->pending_extension_manager()->AddFromSync(
id, update_url, &IsTheme,
kInstallSilently, kEnableOnInstall, kEnableIncognitoOnInstall);
- ExtensionUpdater* extension_updater = extensions_service->updater();
- // Auto-updates should now be on always (see the construction of
- // the ExtensionService in ProfileImpl::InitExtensions()).
- if (!extension_updater) {
- LOG(DFATAL) << "Extension updater unexpectedly NULL; "
- << "auto-updates may be turned off";
- return;
- }
- extension_updater->CheckNow();
+ extensions_service->CheckForUpdatesSoon();
}
} else if (theme_specifics.use_system_theme_by_default()) {
- profile->SetNativeTheme();
+ ThemeServiceFactory::GetForProfile(profile)->SetNativeTheme();
} else {
- profile->ClearTheme();
+ ThemeServiceFactory::GetForProfile(profile)->UseDefaultTheme();
}
}
bool UpdateThemeSpecificsOrSetCurrentThemeIfNecessary(
Profile* profile, sync_pb::ThemeSpecifics* theme_specifics) {
if (!theme_specifics->use_custom_theme() &&
- (profile->GetTheme() || (UseSystemTheme(profile) &&
- IsSystemThemeDistinctFromDefaultTheme()))) {
+ (ThemeServiceFactory::GetThemeForProfile(profile) ||
+ (UseSystemTheme(profile) &&
+ IsSystemThemeDistinctFromDefaultTheme()))) {
GetThemeSpecificsFromCurrentTheme(profile, theme_specifics);
return true;
} else {
@@ -174,7 +164,8 @@ void GetThemeSpecificsFromCurrentTheme(
Profile* profile,
sync_pb::ThemeSpecifics* theme_specifics) {
DCHECK(profile);
- const Extension* current_theme = profile->GetTheme();
+ const Extension* current_theme =
+ ThemeServiceFactory::GetThemeForProfile(profile);
if (current_theme) {
DCHECK(current_theme->is_theme());
}
diff --git a/chrome/browser/sync/glue/theme_util_unittest.cc b/chrome/browser/sync/glue/theme_util_unittest.cc
index dfa3ea3..d72eb4a 100644
--- a/chrome/browser/sync/glue/theme_util_unittest.cc
+++ b/chrome/browser/sync/glue/theme_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,6 +8,8 @@
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
+#include "chrome/browser/themes/theme_service.h"
+#include "chrome/browser/themes/theme_service_factory.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/test/testing_profile.h"
@@ -33,7 +35,8 @@ scoped_refptr<Extension> MakeThemeExtension(const FilePath& extension_path,
source.SetString(extension_manifest_keys::kVersion, "0.0.0.0");
std::string error;
scoped_refptr<Extension> extension = Extension::Create(
- extension_path, Extension::INTERNAL, source, false, true, &error);
+ extension_path, Extension::INTERNAL, source,
+ Extension::STRICT_ERROR_CHECKS, &error);
EXPECT_TRUE(extension);
EXPECT_EQ("", error);
return extension;
@@ -88,30 +91,37 @@ TEST_F(ThemeUtilTest, AreThemeSpecificsEqualHelper) {
EXPECT_TRUE(AreThemeSpecificsEqualHelper(a, b, true));
}
-class MockProfile : public TestingProfile {
+class MockThemeService : public ThemeService {
public:
MOCK_METHOD0(SetNativeTheme, void());
- MOCK_METHOD0(ClearTheme, void());
- MOCK_METHOD0(GetTheme, Extension*());
+ MOCK_METHOD0(UseDefaultTheme, void());
+ MOCK_CONST_METHOD0(GetThemeID, std::string());
};
TEST_F(ThemeUtilTest, SetCurrentThemeDefaultTheme) {
sync_pb::ThemeSpecifics theme_specifics;
+ TestingProfile profile;
+ MockThemeService* mock_theme_service = new MockThemeService;
+ ThemeServiceFactory::GetInstance()->ForceAssociationBetween(&profile,
+ mock_theme_service);
- MockProfile mock_profile;
- EXPECT_CALL(mock_profile, ClearTheme()).Times(1);
+ EXPECT_CALL(*mock_theme_service, UseDefaultTheme()).Times(1);
- SetCurrentThemeFromThemeSpecifics(theme_specifics, &mock_profile);
+ SetCurrentThemeFromThemeSpecifics(theme_specifics, &profile);
}
TEST_F(ThemeUtilTest, SetCurrentThemeSystemTheme) {
sync_pb::ThemeSpecifics theme_specifics;
theme_specifics.set_use_system_theme_by_default(true);
- MockProfile mock_profile;
- EXPECT_CALL(mock_profile, SetNativeTheme()).Times(1);
+ TestingProfile profile;
+ MockThemeService* mock_theme_service = new MockThemeService;
+ ThemeServiceFactory::GetInstance()->ForceAssociationBetween(&profile,
+ mock_theme_service);
- SetCurrentThemeFromThemeSpecifics(theme_specifics, &mock_profile);
+ EXPECT_CALL(*mock_theme_service, SetNativeTheme()).Times(1);
+
+ SetCurrentThemeFromThemeSpecifics(theme_specifics, &profile);
}
// TODO(akalin): Make ExtensionService/ExtensionUpdater testable
@@ -205,15 +215,19 @@ TEST_F(ThemeUtilTest, GetThemeSpecificsHelperCustomThemeDistinct) {
}
TEST_F(ThemeUtilTest, SetCurrentThemeIfNecessaryDefaultThemeNotNecessary) {
- MockProfile mock_profile;
- Extension* extension = NULL;
- EXPECT_CALL(mock_profile, GetTheme()).WillOnce(Return(extension));
+ TestingProfile profile;
+ MockThemeService* mock_theme_service = new MockThemeService;
+ ThemeServiceFactory::GetInstance()->ForceAssociationBetween(&profile,
+ mock_theme_service);
+
+ EXPECT_CALL(*mock_theme_service, GetThemeID()).WillRepeatedly(Return(
+ ThemeService::kDefaultThemeID));
// TODO(akalin): Mock out call to GetPrefs() under TOOLKIT_USES_GTK.
sync_pb::ThemeSpecifics theme_specifics;
SetCurrentThemeFromThemeSpecificsIfNecessary(theme_specifics,
- &mock_profile);
+ &profile);
}
} // namespace
diff --git a/chrome/browser/sync/glue/typed_url_change_processor.cc b/chrome/browser/sync/glue/typed_url_change_processor.cc
index 4651d52..c30a43d 100644
--- a/chrome/browser/sync/glue/typed_url_change_processor.cc
+++ b/chrome/browser/sync/glue/typed_url_change_processor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,8 +12,8 @@
#include "chrome/browser/sync/glue/typed_url_model_associator.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_type.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/glue/typed_url_change_processor.h b/chrome/browser/sync/glue/typed_url_change_processor.h
index 7c81588..501ee0e 100644
--- a/chrome/browser/sync/glue/typed_url_change_processor.h
+++ b/chrome/browser/sync/glue/typed_url_change_processor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,12 +9,12 @@
#include "chrome/browser/sync/glue/change_processor.h"
#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_type.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
class MessageLoop;
class NotificationService;
diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.cc b/chrome/browser/sync/glue/typed_url_data_type_controller.cc
index 316c245..93be84d 100644
--- a/chrome/browser/sync/glue/typed_url_data_type_controller.cc
+++ b/chrome/browser/sync/glue/typed_url_data_type_controller.cc
@@ -1,21 +1,21 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/glue/typed_url_data_type_controller.h"
-#include "base/metrics/histogram.h"
#include "base/logging.h"
+#include "base/metrics/histogram.h"
#include "base/task.h"
#include "base/time.h"
#include "chrome/browser/history/history.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/typed_url_change_processor.h"
#include "chrome/browser/sync/glue/typed_url_model_associator.h"
-#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
-#include "chrome/common/notification_service.h"
+#include "chrome/browser/sync/profile_sync_service.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
namespace browser_sync {
@@ -52,7 +52,10 @@ TypedUrlDataTypeController::TypedUrlDataTypeController(
: profile_sync_factory_(profile_sync_factory),
profile_(profile),
sync_service_(sync_service),
- state_(NOT_RUNNING) {
+ state_(NOT_RUNNING),
+ abort_association_(false),
+ abort_association_complete_(false, false),
+ datatype_stopped_(false, false) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(profile_sync_factory);
DCHECK(profile);
@@ -68,12 +71,13 @@ void TypedUrlDataTypeController::Start(StartCallback* start_callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(start_callback);
if (state_ != NOT_RUNNING || start_callback_.get()) {
- start_callback->Run(BUSY);
+ start_callback->Run(BUSY, FROM_HERE);
delete start_callback;
return;
}
start_callback_.reset(start_callback);
+ abort_association_ = false;
HistoryService* history = profile_->GetHistoryServiceWithoutCreating();
if (history) {
@@ -100,39 +104,65 @@ void TypedUrlDataTypeController::Observe(NotificationType type,
history_service_->ScheduleDBTask(new ControlTask(this, true), this);
}
+// TODO(sync): Blocking the UI thread at shutdown is bad. If we had a way of
+// distinguishing chrome shutdown from sync shutdown, we should be able to avoid
+// this (http://crbug.com/55662). Further, all this functionality should be
+// abstracted to a higher layer, where we could ensure all datatypes are doing
+// the same thing (http://crbug.com/76232).
void TypedUrlDataTypeController::Stop() {
VLOG(1) << "Stopping typed_url data type controller.";
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // If Stop() is called while Start() is waiting for association to
+ // complete, we need to abort the association and wait for the DB
+ // thread to finish the StartImpl() task.
+ if (state_ == ASSOCIATING) {
+ {
+ base::AutoLock lock(abort_association_lock_);
+ abort_association_ = true;
+ if (model_associator_.get())
+ model_associator_->AbortAssociation();
+ }
+ // Wait for the model association to abort.
+ abort_association_complete_.Wait();
+ StartDoneImpl(ABORTED, STOPPING);
+ }
+
+ // If Stop() is called while Start() is waiting for the history service to
+ // load, abort the start.
+ if (state_ == MODEL_STARTING)
+ StartDoneImpl(ABORTED, STOPPING);
+
+ DCHECK(!start_callback_.get());
+
if (change_processor_ != NULL)
sync_service_->DeactivateDataType(this, change_processor_.get());
- if (model_associator_ != NULL)
- model_associator_->DisassociateModels();
-
set_state(NOT_RUNNING);
DCHECK(history_service_.get());
history_service_->ScheduleDBTask(new ControlTask(this, false), this);
+ datatype_stopped_.Wait();
}
bool TypedUrlDataTypeController::enabled() {
return true;
}
-syncable::ModelType TypedUrlDataTypeController::type() {
+syncable::ModelType TypedUrlDataTypeController::type() const {
return syncable::TYPED_URLS;
}
-browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group() {
+browser_sync::ModelSafeGroup TypedUrlDataTypeController::model_safe_group()
+ const {
return browser_sync::GROUP_HISTORY;
}
-const char* TypedUrlDataTypeController::name() const {
+std::string TypedUrlDataTypeController::name() const {
// For logging only.
return "typed_url";
}
-DataTypeController::State TypedUrlDataTypeController::state() {
+DataTypeController::State TypedUrlDataTypeController::state() const {
return state_;
}
@@ -140,13 +170,25 @@ void TypedUrlDataTypeController::StartImpl(history::HistoryBackend* backend) {
VLOG(1) << "TypedUrl data type controller StartImpl called.";
// No additional services need to be started before we can proceed
// with model association.
- ProfileSyncFactory::SyncComponents sync_components =
- profile_sync_factory_->CreateTypedUrlSyncComponents(
- sync_service_,
- backend,
- this);
- model_associator_.reset(sync_components.model_associator);
- change_processor_.reset(sync_components.change_processor);
+ {
+ base::AutoLock lock(abort_association_lock_);
+ if (abort_association_) {
+ abort_association_complete_.Signal();
+ return;
+ }
+ ProfileSyncFactory::SyncComponents sync_components =
+ profile_sync_factory_->CreateTypedUrlSyncComponents(
+ sync_service_,
+ backend,
+ this);
+ model_associator_.reset(sync_components.model_associator);
+ change_processor_.reset(sync_components.change_processor);
+ }
+
+ if (!model_associator_->CryptoReadyIfNecessary()) {
+ StartFailed(NEEDS_CRYPTO);
+ return;
+ }
bool sync_has_nodes = false;
if (!model_associator_->SyncModelHasUserCreatedNodes(&sync_has_nodes)) {
@@ -171,12 +213,17 @@ void TypedUrlDataTypeController::StartDone(
DataTypeController::StartResult result,
DataTypeController::State new_state) {
VLOG(1) << "TypedUrl data type controller StartDone called.";
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(
- this,
- &TypedUrlDataTypeController::StartDoneImpl,
- result,
- new_state));
+
+ abort_association_complete_.Signal();
+ base::AutoLock lock(abort_association_lock_);
+ if (!abort_association_) {
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(
+ this,
+ &TypedUrlDataTypeController::StartDoneImpl,
+ result,
+ new_state));
+ }
}
void TypedUrlDataTypeController::StartDoneImpl(
@@ -185,7 +232,7 @@ void TypedUrlDataTypeController::StartDoneImpl(
VLOG(1) << "TypedUrl data type controller StartDoneImpl called.";
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
set_state(new_state);
- start_callback_->Run(result);
+ start_callback_->Run(result, FROM_HERE);
start_callback_.reset();
if (result == UNRECOVERABLE_ERROR || result == ASSOCIATION_FAILED) {
@@ -198,10 +245,13 @@ void TypedUrlDataTypeController::StartDoneImpl(
void TypedUrlDataTypeController::StopImpl() {
VLOG(1) << "TypedUrl data type controller StopImpl called.";
+ if (model_associator_ != NULL)
+ model_associator_->DisassociateModels();
+
change_processor_.reset();
model_associator_.reset();
- state_ = NOT_RUNNING;
+ datatype_stopped_.Signal();
}
void TypedUrlDataTypeController::StartFailed(StartResult result) {
diff --git a/chrome/browser/sync/glue/typed_url_data_type_controller.h b/chrome/browser/sync/glue/typed_url_data_type_controller.h
index edffa3b..386a01d 100644
--- a/chrome/browser/sync/glue/typed_url_data_type_controller.h
+++ b/chrome/browser/sync/glue/typed_url_data_type_controller.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,13 +9,14 @@
#include <string>
#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/profile_sync_service.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_type.h"
+#include "chrome/browser/sync/profile_sync_service.h"
#include "content/browser/cancelable_request.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
class NotificationSource;
class NotificationDetails;
@@ -52,13 +53,13 @@ class TypedUrlDataTypeController : public DataTypeController,
virtual bool enabled();
- virtual syncable::ModelType type();
+ virtual syncable::ModelType type() const;
- virtual browser_sync::ModelSafeGroup model_safe_group();
+ virtual browser_sync::ModelSafeGroup model_safe_group() const;
- virtual const char* name() const;
+ virtual std::string name() const;
- virtual State state();
+ virtual State state() const;
// UnrecoverableHandler implementation
virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
@@ -109,6 +110,14 @@ class TypedUrlDataTypeController : public DataTypeController,
NotificationRegistrar notification_registrar_;
+ base::Lock abort_association_lock_;
+ bool abort_association_;
+ base::WaitableEvent abort_association_complete_;
+
+ // Barrier to ensure that the datatype has been stopped on the DB thread
+ // from the UI thread.
+ base::WaitableEvent datatype_stopped_;
+
DISALLOW_COPY_AND_ASSIGN(TypedUrlDataTypeController);
};
diff --git a/chrome/browser/sync/glue/typed_url_model_associator.cc b/chrome/browser/sync/glue/typed_url_model_associator.cc
index 02144fc..2c05599 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator.cc
+++ b/chrome/browser/sync/glue/typed_url_model_associator.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -177,7 +177,7 @@ bool TypedUrlModelAssociator::AssociateModels() {
// the history database after closing the write transaction, since
// this is the only thread that writes to the database. We also don't have
// to worry about the sync model getting out of sync, because changes are
- // propogated to the ChangeProcessor on this thread.
+ // propagated to the ChangeProcessor on this thread.
return WriteToHistoryBackend(&titles, &new_urls, &updated_urls,
&new_visits, NULL);
}
@@ -487,4 +487,13 @@ void TypedUrlModelAssociator::DiffVisits(
}
}
+bool TypedUrlModelAssociator::CryptoReadyIfNecessary() {
+ // We only access the cryptographer while holding a transaction.
+ sync_api::ReadTransaction trans(sync_service_->GetUserShare());
+ syncable::ModelTypeSet encrypted_types;
+ sync_service_->GetEncryptedDataTypes(&encrypted_types);
+ return encrypted_types.count(syncable::TYPED_URLS) == 0 ||
+ sync_service_->IsCryptographerReady(&trans);
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/typed_url_model_associator.h b/chrome/browser/sync/glue/typed_url_model_associator.h
index 0ab3533..e9705a6 100644
--- a/chrome/browser/sync/glue/typed_url_model_associator.h
+++ b/chrome/browser/sync/glue/typed_url_model_associator.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -75,6 +75,8 @@ class TypedUrlModelAssociator
virtual void AbortAssociation();
+ virtual bool CryptoReadyIfNecessary();
+
// Not implemented.
virtual const std::string* GetChromeNodeFromSyncId(int64 sync_id);
diff --git a/chrome/browser/sync/glue/ui_model_worker.cc b/chrome/browser/sync/glue/ui_model_worker.cc
index 38f4a98..541a7d8 100644
--- a/chrome/browser/sync/glue/ui_model_worker.cc
+++ b/chrome/browser/sync/glue/ui_model_worker.cc
@@ -7,6 +7,7 @@
#include "base/message_loop.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/synchronization/waitable_event.h"
+#include "content/browser/browser_thread.h"
namespace browser_sync {
@@ -19,7 +20,7 @@ void UIModelWorker::DoWorkAndWaitUntilDone(Callback0::Type* work) {
// with state_ = STOPPED, so it is safe to read / compare in this case.
CHECK_NE(ANNOTATE_UNPROTECTED_READ(state_), STOPPED);
- if (MessageLoop::current() == ui_loop_) {
+ if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
DLOG(WARNING) << "DoWorkAndWaitUntilDone called from "
<< "ui_loop_. Probably a nested invocation?";
work->Run();
@@ -35,17 +36,21 @@ void UIModelWorker::DoWorkAndWaitUntilDone(Callback0::Type* work) {
base::AutoLock lock(lock_);
DCHECK(!pending_work_);
pending_work_ = new CallDoWorkAndSignalTask(work, &work_done, this);
- ui_loop_->PostTask(FROM_HERE, pending_work_);
+ if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, pending_work_)) {
+ LOG(WARNING) << "Could not post work to UI loop.";
+ pending_work_ = NULL;
+ syncapi_event_.Signal();
+ return;
+ }
}
syncapi_event_.Signal(); // Notify that the syncapi produced work for us.
work_done.Wait();
}
-UIModelWorker::UIModelWorker(MessageLoop* ui_loop)
+UIModelWorker::UIModelWorker()
: state_(WORKING),
pending_work_(NULL),
syncapi_has_shutdown_(false),
- ui_loop_(ui_loop),
syncapi_event_(&lock_) {
}
@@ -67,7 +72,7 @@ void UIModelWorker::OnSyncerShutdownComplete() {
}
void UIModelWorker::Stop() {
- DCHECK_EQ(MessageLoop::current(), ui_loop_);
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
base::AutoLock lock(lock_);
DCHECK_EQ(state_, WORKING);
@@ -94,7 +99,7 @@ ModelSafeGroup UIModelWorker::GetModelSafeGroup() {
}
bool UIModelWorker::CurrentThreadIsWorkThread() {
- return MessageLoop::current() == ui_loop_;
+ return BrowserThread::CurrentlyOn(BrowserThread::UI);
}
void UIModelWorker::CallDoWorkAndSignalTask::Run() {
diff --git a/chrome/browser/sync/glue/ui_model_worker.h b/chrome/browser/sync/glue/ui_model_worker.h
index 503d8cb..6a338db 100644
--- a/chrome/browser/sync/glue/ui_model_worker.h
+++ b/chrome/browser/sync/glue/ui_model_worker.h
@@ -31,7 +31,7 @@ namespace browser_sync {
// after the actual syncer pthread has exited.
class UIModelWorker : public browser_sync::ModelSafeWorker {
public:
- explicit UIModelWorker(MessageLoop* ui_loop);
+ UIModelWorker();
virtual ~UIModelWorker();
// A simple task to signal a waitable event after Run()ning a Closure.
@@ -77,7 +77,7 @@ class UIModelWorker : public browser_sync::ModelSafeWorker {
void OnSyncerShutdownComplete();
// Callback from |pending_work_| to notify us that it has been run.
- // Called on |ui_loop_|.
+ // Called on ui loop.
void OnTaskCompleted() { pending_work_ = NULL; }
private:
@@ -110,9 +110,6 @@ class UIModelWorker : public browser_sync::ModelSafeWorker {
// the UI thread in Stop().
bool syncapi_has_shutdown_;
- // The UI model home-sweet-home MessageLoop.
- MessageLoop* const ui_loop_;
-
// We use a Lock for all data members and a ConditionVariable to synchronize.
// We do this instead of using a WaitableEvent and a bool condition in order
// to guard against races that could arise due to the fact that the lack of a
diff --git a/chrome/browser/sync/glue/ui_model_worker_unittest.cc b/chrome/browser/sync/glue/ui_model_worker_unittest.cc
index a2a1dde..c44af67 100644
--- a/chrome/browser/sync/glue/ui_model_worker_unittest.cc
+++ b/chrome/browser/sync/glue/ui_model_worker_unittest.cc
@@ -1,12 +1,13 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "base/callback.h"
+#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
-#include "base/ref_counted.h"
#include "base/threading/thread.h"
#include "base/synchronization/waitable_event.h"
+#include "content/browser/browser_thread.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/ui_model_worker.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -18,22 +19,20 @@ using namespace sync_api;
class UIModelWorkerVisitor {
public:
- UIModelWorkerVisitor(MessageLoop* faux_ui_loop,
- base::WaitableEvent* was_run,
+ UIModelWorkerVisitor(base::WaitableEvent* was_run,
bool quit_loop)
- : faux_ui_loop_(faux_ui_loop), quit_loop_when_run_(quit_loop),
+ : quit_loop_when_run_(quit_loop),
was_run_(was_run) { }
virtual ~UIModelWorkerVisitor() { }
virtual void DoWork() {
- EXPECT_EQ(MessageLoop::current(), faux_ui_loop_);
+ EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::UI));
was_run_->Signal();
if (quit_loop_when_run_)
MessageLoop::current()->Quit();
}
private:
- MessageLoop* faux_ui_loop_;
bool quit_loop_when_run_;
base::WaitableEvent* was_run_;
DISALLOW_COPY_AND_ASSIGN(UIModelWorkerVisitor);
@@ -110,7 +109,8 @@ class UIModelWorkerTest : public testing::Test {
virtual void SetUp() {
faux_syncer_thread_.Start();
- bmw_ = new UIModelWorker(&faux_ui_loop_);
+ ui_thread_.reset(new BrowserThread(BrowserThread::UI, &faux_ui_loop_));
+ bmw_ = new UIModelWorker();
syncer_.reset(new Syncer(bmw_.get()));
}
@@ -118,9 +118,9 @@ class UIModelWorkerTest : public testing::Test {
UIModelWorker* bmw() { return bmw_.get(); }
base::Thread* core_thread() { return &faux_core_thread_; }
base::Thread* syncer_thread() { return &faux_syncer_thread_; }
- MessageLoop* ui_loop() { return &faux_ui_loop_; }
private:
MessageLoop faux_ui_loop_;
+ scoped_ptr<BrowserThread> ui_thread_;
base::Thread faux_syncer_thread_;
base::Thread faux_core_thread_;
scoped_refptr<UIModelWorker> bmw_;
@@ -130,7 +130,7 @@ class UIModelWorkerTest : public testing::Test {
TEST_F(UIModelWorkerTest, ScheduledWorkRunsOnUILoop) {
base::WaitableEvent v_was_run(false, false);
scoped_ptr<UIModelWorkerVisitor> v(
- new UIModelWorkerVisitor(ui_loop(), &v_was_run, true));
+ new UIModelWorkerVisitor(&v_was_run, true));
syncer_thread()->message_loop()->PostTask(FROM_HERE,
new FakeSyncShareTask(syncer(), v.get()));
@@ -163,7 +163,7 @@ TEST_F(UIModelWorkerTest, StopWithPendingWork) {
core_thread()->Start();
base::WaitableEvent v_ran(false, false);
scoped_ptr<UIModelWorkerVisitor> v(new UIModelWorkerVisitor(
- ui_loop(), &v_ran, false));
+ &v_ran, false));
base::WaitableEvent* jobs[] = { &v_ran };
// The current message loop is not running, so queue a task to cause
@@ -195,13 +195,13 @@ TEST_F(UIModelWorkerTest, HypotheticalManualPumpFlooding) {
// Our ammunition.
base::WaitableEvent fox1_ran(false, false);
scoped_ptr<UIModelWorkerVisitor> fox1(new UIModelWorkerVisitor(
- ui_loop(), &fox1_ran, false));
+ &fox1_ran, false));
base::WaitableEvent fox2_ran(false, false);
scoped_ptr<UIModelWorkerVisitor> fox2(new UIModelWorkerVisitor(
- ui_loop(), &fox2_ran, false));
+ &fox2_ran, false));
base::WaitableEvent fox3_ran(false, false);
scoped_ptr<UIModelWorkerVisitor> fox3(new UIModelWorkerVisitor(
- ui_loop(), &fox3_ran, false));
+ &fox3_ran, false));
base::WaitableEvent* jobs[] = { &fox1_ran, &fox2_ran, &fox3_ran };
// The current message loop is not running, so queue a task to cause
diff --git a/chrome/browser/sync/js_arg_list.cc b/chrome/browser/sync/js_arg_list.cc
index 23165a3..a3e4e33 100644
--- a/chrome/browser/sync/js_arg_list.cc
+++ b/chrome/browser/sync/js_arg_list.cc
@@ -5,7 +5,7 @@
#include "chrome/browser/sync/js_arg_list.h"
#include "base/json/json_writer.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/js_arg_list.h b/chrome/browser/sync/js_arg_list.h
index 66d1b21..90c1f0f 100644
--- a/chrome/browser/sync/js_arg_list.h
+++ b/chrome/browser/sync/js_arg_list.h
@@ -11,7 +11,7 @@
#include <string>
#include <vector>
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "base/values.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/js_arg_list_unittest.cc b/chrome/browser/sync/js_arg_list_unittest.cc
index 961a46b..beed519 100644
--- a/chrome/browser/sync/js_arg_list_unittest.cc
+++ b/chrome/browser/sync/js_arg_list_unittest.cc
@@ -4,7 +4,7 @@
#include "chrome/browser/sync/js_arg_list.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/js_sync_manager_observer.cc b/chrome/browser/sync/js_sync_manager_observer.cc
index 3999fdd..ef9f34d 100644
--- a/chrome/browser/sync/js_sync_manager_observer.cc
+++ b/chrome/browser/sync/js_sync_manager_observer.cc
@@ -98,19 +98,20 @@ void JsSyncManagerObserver::OnEncryptionComplete(
JsArgList(return_args), NULL);
}
+void JsSyncManagerObserver::OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) {
+ ListValue return_args;
+ return_args.Append(syncable::ModelTypeSetToValue(types));
+ parent_router_->RouteJsEvent("onMigrationNeededForTypes",
+ JsArgList(return_args), NULL);
+ // TODO(akalin): Bug 79247. Hook up JS boiler plate!
+}
+
void JsSyncManagerObserver::OnInitializationComplete() {
parent_router_->RouteJsEvent("onInitializationComplete",
JsArgList(), NULL);
}
-void JsSyncManagerObserver::OnPaused() {
- parent_router_->RouteJsEvent("onPaused", JsArgList(), NULL);
-}
-
-void JsSyncManagerObserver::OnResumed() {
- parent_router_->RouteJsEvent("onResumed", JsArgList(), NULL);
-}
-
void JsSyncManagerObserver::OnStopSyncingPermanently() {
parent_router_->RouteJsEvent("onStopSyncingPermanently",
JsArgList(), NULL);
diff --git a/chrome/browser/sync/js_sync_manager_observer.h b/chrome/browser/sync/js_sync_manager_observer.h
index 2fa8638..9436f52 100644
--- a/chrome/browser/sync/js_sync_manager_observer.h
+++ b/chrome/browser/sync/js_sync_manager_observer.h
@@ -40,11 +40,10 @@ class JsSyncManagerObserver : public sync_api::SyncManager::Observer {
virtual void OnEncryptionComplete(
const syncable::ModelTypeSet& encrypted_types);
virtual void OnInitializationComplete();
- virtual void OnPaused();
- virtual void OnResumed();
virtual void OnStopSyncingPermanently();
virtual void OnClearServerDataSucceeded();
virtual void OnClearServerDataFailed();
+ virtual void OnMigrationNeededForTypes(const syncable::ModelTypeSet& types);
private:
JsEventRouter* parent_router_;
diff --git a/chrome/browser/sync/js_sync_manager_observer_unittest.cc b/chrome/browser/sync/js_sync_manager_observer_unittest.cc
index f5ed961..3631963 100644
--- a/chrome/browser/sync/js_sync_manager_observer_unittest.cc
+++ b/chrome/browser/sync/js_sync_manager_observer_unittest.cc
@@ -13,7 +13,7 @@
#include "chrome/browser/sync/js_test_util.h"
#include "chrome/browser/sync/sessions/session_state.h"
#include "chrome/browser/sync/syncable/model_type.h"
-#include "chrome/test/sync/engine/test_directory_setter_upper.h"
+#include "chrome/test/sync/engine/test_user_share.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace browser_sync {
@@ -39,12 +39,7 @@ TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) {
EXPECT_CALL(mock_router_,
RouteJsEvent("onPassphraseFailed",
HasArgs(JsArgList()), NULL));
- EXPECT_CALL(mock_router_,
- RouteJsEvent("onPaused",
- HasArgs(JsArgList()), NULL));
- EXPECT_CALL(mock_router_,
- RouteJsEvent("onResumed",
- HasArgs(JsArgList()), NULL));
+
EXPECT_CALL(mock_router_,
RouteJsEvent("onStopSyncingPermanently",
HasArgs(JsArgList()), NULL));
@@ -57,8 +52,6 @@ TEST_F(JsSyncManagerObserverTest, NoArgNotifiations) {
sync_manager_observer_.OnInitializationComplete();
sync_manager_observer_.OnPassphraseFailed();
- sync_manager_observer_.OnPaused();
- sync_manager_observer_.OnResumed();
sync_manager_observer_.OnStopSyncingPermanently();
sync_manager_observer_.OnClearServerDataSucceeded();
sync_manager_observer_.OnClearServerDataFailed();
@@ -173,6 +166,28 @@ TEST_F(JsSyncManagerObserverTest, OnEncryptionComplete) {
sync_manager_observer_.OnEncryptionComplete(encrypted_types);
}
+
+TEST_F(JsSyncManagerObserverTest, OnMigrationNeededForTypes) {
+ ListValue expected_args;
+ ListValue* type_values = new ListValue();
+ syncable::ModelTypeSet types;
+
+ expected_args.Append(type_values);
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType type = syncable::ModelTypeFromInt(i);
+ types.insert(type);
+ type_values->Append(Value::CreateStringValue(
+ syncable::ModelTypeToString(type)));
+ }
+
+ EXPECT_CALL(mock_router_,
+ RouteJsEvent("onMigrationNeededForTypes",
+ HasArgsAsList(expected_args), NULL));
+
+ sync_manager_observer_.OnMigrationNeededForTypes(types);
+}
+
namespace {
// Makes a node of the given model type. Returns the id of the
@@ -194,11 +209,8 @@ int64 MakeNode(sync_api::UserShare* share, syncable::ModelType model_type) {
TEST_F(JsSyncManagerObserverTest, OnChangesApplied) {
InSequence dummy;
- TestDirectorySetterUpper setter_upper;
- sync_api::UserShare share;
- setter_upper.SetUp();
- share.dir_manager.reset(setter_upper.manager());
- share.name = setter_upper.name();
+ TestUserShare test_user_share;
+ test_user_share.SetUp();
// We don't test with passwords as that requires additional setup.
@@ -206,7 +218,8 @@ TEST_F(JsSyncManagerObserverTest, OnChangesApplied) {
sync_api::SyncManager::ChangeRecord changes[syncable::MODEL_TYPE_COUNT];
for (int i = syncable::AUTOFILL_PROFILE;
i < syncable::MODEL_TYPE_COUNT; ++i) {
- changes[i].id = MakeNode(&share, syncable::ModelTypeFromInt(i));
+ changes[i].id =
+ MakeNode(test_user_share.user_share(), syncable::ModelTypeFromInt(i));
switch (i % 3) {
case 0:
changes[i].action =
@@ -222,7 +235,7 @@ TEST_F(JsSyncManagerObserverTest, OnChangesApplied) {
break;
}
{
- sync_api::ReadTransaction trans(&share);
+ sync_api::ReadTransaction trans(test_user_share.user_share());
sync_api::ReadNode node(&trans);
EXPECT_TRUE(node.InitByIdLookup(changes[i].id));
changes[i].specifics = node.GetEntry()->Get(syncable::SPECIFICS);
@@ -243,7 +256,7 @@ TEST_F(JsSyncManagerObserverTest, OnChangesApplied) {
ListValue* expected_changes = new ListValue();
expected_args.Append(expected_changes);
for (int j = i; j < syncable::MODEL_TYPE_COUNT; ++j) {
- sync_api::ReadTransaction trans(&share);
+ sync_api::ReadTransaction trans(test_user_share.user_share());
expected_changes->Append(changes[j].ToValue(&trans));
}
EXPECT_CALL(mock_router_,
@@ -254,15 +267,13 @@ TEST_F(JsSyncManagerObserverTest, OnChangesApplied) {
// Fire OnChangesApplied() for each data type.
for (int i = syncable::AUTOFILL_PROFILE;
i < syncable::MODEL_TYPE_COUNT; ++i) {
- sync_api::ReadTransaction trans(&share);
+ sync_api::ReadTransaction trans(test_user_share.user_share());
sync_manager_observer_.OnChangesApplied(syncable::ModelTypeFromInt(i),
&trans, &changes[i],
syncable::MODEL_TYPE_COUNT - i);
}
- // |share.dir_manager| does not actually own its value.
- ignore_result(share.dir_manager.release());
- setter_upper.TearDown();
+ test_user_share.TearDown();
}
} // namespace
diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc
index 7fec7f4..de3a6d7 100644
--- a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc
+++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -12,7 +12,6 @@
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/string_number_conversions.h"
-#include "chrome/browser/sync/sync_constants.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "jingle/notifier/listener/xml_element_util.h"
#include "talk/xmpp/constants.h"
@@ -25,6 +24,7 @@ namespace sync_notifier {
namespace {
const char kBotJid[] = "tango@bot.talk.google.com";
+const char kServiceUrl[] = "http://www.google.com/chrome/sync";
const buzz::QName kQnData("google:notifier", "data");
const buzz::QName kQnSeq("", "seq");
@@ -167,8 +167,7 @@ class CacheInvalidationSendMessageTask : public buzz::XmppTask {
iq->AddElement(cache_invalidation_iq_packet);
cache_invalidation_iq_packet->SetAttr(kQnSeq, base::IntToString(seq));
cache_invalidation_iq_packet->SetAttr(kQnSid, sid);
- cache_invalidation_iq_packet->SetAttr(kQnServiceUrl,
- browser_sync::kSyncServiceUrl);
+ cache_invalidation_iq_packet->SetAttr(kQnServiceUrl, kServiceUrl);
cache_invalidation_iq_packet->SetBodyText(msg);
return iq;
}
diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h
index a879836..707dacb 100644
--- a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h
+++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -13,9 +13,9 @@
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
-#include "base/scoped_callback_factory.h"
+#include "base/memory/scoped_callback_factory.h"
+#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
-#include "base/weak_ptr.h"
namespace invalidation {
class InvalidationClient;
diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc b/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc
index 1d30bbc..52f9591 100644
--- a/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc
+++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler_unittest.cc
@@ -1,12 +1,12 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/notifier/cache_invalidation_packet_handler.h"
#include "base/base64.h"
+#include "base/memory/weak_ptr.h"
#include "base/message_loop.h"
-#include "base/weak_ptr.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "jingle/notifier/base/fake_base_task.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync/notifier/chrome_invalidation_client.cc b/chrome/browser/sync/notifier/chrome_invalidation_client.cc
index 0c38936..074e481 100644
--- a/chrome/browser/sync/notifier/chrome_invalidation_client.cc
+++ b/chrome/browser/sync/notifier/chrome_invalidation_client.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -75,6 +75,7 @@ void ChromeInvalidationClient::Start(
ChangeBaseTask(base_task);
registration_manager_.reset(
new RegistrationManager(invalidation_client_.get()));
+ registration_manager_->SetRegisteredTypes(registered_types_);
}
void ChromeInvalidationClient::ChangeBaseTask(
@@ -105,8 +106,11 @@ void ChromeInvalidationClient::Stop() {
void ChromeInvalidationClient::RegisterTypes(
const syncable::ModelTypeSet& types) {
DCHECK(non_thread_safe_.CalledOnValidThread());
-
- registration_manager_->SetRegisteredTypes(types);
+ registered_types_ = types;
+ if (registration_manager_.get()) {
+ registration_manager_->SetRegisteredTypes(registered_types_);
+ }
+ // TODO(akalin): Clear invalidation versions for unregistered types.
}
void ChromeInvalidationClient::Invalidate(
@@ -116,30 +120,72 @@ void ChromeInvalidationClient::Invalidate(
DCHECK(invalidation::IsCallbackRepeatable(callback));
VLOG(1) << "Invalidate: " << InvalidationToString(invalidation);
syncable::ModelType model_type;
- if (ObjectIdToRealModelType(invalidation.object_id(), &model_type)) {
- std::string payload;
- // payload() CHECK()'s has_payload(), so we must check it ourselves first.
- if (invalidation.has_payload())
- payload = invalidation.payload();
-
- listener_->OnInvalidate(model_type, payload);
- } else {
+ if (!ObjectIdToRealModelType(invalidation.object_id(), &model_type)) {
LOG(WARNING) << "Could not get invalidation model type; "
<< "invalidating everything";
- listener_->OnInvalidateAll();
+ EmitInvalidation(registered_types_, std::string());
+ RunAndDeleteClosure(callback);
+ return;
}
+ // The invalidation API spec allows for the possibility of redundant
+ // invalidations, so keep track of the max versions and drop
+ // invalidations with old versions.
+ //
+ // TODO(akalin): Now that we keep track of registered types, we
+ // should drop invalidations for unregistered types. We may also
+ // have to filter it at a higher level, as invalidations for
+ // newly-unregistered types may already be in flight.
+ //
+ // TODO(akalin): Persist |max_invalidation_versions_| somehow.
+ if (invalidation.version() != UNKNOWN_OBJECT_VERSION) {
+ std::map<syncable::ModelType, int64>::const_iterator it =
+ max_invalidation_versions_.find(model_type);
+ if ((it != max_invalidation_versions_.end()) &&
+ (invalidation.version() <= it->second)) {
+ // Drop redundant invalidations.
+ RunAndDeleteClosure(callback);
+ return;
+ }
+ max_invalidation_versions_[model_type] = invalidation.version();
+ }
+
+ std::string payload;
+ // payload() CHECK()'s has_payload(), so we must check it ourselves first.
+ if (invalidation.has_payload())
+ payload = invalidation.payload();
+
+ syncable::ModelTypeSet types;
+ types.insert(model_type);
+ EmitInvalidation(types, payload);
+ // TODO(akalin): We should really |callback| only after we get the
+ // updates from the sync server. (see http://crbug.com/78462).
RunAndDeleteClosure(callback);
}
+// This should behave as if we got an invalidation with version
+// UNKNOWN_OBJECT_VERSION for all known data types.
void ChromeInvalidationClient::InvalidateAll(
invalidation::Closure* callback) {
DCHECK(non_thread_safe_.CalledOnValidThread());
DCHECK(invalidation::IsCallbackRepeatable(callback));
VLOG(1) << "InvalidateAll";
- listener_->OnInvalidateAll();
+ EmitInvalidation(registered_types_, std::string());
+ // TODO(akalin): We should really |callback| only after we get the
+ // updates from the sync server. (see http://crbug.com/76482).
RunAndDeleteClosure(callback);
}
+void ChromeInvalidationClient::EmitInvalidation(
+ const syncable::ModelTypeSet& types, const std::string& payload) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ // TODO(akalin): Move all uses of ModelTypeBitSet for invalidations
+ // to ModelTypeSet.
+ syncable::ModelTypePayloadMap type_payloads =
+ syncable::ModelTypePayloadMapFromBitSet(
+ syncable::ModelTypeBitSetFromSet(types), payload);
+ listener_->OnInvalidate(type_payloads);
+}
+
void ChromeInvalidationClient::RegistrationStateChanged(
const invalidation::ObjectId& object_id,
invalidation::RegistrationState new_state,
@@ -159,9 +205,8 @@ void ChromeInvalidationClient::RegistrationStateChanged(
}
if (new_state != invalidation::RegistrationState_REGISTERED) {
- // TODO(akalin): Figure out something else to do if the failure
- // isn't transient. Even if it is transient, we may still want to
- // add exponential back-off or limit the number of attempts.
+ // We don't care about |unknown_hint|; we let
+ // |registration_manager_| handle the registration backoff policy.
registration_manager_->MarkRegistrationLost(model_type);
}
}
@@ -175,19 +220,9 @@ void ChromeInvalidationClient::AllRegistrationsLost(
RunAndDeleteClosure(callback);
}
-void ChromeInvalidationClient::RegistrationLost(
- const invalidation::ObjectId& object_id,
- invalidation::Closure* callback) {
- DCHECK(non_thread_safe_.CalledOnValidThread());
- DCHECK(invalidation::IsCallbackRepeatable(callback));
- VLOG(1) << "RegistrationLost: " << ObjectIdToString(object_id);
- syncable::ModelType model_type;
- if (ObjectIdToRealModelType(object_id, &model_type)) {
- registration_manager_->MarkRegistrationLost(model_type);
- } else {
- LOG(WARNING) << "Could not get object id model type; ignoring";
- }
- RunAndDeleteClosure(callback);
+void ChromeInvalidationClient::SessionStatusChanged(bool has_session) {
+ VLOG(1) << "SessionStatusChanged: " << has_session;
+ listener_->OnSessionStatusChanged(has_session);
}
void ChromeInvalidationClient::WriteState(const std::string& state) {
diff --git a/chrome/browser/sync/notifier/chrome_invalidation_client.h b/chrome/browser/sync/notifier/chrome_invalidation_client.h
index 904b91d..7ab0f3a 100644
--- a/chrome/browser/sync/notifier/chrome_invalidation_client.h
+++ b/chrome/browser/sync/notifier/chrome_invalidation_client.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -9,18 +9,25 @@
#define CHROME_BROWSER_SYNC_NOTIFIER_CHROME_INVALIDATION_CLIENT_H_
#pragma once
+#include <map>
#include <string>
#include "base/basictypes.h"
-#include "base/scoped_callback_factory.h"
-#include "base/scoped_ptr.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_callback_factory.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/threading/non_thread_safe.h"
-#include "base/weak_ptr.h"
#include "chrome/browser/sync/notifier/chrome_system_resources.h"
#include "chrome/browser/sync/notifier/state_writer.h"
#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
#include "google/cacheinvalidation/invalidation-client.h"
+// TODO(akalin): Move invalidation::InvalidationListener into its own
+// file and include that instead of invalidation-client.h (which
+// includes generated protobuf header files).
+
namespace talk_base {
class Task;
} // namespace
@@ -30,9 +37,6 @@ namespace sync_notifier {
class CacheInvalidationPacketHandler;
class RegistrationManager;
-// TODO(akalin): Hook this up to a NetworkChangeNotifier so we can
-// properly notify invalidation_client_.
-
class ChromeInvalidationClient
: public invalidation::InvalidationListener,
public StateWriter {
@@ -41,10 +45,10 @@ class ChromeInvalidationClient
public:
virtual ~Listener();
- virtual void OnInvalidate(syncable::ModelType model_type,
- const std::string& payload) = 0;
+ virtual void OnInvalidate(
+ const syncable::ModelTypePayloadMap& type_payloads) = 0;
- virtual void OnInvalidateAll() = 0;
+ virtual void OnSessionStatusChanged(bool has_session) = 0;
};
ChromeInvalidationClient();
@@ -67,31 +71,22 @@ class ChromeInvalidationClient
void ChangeBaseTask(base::WeakPtr<talk_base::Task> base_task);
// Register the sync types that we're interested in getting
- // notifications for. Must only be called between calls to Start()
- // and Stop().
+ // notifications for. May be called at any time.
void RegisterTypes(const syncable::ModelTypeSet& types);
// invalidation::InvalidationListener implementation.
- //
- // TODO(akalin): Move these into a private inner class.
-
virtual void Invalidate(const invalidation::Invalidation& invalidation,
- invalidation::Closure* callback);
-
- virtual void InvalidateAll(invalidation::Closure* callback);
-
+ invalidation::Closure* callback) OVERRIDE;
+ virtual void InvalidateAll(invalidation::Closure* callback) OVERRIDE;
virtual void RegistrationStateChanged(
const invalidation::ObjectId& object_id,
invalidation::RegistrationState new_state,
- const invalidation::UnknownHint& unknown_hint);
-
- virtual void AllRegistrationsLost(invalidation::Closure* callback);
-
- virtual void RegistrationLost(const invalidation::ObjectId& object_id,
- invalidation::Closure* callback);
+ const invalidation::UnknownHint& unknown_hint) OVERRIDE;
+ virtual void AllRegistrationsLost(invalidation::Closure* callback) OVERRIDE;
+ virtual void SessionStatusChanged(bool has_session) OVERRIDE;
// StateWriter implementation.
- virtual void WriteState(const std::string& state);
+ virtual void WriteState(const std::string& state) OVERRIDE;
private:
friend class ChromeInvalidationClientTest;
@@ -99,6 +94,8 @@ class ChromeInvalidationClient
// Should only be called between calls to Start() and Stop().
void HandleOutboundPacket(
invalidation::NetworkEndpoint* const& network_endpoint);
+ void EmitInvalidation(
+ const syncable::ModelTypeSet& types, const std::string& payload);
base::NonThreadSafe non_thread_safe_;
ChromeSystemResources chrome_system_resources_;
@@ -111,6 +108,9 @@ class ChromeInvalidationClient
scoped_ptr<CacheInvalidationPacketHandler>
cache_invalidation_packet_handler_;
scoped_ptr<RegistrationManager> registration_manager_;
+ std::map<syncable::ModelType, int64> max_invalidation_versions_;
+ // Stored to pass to |registration_manager_| on start.
+ syncable::ModelTypeSet registered_types_;
DISALLOW_COPY_AND_ASSIGN(ChromeInvalidationClient);
};
diff --git a/chrome/browser/sync/notifier/chrome_invalidation_client_unittest.cc b/chrome/browser/sync/notifier/chrome_invalidation_client_unittest.cc
index 13438b9..5a99e77 100644
--- a/chrome/browser/sync/notifier/chrome_invalidation_client_unittest.cc
+++ b/chrome/browser/sync/notifier/chrome_invalidation_client_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -7,6 +7,8 @@
#include "base/message_loop.h"
#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
#include "chrome/browser/sync/notifier/state_writer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
#include "jingle/notifier/base/fake_base_task.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -15,12 +17,21 @@ namespace sync_notifier {
using ::testing::_;
using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace {
+
+const char kClientId[] = "client_id";
+const char kClientInfo[] = "client_info";
+const char kState[] = "state";
+
+static const int64 kUnknownVersion =
+ invalidation::InvalidationListener::UNKNOWN_OBJECT_VERSION;
class MockListener : public ChromeInvalidationClient::Listener {
public:
- MOCK_METHOD2(OnInvalidate, void(syncable::ModelType,
- const std::string& payload));
- MOCK_METHOD0(OnInvalidateAll, void());
+ MOCK_METHOD1(OnInvalidate, void(const syncable::ModelTypePayloadMap&));
+ MOCK_METHOD1(OnSessionStatusChanged, void(bool));
};
class MockStateWriter : public StateWriter {
@@ -28,18 +39,15 @@ class MockStateWriter : public StateWriter {
MOCK_METHOD1(WriteState, void(const std::string&));
};
-class MockInvalidationClient : public invalidation::InvalidationClient {
+class MockCallback {
public:
- MOCK_METHOD1(Start, void(const std::string& str));
- MOCK_METHOD1(Register, void(const invalidation::ObjectId&));
- MOCK_METHOD1(Unregister, void(const invalidation::ObjectId&));
- MOCK_METHOD0(network_endpoint, invalidation::NetworkEndpoint*());
+ MOCK_METHOD0(Run, void());
+
+ invalidation::Closure* MakeClosure() {
+ return invalidation::NewPermanentCallback(this, &MockCallback::Run);
+ }
};
-namespace {
-const char kClientId[] = "client_id";
-const char kClientInfo[] = "client_info";
-const char kState[] = "state";
} // namespace
class ChromeInvalidationClientTest : public testing::Test {
@@ -56,7 +64,7 @@ class ChromeInvalidationClientTest : public testing::Test {
}
// Simulates DoInformOutboundListener() from network-manager.cc.
- virtual void SimulateInformOutboundListener() {
+ void SimulateInformOutboundListener() {
// Explicitness hack here to work around broken callback
// implementations.
void (invalidation::NetworkCallback::*run_function)(
@@ -69,13 +77,164 @@ class ChromeInvalidationClientTest : public testing::Test {
client_.invalidation_client_->network_endpoint()));
}
+ // |payload| can be NULL, but not |type_name|.
+ void FireInvalidate(const char* type_name,
+ int64 version, const char* payload) {
+ const invalidation::ObjectId object_id(
+ invalidation::ObjectSource::CHROME_SYNC, type_name);
+ std::string payload_tmp = payload ? payload : "";
+ const invalidation::Invalidation invalidation(
+ object_id, version, payload ? &payload_tmp : NULL, NULL);
+ MockCallback mock_callback;
+ EXPECT_CALL(mock_callback, Run());
+ client_.Invalidate(invalidation, mock_callback.MakeClosure());
+ }
+
+ void FireInvalidateAll() {
+ MockCallback mock_callback;
+ EXPECT_CALL(mock_callback, Run());
+ client_.InvalidateAll(mock_callback.MakeClosure());
+ }
+
MessageLoop message_loop_;
- MockListener mock_listener_;
- MockStateWriter mock_state_writer_;
+ StrictMock<MockListener> mock_listener_;
+ StrictMock<MockStateWriter> mock_state_writer_;
notifier::FakeBaseTask fake_base_task_;
ChromeInvalidationClient client_;
};
+namespace {
+
+syncable::ModelTypePayloadMap MakeMap(syncable::ModelType model_type,
+ const std::string& payload) {
+ syncable::ModelTypePayloadMap type_payloads;
+ type_payloads[model_type] = payload;
+ return type_payloads;
+}
+
+syncable::ModelTypePayloadMap MakeMapFromSet(syncable::ModelTypeSet types,
+ const std::string& payload) {
+ syncable::ModelTypePayloadMap type_payloads;
+ for (syncable::ModelTypeSet::const_iterator it = types.begin();
+ it != types.end(); ++it) {
+ type_payloads[*it] = payload;
+ }
+ return type_payloads;
+}
+
+} // namespace
+
+TEST_F(ChromeInvalidationClientTest, InvalidateBadObjectId) {
+ syncable::ModelTypeSet types;
+ types.insert(syncable::BOOKMARKS);
+ types.insert(syncable::APPS);
+ client_.RegisterTypes(types);
+ EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
+ FireInvalidate("bad", 1, NULL);
+}
+
+TEST_F(ChromeInvalidationClientTest, InvalidateNoPayload) {
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::BOOKMARKS, "")));
+ FireInvalidate("BOOKMARK", 1, NULL);
+}
+
+TEST_F(ChromeInvalidationClientTest, InvalidateWithPayload) {
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::PREFERENCES, "payload")));
+ FireInvalidate("PREFERENCE", 1, "payload");
+}
+
+TEST_F(ChromeInvalidationClientTest, InvalidateVersion) {
+ using ::testing::Mock;
+
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::APPS, "")));
+
+ // Should trigger.
+ FireInvalidate("APP", 1, NULL);
+
+ Mock::VerifyAndClearExpectations(&mock_listener_);
+
+ // Should be dropped.
+ FireInvalidate("APP", 1, NULL);
+}
+
+TEST_F(ChromeInvalidationClientTest, InvalidateUnknownVersion) {
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::EXTENSIONS, "")))
+ .Times(2);
+
+ // Should trigger twice.
+ FireInvalidate("EXTENSION", kUnknownVersion, NULL);
+ FireInvalidate("EXTENSION", kUnknownVersion, NULL);
+}
+
+TEST_F(ChromeInvalidationClientTest, InvalidateVersionMultipleTypes) {
+ using ::testing::Mock;
+
+ syncable::ModelTypeSet types;
+ types.insert(syncable::BOOKMARKS);
+ types.insert(syncable::APPS);
+ client_.RegisterTypes(types);
+
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::APPS, "")));
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::EXTENSIONS, "")));
+
+ // Should trigger both.
+ FireInvalidate("APP", 3, NULL);
+ FireInvalidate("EXTENSION", 2, NULL);
+
+ Mock::VerifyAndClearExpectations(&mock_listener_);
+
+ // Should both be dropped.
+ FireInvalidate("APP", 1, NULL);
+ FireInvalidate("EXTENSION", 1, NULL);
+
+ Mock::VerifyAndClearExpectations(&mock_listener_);
+
+ // InvalidateAll shouldn't change any version state.
+ EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
+ FireInvalidateAll();
+
+ Mock::VerifyAndClearExpectations(&mock_listener_);
+
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::PREFERENCES, "")));
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::EXTENSIONS, "")));
+ EXPECT_CALL(mock_listener_,
+ OnInvalidate(MakeMap(syncable::APPS, "")));
+
+ // Should trigger all three.
+ FireInvalidate("PREFERENCE", 5, NULL);
+ FireInvalidate("EXTENSION", 3, NULL);
+ FireInvalidate("APP", 4, NULL);
+}
+
+TEST_F(ChromeInvalidationClientTest, InvalidateAll) {
+ syncable::ModelTypeSet types;
+ types.insert(syncable::PREFERENCES);
+ types.insert(syncable::EXTENSIONS);
+ client_.RegisterTypes(types);
+ EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
+ FireInvalidateAll();
+}
+
+TEST_F(ChromeInvalidationClientTest, RegisterTypes) {
+ syncable::ModelTypeSet types;
+ types.insert(syncable::PREFERENCES);
+ types.insert(syncable::EXTENSIONS);
+ client_.RegisterTypes(types);
+ // Registered types should be preserved across Stop/Start.
+ TearDown();
+ SetUp();
+ EXPECT_CALL(mock_listener_, OnInvalidate(MakeMapFromSet(types, "")));
+ FireInvalidateAll();
+}
+
// Outbound packet sending should be resilient to
// changing/disappearing base tasks.
TEST_F(ChromeInvalidationClientTest, OutboundPackets) {
diff --git a/chrome/browser/sync/notifier/chrome_system_resources.h b/chrome/browser/sync/notifier/chrome_system_resources.h
index 06b8001..a3f9875 100644
--- a/chrome/browser/sync/notifier/chrome_system_resources.h
+++ b/chrome/browser/sync/notifier/chrome_system_resources.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -13,8 +13,8 @@
#include <set>
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "base/task.h"
#include "base/threading/non_thread_safe.h"
#include "chrome/browser/sync/notifier/state_writer.h"
diff --git a/chrome/browser/sync/notifier/invalidation_notifier.cc b/chrome/browser/sync/notifier/invalidation_notifier.cc
new file mode 100644
index 0000000..03bf2b7
--- /dev/null
+++ b/chrome/browser/sync/notifier/invalidation_notifier.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/invalidation_notifier.h"
+
+#include "base/logging.h"
+#include "base/message_loop_proxy.h"
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
+#include "chrome/browser/sync/protocol/service_constants.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "jingle/notifier/base/const_communicator.h"
+#include "jingle/notifier/base/notifier_options_util.h"
+#include "jingle/notifier/communicator/connection_options.h"
+#include "net/base/host_port_pair.h"
+#include "net/url_request/url_request_context.h"
+#include "talk/xmpp/jid.h"
+#include "talk/xmpp/xmppclientsettings.h"
+
+namespace sync_notifier {
+
+InvalidationNotifier::InvalidationNotifier(
+ const notifier::NotifierOptions& notifier_options,
+ const std::string& client_info)
+ : state_(STOPPED),
+ notifier_options_(notifier_options),
+ client_info_(client_info) {
+ DCHECK_EQ(notifier::NOTIFICATION_SERVER,
+ notifier_options.notification_method);
+ DCHECK(notifier_options_.request_context_getter);
+ // TODO(akalin): Replace NonThreadSafe checks with IO thread checks.
+ DCHECK(notifier_options_.request_context_getter->GetIOMessageLoopProxy()->
+ BelongsToCurrentThread());
+}
+
+InvalidationNotifier::~InvalidationNotifier() {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+}
+
+void InvalidationNotifier::AddObserver(SyncNotifierObserver* observer) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ observers_.AddObserver(observer);
+}
+
+void InvalidationNotifier::RemoveObserver(SyncNotifierObserver* observer) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ observers_.RemoveObserver(observer);
+}
+
+void InvalidationNotifier::SetState(const std::string& state) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ invalidation_state_ = state;
+}
+
+void InvalidationNotifier::UpdateCredentials(
+ const std::string& email, const std::string& token) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ VLOG(1) << "Updating credentials for " << email;
+ buzz::XmppClientSettings xmpp_client_settings =
+ notifier::MakeXmppClientSettings(notifier_options_,
+ email, token, SYNC_SERVICE_NAME);
+ if (state_ >= CONNECTING) {
+ login_->UpdateXmppSettings(xmpp_client_settings);
+ } else {
+ notifier::ConnectionOptions options;
+ VLOG(1) << "First time updating credentials: connecting";
+ login_.reset(
+ new notifier::Login(this,
+ xmpp_client_settings,
+ notifier::ConnectionOptions(),
+ notifier_options_.request_context_getter,
+ notifier::GetServerList(notifier_options_),
+ notifier_options_.try_ssltcp_first,
+ notifier_options_.auth_mechanism));
+ login_->StartConnection();
+ state_ = CONNECTING;
+ }
+}
+
+void InvalidationNotifier::UpdateEnabledTypes(
+ const syncable::ModelTypeSet& types) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ invalidation_client_.RegisterTypes(types);
+}
+
+void InvalidationNotifier::SendNotification() {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+}
+
+void InvalidationNotifier::OnConnect(
+ base::WeakPtr<talk_base::Task> base_task) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ VLOG(1) << "OnConnect";
+ if (state_ >= STARTED) {
+ invalidation_client_.ChangeBaseTask(base_task);
+ } else {
+ VLOG(1) << "First time connecting: starting invalidation client";
+ // TODO(akalin): Make cache_guid() part of the client ID. If we
+ // do so and we somehow propagate it up to the server somehow, we
+ // can make it so that we won't receive any notifications that
+ // were generated from our own changes.
+ const std::string kClientId = "invalidation_notifier";
+ invalidation_client_.Start(
+ kClientId, client_info_, invalidation_state_, this, this, base_task);
+ invalidation_state_.clear();
+ state_ = STARTED;
+ }
+}
+
+void InvalidationNotifier::OnDisconnect() {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ VLOG(1) << "OnDisconnect";
+}
+
+void InvalidationNotifier::OnInvalidate(
+ const syncable::ModelTypePayloadMap& type_payloads) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ FOR_EACH_OBSERVER(SyncNotifierObserver, observers_,
+ OnIncomingNotification(type_payloads));
+}
+
+void InvalidationNotifier::OnSessionStatusChanged(bool has_session) {
+ FOR_EACH_OBSERVER(SyncNotifierObserver, observers_,
+ OnNotificationStateChange(has_session));
+}
+
+void InvalidationNotifier::WriteState(const std::string& state) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ VLOG(1) << "WriteState";
+ FOR_EACH_OBSERVER(SyncNotifierObserver, observers_, StoreState(state));
+}
+
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/invalidation_notifier.h b/chrome/browser/sync/notifier/invalidation_notifier.h
new file mode 100644
index 0000000..762aa93
--- /dev/null
+++ b/chrome/browser/sync/notifier/invalidation_notifier.h
@@ -0,0 +1,104 @@
+// Copyright (c) 2011 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.
+//
+// An implementation of SyncNotifier that wraps an invalidation
+// client. Handles the details of connecting to XMPP and hooking it
+// up to the invalidation client.
+//
+// You probably don't want to use this directly; use
+// NonBlockingInvalidationNotifier.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_INVALIDATION_NOTIFIER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_INVALIDATION_NOTIFIER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/threading/non_thread_safe.h"
+#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
+#include "chrome/browser/sync/notifier/state_writer.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "jingle/notifier/base/notifier_options.h"
+#include "jingle/notifier/communicator/login.h"
+
+namespace sync_notifier {
+
+// This class must live on the IO thread.
+class InvalidationNotifier
+ : public SyncNotifier,
+ public notifier::LoginDelegate,
+ public ChromeInvalidationClient::Listener,
+ public StateWriter {
+ public:
+ InvalidationNotifier(
+ const notifier::NotifierOptions& notifier_options,
+ const std::string& client_info);
+
+ virtual ~InvalidationNotifier();
+
+ // SyncNotifier implementation.
+ virtual void AddObserver(SyncNotifierObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(SyncNotifierObserver* observer) OVERRIDE;
+ virtual void SetState(const std::string& state) OVERRIDE;
+ virtual void UpdateCredentials(
+ const std::string& email, const std::string& token) OVERRIDE;
+ virtual void UpdateEnabledTypes(
+ const syncable::ModelTypeSet& types) OVERRIDE;
+ virtual void SendNotification() OVERRIDE;
+
+ // notifier::LoginDelegate implementation.
+ virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) OVERRIDE;
+ virtual void OnDisconnect() OVERRIDE;
+
+ // ChromeInvalidationClient::Listener implementation.
+ virtual void OnInvalidate(
+ const syncable::ModelTypePayloadMap& type_payloads) OVERRIDE;
+ virtual void OnSessionStatusChanged(bool has_session) OVERRIDE;
+
+ // StateWriter implementation.
+ virtual void WriteState(const std::string& state) OVERRIDE;
+
+ private:
+ base::NonThreadSafe non_thread_safe_;
+
+ // We start off in the STOPPED state. When we get our initial
+ // credentials, we connect and move to the CONNECTING state. When
+ // we're connected we start the invalidation client and move to the
+ // STARTED state. We never go back to a previous state.
+ enum State {
+ STOPPED,
+ CONNECTING,
+ STARTED
+ };
+ State state_;
+
+ // Used to build parameters for |login_|.
+ const notifier::NotifierOptions notifier_options_;
+
+ // Passed to |invalidation_client_|.
+ const std::string client_info_;
+
+ // Our observers (which must live on the same thread).
+ ObserverList<SyncNotifierObserver> observers_;
+
+ // The state to pass to |chrome_invalidation_client_|.
+ std::string invalidation_state_;
+
+ // The XMPP connection manager.
+ scoped_ptr<notifier::Login> login_;
+
+ // The invalidation client.
+ ChromeInvalidationClient invalidation_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(InvalidationNotifier);
+};
+
+} // namespace sync_notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_INVALIDATION_NOTIFIER_H_
diff --git a/chrome/browser/sync/notifier/invalidation_notifier_unittest.cc b/chrome/browser/sync/notifier/invalidation_notifier_unittest.cc
new file mode 100644
index 0000000..bcfa9a4
--- /dev/null
+++ b/chrome/browser/sync/notifier/invalidation_notifier_unittest.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/invalidation_notifier.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "chrome/browser/sync/notifier/mock_sync_notifier_observer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "chrome/test/test_url_request_context_getter.h"
+#include "content/browser/browser_thread.h"
+#include "jingle/notifier/base/fake_base_task.h"
+#include "net/base/cert_verifier.h"
+#include "net/base/host_resolver.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sync_notifier {
+
+namespace {
+
+using ::testing::InSequence;
+using ::testing::StrictMock;
+
+class InvalidationNotifierTest : public testing::Test {
+ public:
+ InvalidationNotifierTest() : io_thread_(BrowserThread::IO, &message_loop_) {}
+
+ protected:
+ virtual void SetUp() {
+ request_context_getter_ = new TestURLRequestContextGetter;
+ notifier::NotifierOptions notifier_options;
+ notifier_options.request_context_getter = request_context_getter_;
+ invalidation_notifier_.reset(new InvalidationNotifier(notifier_options,
+ "fake_client_info"));
+ invalidation_notifier_->AddObserver(&mock_observer_);
+ }
+
+ virtual void TearDown() {
+ invalidation_notifier_->RemoveObserver(&mock_observer_);
+ invalidation_notifier_.reset();
+ request_context_getter_ = NULL;
+ }
+
+ MessageLoop message_loop_;
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ scoped_ptr<InvalidationNotifier> invalidation_notifier_;
+ StrictMock<MockSyncNotifierObserver> mock_observer_;
+ notifier::FakeBaseTask fake_base_task_;
+ // Since this test calls HostResolver code, we need an IO thread.
+ BrowserThread io_thread_;
+};
+
+TEST_F(InvalidationNotifierTest, Basic) {
+ InSequence dummy;
+
+ syncable::ModelTypePayloadMap type_payloads;
+ type_payloads[syncable::PREFERENCES] = "payload";
+ type_payloads[syncable::BOOKMARKS] = "";
+ type_payloads[syncable::AUTOFILL] = "";
+
+ EXPECT_CALL(mock_observer_, OnNotificationStateChange(true));
+ EXPECT_CALL(mock_observer_, StoreState("new_fake_state"));
+ EXPECT_CALL(mock_observer_, OnIncomingNotification(type_payloads));
+ EXPECT_CALL(mock_observer_, OnNotificationStateChange(false));
+
+ invalidation_notifier_->SetState("fake_state");
+ invalidation_notifier_->UpdateCredentials("foo@bar.com", "fake_token");
+
+ invalidation_notifier_->OnConnect(fake_base_task_.AsWeakPtr());
+ invalidation_notifier_->OnSessionStatusChanged(true);
+
+ invalidation_notifier_->WriteState("new_fake_state");
+
+ invalidation_notifier_->OnInvalidate(type_payloads);
+
+ // Shouldn't trigger notification state change.
+ invalidation_notifier_->OnDisconnect();
+ invalidation_notifier_->OnConnect(fake_base_task_.AsWeakPtr());
+
+ invalidation_notifier_->OnSessionStatusChanged(false);
+}
+
+} // namespace
+
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/mock_sync_notifier_observer.cc b/chrome/browser/sync/notifier/mock_sync_notifier_observer.cc
new file mode 100644
index 0000000..b46ac68
--- /dev/null
+++ b/chrome/browser/sync/notifier/mock_sync_notifier_observer.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/mock_sync_notifier_observer.h"
+
+namespace sync_notifier {
+
+MockSyncNotifierObserver::MockSyncNotifierObserver() {}
+MockSyncNotifierObserver::~MockSyncNotifierObserver() {}
+
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/mock_sync_notifier_observer.h b/chrome/browser/sync/notifier/mock_sync_notifier_observer.h
new file mode 100644
index 0000000..a9e141f
--- /dev/null
+++ b/chrome/browser/sync/notifier/mock_sync_notifier_observer.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_MOCK_SYNC_NOTIFIER_OBSERVER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_MOCK_SYNC_NOTIFIER_OBSERVER_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace sync_notifier {
+
+class MockSyncNotifierObserver : public SyncNotifierObserver {
+ public:
+ MockSyncNotifierObserver();
+ virtual ~MockSyncNotifierObserver();
+
+ MOCK_METHOD1(OnIncomingNotification,
+ void(const syncable::ModelTypePayloadMap&));
+ MOCK_METHOD1(OnNotificationStateChange, void(bool));
+ MOCK_METHOD1(StoreState, void(const std::string&));
+};
+
+} // namespace sync_notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_MOCK_SYNC_NOTIFIER_OBSERVER_H_
diff --git a/chrome/browser/sync/notifier/non_blocking_invalidation_notifier.cc b/chrome/browser/sync/notifier/non_blocking_invalidation_notifier.cc
new file mode 100644
index 0000000..d5e1848
--- /dev/null
+++ b/chrome/browser/sync/notifier/non_blocking_invalidation_notifier.cc
@@ -0,0 +1,216 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h"
+
+#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/observer_list_threadsafe.h"
+#include "base/threading/thread.h"
+#include "chrome/browser/sync/notifier/invalidation_notifier.h"
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
+
+namespace sync_notifier {
+
+class NonBlockingInvalidationNotifier::Core
+ : public base::RefCountedThreadSafe<NonBlockingInvalidationNotifier::Core>,
+ public SyncNotifierObserver {
+ public:
+ // Called on parent thread.
+ Core();
+
+ // Called on parent thread.
+ void AddObserver(SyncNotifierObserver* observer);
+ void RemoveObserver(SyncNotifierObserver* observer);
+
+ // Helpers called on I/O thread.
+ void Initialize(const notifier::NotifierOptions& notifier_options,
+ const std::string& client_info);
+ void Teardown();
+ void SetState(const std::string& state);
+ void UpdateCredentials(const std::string& email, const std::string& token);
+ void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
+ void SendNotification();
+
+ // SyncNotifierObserver implementation (all called on I/O thread).
+ virtual void OnIncomingNotification(
+ const syncable::ModelTypePayloadMap& type_payloads);
+ virtual void OnNotificationStateChange(bool notifications_enabled);
+ virtual void StoreState(const std::string& state);
+
+ private:
+ friend class
+ base::RefCountedThreadSafe<NonBlockingInvalidationNotifier::Core>;
+ // Called on parent or I/O thread.
+ ~Core();
+
+ scoped_ptr<InvalidationNotifier> invalidation_notifier_;
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+ scoped_refptr<ObserverListThreadSafe<SyncNotifierObserver> > observers_;
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+NonBlockingInvalidationNotifier::Core::Core()
+ : observers_(new ObserverListThreadSafe<SyncNotifierObserver>()) {
+}
+
+NonBlockingInvalidationNotifier::Core::~Core() {
+}
+
+void NonBlockingInvalidationNotifier::Core::Initialize(
+ const notifier::NotifierOptions& notifier_options,
+ const std::string& client_info) {
+ DCHECK(notifier_options.request_context_getter);
+ DCHECK_EQ(notifier::NOTIFICATION_SERVER,
+ notifier_options.notification_method);
+ io_message_loop_proxy_ = notifier_options.request_context_getter->
+ GetIOMessageLoopProxy();
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ invalidation_notifier_.reset(
+ new InvalidationNotifier(notifier_options, client_info));
+ invalidation_notifier_->AddObserver(this);
+}
+
+
+void NonBlockingInvalidationNotifier::Core::Teardown() {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ invalidation_notifier_->RemoveObserver(this);
+ invalidation_notifier_.reset();
+ io_message_loop_proxy_ = NULL;
+}
+
+void NonBlockingInvalidationNotifier::Core::AddObserver(
+ SyncNotifierObserver* observer) {
+ observers_->AddObserver(observer);
+}
+
+void NonBlockingInvalidationNotifier::Core::RemoveObserver(
+ SyncNotifierObserver* observer) {
+ observers_->RemoveObserver(observer);
+}
+
+void NonBlockingInvalidationNotifier::Core::SetState(
+ const std::string& state) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ invalidation_notifier_->SetState(state);
+}
+
+void NonBlockingInvalidationNotifier::Core::UpdateCredentials(
+ const std::string& email, const std::string& token) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ invalidation_notifier_->UpdateCredentials(email, token);
+}
+
+void NonBlockingInvalidationNotifier::Core::UpdateEnabledTypes(
+ const syncable::ModelTypeSet& types) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ invalidation_notifier_->UpdateEnabledTypes(types);
+}
+
+void NonBlockingInvalidationNotifier::Core::OnIncomingNotification(
+ const syncable::ModelTypePayloadMap& type_payloads) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ observers_->Notify(&SyncNotifierObserver::OnIncomingNotification,
+ type_payloads);
+}
+
+void NonBlockingInvalidationNotifier::Core::OnNotificationStateChange(
+ bool notifications_enabled) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ observers_->Notify(&SyncNotifierObserver::OnNotificationStateChange,
+ notifications_enabled);
+}
+
+void NonBlockingInvalidationNotifier::Core::StoreState(
+ const std::string& state) {
+ DCHECK(io_message_loop_proxy_->BelongsToCurrentThread());
+ observers_->Notify(&SyncNotifierObserver::StoreState, state);
+}
+
+NonBlockingInvalidationNotifier::NonBlockingInvalidationNotifier(
+ const notifier::NotifierOptions& notifier_options,
+ const std::string& client_info)
+ : core_(new Core),
+ construction_message_loop_proxy_(
+ base::MessageLoopProxy::CreateForCurrentThread()),
+ io_message_loop_proxy_(notifier_options.request_context_getter->
+ GetIOMessageLoopProxy()) {
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ core_.get(),
+ &NonBlockingInvalidationNotifier::Core::Initialize,
+ notifier_options, client_info));
+}
+
+NonBlockingInvalidationNotifier::~NonBlockingInvalidationNotifier() {
+ DCHECK(construction_message_loop_proxy_->BelongsToCurrentThread());
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ core_.get(),
+ &NonBlockingInvalidationNotifier::Core::Teardown));
+}
+
+void NonBlockingInvalidationNotifier::AddObserver(
+ SyncNotifierObserver* observer) {
+ CheckOrSetValidThread();
+ core_->AddObserver(observer);
+}
+
+void NonBlockingInvalidationNotifier::RemoveObserver(
+ SyncNotifierObserver* observer) {
+ CheckOrSetValidThread();
+ core_->RemoveObserver(observer);
+}
+
+void NonBlockingInvalidationNotifier::SetState(const std::string& state) {
+ CheckOrSetValidThread();
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ core_.get(),
+ &NonBlockingInvalidationNotifier::Core::SetState,
+ state));
+}
+
+void NonBlockingInvalidationNotifier::UpdateCredentials(
+ const std::string& email, const std::string& token) {
+ CheckOrSetValidThread();
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ core_.get(),
+ &NonBlockingInvalidationNotifier::Core::UpdateCredentials,
+ email, token));
+}
+
+void NonBlockingInvalidationNotifier::UpdateEnabledTypes(
+ const syncable::ModelTypeSet& types) {
+ CheckOrSetValidThread();
+ io_message_loop_proxy_->PostTask(
+ FROM_HERE,
+ NewRunnableMethod(
+ core_.get(),
+ &NonBlockingInvalidationNotifier::Core::UpdateEnabledTypes,
+ types));
+}
+
+void NonBlockingInvalidationNotifier::SendNotification() {
+ CheckOrSetValidThread();
+ // InvalidationClient doesn't implement SendNotification(), so no
+ // need to forward on the call.
+}
+
+void NonBlockingInvalidationNotifier::CheckOrSetValidThread() {
+ if (method_message_loop_proxy_) {
+ DCHECK(method_message_loop_proxy_->BelongsToCurrentThread());
+ } else {
+ method_message_loop_proxy_ =
+ base::MessageLoopProxy::CreateForCurrentThread();
+ }
+}
+
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h b/chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h
new file mode 100644
index 0000000..362e4fb
--- /dev/null
+++ b/chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h
@@ -0,0 +1,56 @@
+// Copyright (c) 2011 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.
+//
+// An implementation of SyncNotifier that wraps InvalidationNotifier
+// on its own thread.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_NON_BLOCKING_INVALIDATION_NOTIFIER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_NON_BLOCKING_INVALIDATION_NOTIFIER_H_
+#pragma once
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "jingle/notifier/base/notifier_options.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+namespace sync_notifier {
+
+class NonBlockingInvalidationNotifier : public SyncNotifier {
+ public:
+ NonBlockingInvalidationNotifier(
+ const notifier::NotifierOptions& notifier_options,
+ const std::string& client_info);
+
+ virtual ~NonBlockingInvalidationNotifier();
+
+ // SyncNotifier implementation.
+ virtual void AddObserver(SyncNotifierObserver* observer);
+ virtual void RemoveObserver(SyncNotifierObserver* observer);
+ virtual void SetState(const std::string& state);
+ virtual void UpdateCredentials(
+ const std::string& email, const std::string& token);
+ virtual void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
+ virtual void SendNotification();
+
+ private:
+ void CheckOrSetValidThread();
+ // The real guts of NonBlockingInvalidationNotifier, which allows this class
+ // to not be refcounted.
+ class Core;
+ scoped_refptr<Core> core_;
+ scoped_refptr<base::MessageLoopProxy> construction_message_loop_proxy_;
+ scoped_refptr<base::MessageLoopProxy> method_message_loop_proxy_;
+ scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
+ DISALLOW_COPY_AND_ASSIGN(NonBlockingInvalidationNotifier);
+};
+
+} // namespace sync_notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_NON_BLOCKING_INVALIDATION_NOTIFIER_H_
diff --git a/chrome/browser/sync/notifier/non_blocking_invalidation_notifier_unittest.cc b/chrome/browser/sync/notifier/non_blocking_invalidation_notifier_unittest.cc
new file mode 100644
index 0000000..5fec4ee
--- /dev/null
+++ b/chrome/browser/sync/notifier/non_blocking_invalidation_notifier_unittest.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h"
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "chrome/browser/sync/notifier/mock_sync_notifier_observer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "chrome/test/test_url_request_context_getter.h"
+#include "content/browser/browser_thread.h"
+#include "jingle/notifier/base/fake_base_task.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sync_notifier {
+
+namespace {
+
+using ::testing::InSequence;
+using ::testing::StrictMock;
+
+class NonBlockingInvalidationNotifierTest : public testing::Test {
+ public:
+ NonBlockingInvalidationNotifierTest() {}
+
+ protected:
+ virtual void SetUp() {
+ request_context_getter_ = new TestURLRequestContextGetter;
+ notifier::NotifierOptions notifier_options;
+ notifier_options.request_context_getter = request_context_getter_;
+ invalidation_notifier_.reset(
+ new NonBlockingInvalidationNotifier(notifier_options,
+ "fake_client_info"));
+ invalidation_notifier_->AddObserver(&mock_observer_);
+ }
+
+ virtual void TearDown() {
+ invalidation_notifier_->RemoveObserver(&mock_observer_);
+ invalidation_notifier_.reset();
+ {
+ // The request context getter gets deleted on the I/O thread. To prevent a
+ // leak supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ request_context_getter_ = NULL;
+ }
+ MessageLoop::current()->RunAllPending();
+ }
+
+ MessageLoop message_loop_;
+ scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
+ scoped_ptr<NonBlockingInvalidationNotifier> invalidation_notifier_;
+ StrictMock<MockSyncNotifierObserver> mock_observer_;
+ notifier::FakeBaseTask fake_base_task_;
+};
+
+TEST_F(NonBlockingInvalidationNotifierTest, Basic) {
+ syncable::ModelTypeSet types;
+ types.insert(syncable::BOOKMARKS);
+ types.insert(syncable::AUTOFILL);
+
+ invalidation_notifier_->SetState("fake_state");
+ invalidation_notifier_->UpdateCredentials("foo@bar.com", "fake_token");
+ invalidation_notifier_->UpdateEnabledTypes(types);
+}
+
+// TODO(akalin): Add synchronous operations for testing to
+// NonBlockingInvalidationNotifierTest and use that to test it.
+
+} // namespace
+
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/p2p_notifier.cc b/chrome/browser/sync/notifier/p2p_notifier.cc
new file mode 100644
index 0000000..46d9a9e
--- /dev/null
+++ b/chrome/browser/sync/notifier/p2p_notifier.cc
@@ -0,0 +1,150 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/p2p_notifier.h"
+
+#include "base/message_loop_proxy.h"
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
+#include "chrome/browser/sync/protocol/service_constants.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "jingle/notifier/listener/mediator_thread_impl.h"
+#include "jingle/notifier/listener/talk_mediator_impl.h"
+
+namespace sync_notifier {
+
+namespace {
+const char kSyncNotificationChannel[] = "http://www.google.com/chrome/sync";
+const char kSyncNotificationData[] = "sync-ping-p2p";
+} // namespace
+
+P2PNotifier::P2PNotifier(
+ const notifier::NotifierOptions& notifier_options)
+ : talk_mediator_(
+ new notifier::TalkMediatorImpl(
+ new notifier::MediatorThreadImpl(notifier_options),
+ notifier_options)),
+ logged_in_(false),
+ notifications_enabled_(false),
+ construction_message_loop_proxy_(
+ base::MessageLoopProxy::CreateForCurrentThread()) {
+ talk_mediator_->SetDelegate(this);
+}
+
+P2PNotifier::~P2PNotifier() {
+ DCHECK(construction_message_loop_proxy_->BelongsToCurrentThread());
+}
+
+void P2PNotifier::AddObserver(SyncNotifierObserver* observer) {
+ CheckOrSetValidThread();
+ observer_list_.AddObserver(observer);
+}
+
+// Note: Since we need to shutdown TalkMediator on the method_thread, we are
+// calling Logout on TalkMediator when the last observer is removed.
+// Users will need to call UpdateCredentials again to use the same object.
+// TODO(akalin): Think of a better solution to fix this.
+void P2PNotifier::RemoveObserver(SyncNotifierObserver* observer) {
+ CheckOrSetValidThread();
+ observer_list_.RemoveObserver(observer);
+
+ // Logout after the last observer is removed.
+ if (observer_list_.size() == 0) {
+ talk_mediator_->Logout();
+ }
+}
+
+void P2PNotifier::SetState(const std::string& state) {
+ CheckOrSetValidThread();
+}
+
+void P2PNotifier::UpdateCredentials(
+ const std::string& email, const std::string& token) {
+ CheckOrSetValidThread();
+ // If already logged in, the new credentials will take effect on the
+ // next reconnection.
+ talk_mediator_->SetAuthToken(email, token, SYNC_SERVICE_NAME);
+ if (!logged_in_) {
+ if (!talk_mediator_->Login()) {
+ LOG(DFATAL) << "Could not login for " << email;
+ return;
+ }
+
+ notifier::Subscription subscription;
+ subscription.channel = kSyncNotificationChannel;
+ // There may be some subtle issues around case sensitivity of the
+ // from field, but it doesn't matter too much since this is only
+ // used in p2p mode (which is only used in testing).
+ subscription.from = email;
+ talk_mediator_->AddSubscription(subscription);
+
+ logged_in_ = true;
+ }
+}
+
+void P2PNotifier::UpdateEnabledTypes(const syncable::ModelTypeSet& types) {
+ CheckOrSetValidThread();
+ enabled_types_ = types;
+ MaybeEmitNotification();
+}
+
+void P2PNotifier::SendNotification() {
+ CheckOrSetValidThread();
+ VLOG(1) << "Sending XMPP notification...";
+ notifier::Notification notification;
+ notification.channel = kSyncNotificationChannel;
+ notification.data = kSyncNotificationData;
+ talk_mediator_->SendNotification(notification);
+}
+
+void P2PNotifier::OnNotificationStateChange(bool notifications_enabled) {
+ CheckOrSetValidThread();
+ notifications_enabled_ = notifications_enabled;
+ FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_,
+ OnNotificationStateChange(notifications_enabled_));
+ MaybeEmitNotification();
+}
+
+void P2PNotifier::OnIncomingNotification(
+ const notifier::Notification& notification) {
+ CheckOrSetValidThread();
+ VLOG(1) << "Sync received P2P notification.";
+ if (notification.channel != kSyncNotificationChannel) {
+ LOG(WARNING) << "Notification from unexpected source: "
+ << notification.channel;
+ }
+ MaybeEmitNotification();
+}
+
+void P2PNotifier::OnOutgoingNotification() {}
+
+void P2PNotifier::MaybeEmitNotification() {
+ if (!logged_in_) {
+ VLOG(1) << "Not logged in yet -- not emitting notification";
+ return;
+ }
+ if (!notifications_enabled_) {
+ VLOG(1) << "Notifications not enabled -- not emitting notification";
+ return;
+ }
+ if (enabled_types_.empty()) {
+ VLOG(1) << "No enabled types -- not emitting notification";
+ return;
+ }
+ syncable::ModelTypePayloadMap type_payloads =
+ syncable::ModelTypePayloadMapFromBitSet(
+ syncable::ModelTypeBitSetFromSet(enabled_types_), std::string());
+ FOR_EACH_OBSERVER(SyncNotifierObserver, observer_list_,
+ OnIncomingNotification(type_payloads));
+}
+
+void P2PNotifier::CheckOrSetValidThread() {
+ if (method_message_loop_proxy_) {
+ DCHECK(method_message_loop_proxy_->BelongsToCurrentThread());
+ } else {
+ method_message_loop_proxy_ =
+ base::MessageLoopProxy::CreateForCurrentThread();
+ }
+}
+
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/p2p_notifier.h b/chrome/browser/sync/notifier/p2p_notifier.h
new file mode 100644
index 0000000..e5b2743
--- /dev/null
+++ b/chrome/browser/sync/notifier/p2p_notifier.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2011 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.
+//
+// A notifier that uses p2p notifications based on XMPP push
+// notifications. Used only for sync integration tests.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_P2P_NOTIFIER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_P2P_NOTIFIER_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "jingle/notifier/listener/talk_mediator.h"
+
+namespace base {
+class MessageLoopProxy;
+}
+
+
+namespace notifier {
+struct NotifierOptions;
+} // namespace
+
+namespace sync_notifier {
+
+class P2PNotifier
+ : public SyncNotifier,
+ public notifier::TalkMediator::Delegate {
+ public:
+ explicit P2PNotifier(const notifier::NotifierOptions& notifier_options);
+
+ virtual ~P2PNotifier();
+
+ // SyncNotifier implementation
+ virtual void AddObserver(SyncNotifierObserver* observer);
+ virtual void RemoveObserver(SyncNotifierObserver* observer);
+ virtual void SetState(const std::string& state);
+ virtual void UpdateCredentials(
+ const std::string& email, const std::string& token);
+ virtual void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
+ virtual void SendNotification();
+
+ // TalkMediator::Delegate implementation.
+ virtual void OnNotificationStateChange(bool notifications_enabled);
+ virtual void OnIncomingNotification(
+ const notifier::Notification& notification);
+ virtual void OnOutgoingNotification();
+
+ private:
+ // Call OnIncomingNotification() on observers if we have a non-empty
+ // set of enabled types.
+ void MaybeEmitNotification();
+ void CheckOrSetValidThread();
+
+ ObserverList<SyncNotifierObserver> observer_list_;
+
+ // The actual notification listener.
+ scoped_ptr<notifier::TalkMediator> talk_mediator_;
+ // Whether we called Login() on |talk_mediator_| yet.
+ bool logged_in_;
+ // Whether |talk_mediator_| has notified us that notifications are
+ // enabled.
+ bool notifications_enabled_;
+
+ syncable::ModelTypeSet enabled_types_;
+ scoped_refptr<base::MessageLoopProxy> construction_message_loop_proxy_;
+ scoped_refptr<base::MessageLoopProxy> method_message_loop_proxy_;
+};
+
+} // namespace sync_notifier
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_P2P_NOTIFIER_H_
diff --git a/chrome/browser/sync/notifier/registration_manager.cc b/chrome/browser/sync/notifier/registration_manager.cc
index 69181a4..4dbc095 100644
--- a/chrome/browser/sync/notifier/registration_manager.cc
+++ b/chrome/browser/sync/notifier/registration_manager.cc
@@ -4,17 +4,55 @@
#include "chrome/browser/sync/notifier/registration_manager.h"
+#include <algorithm>
+#include <cstddef>
#include <string>
+#include "base/rand_util.h"
#include "chrome/browser/sync/notifier/invalidation_util.h"
#include "chrome/browser/sync/syncable/model_type.h"
namespace sync_notifier {
+RegistrationManager::PendingRegistrationInfo::PendingRegistrationInfo() {}
+
+RegistrationManager::RegistrationStatus::RegistrationStatus()
+ : model_type(syncable::UNSPECIFIED),
+ registration_manager(NULL),
+ state(invalidation::RegistrationState_UNREGISTERED) {}
+
+RegistrationManager::RegistrationStatus::~RegistrationStatus() {}
+
+void RegistrationManager::RegistrationStatus::DoRegister() {
+ DCHECK_NE(model_type, syncable::UNSPECIFIED);
+ DCHECK(registration_manager);
+ // We might be called explicitly, so stop the timer manually and
+ // reset the delay.
+ registration_timer.Stop();
+ delay = base::TimeDelta();
+ registration_manager->DoRegisterType(model_type);
+ DCHECK(!last_registration_request.is_null());
+}
+
+const int RegistrationManager::kInitialRegistrationDelaySeconds = 5;
+const int RegistrationManager::kRegistrationDelayExponent = 2;
+const double RegistrationManager::kRegistrationDelayMaxJitter = 0.5;
+const int RegistrationManager::kMinRegistrationDelaySeconds = 1;
+// 1 hour.
+const int RegistrationManager::kMaxRegistrationDelaySeconds = 60 * 60;
+
RegistrationManager::RegistrationManager(
invalidation::InvalidationClient* invalidation_client)
: invalidation_client_(invalidation_client) {
DCHECK(invalidation_client_);
+ // Initialize statuses.
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
+ RegistrationStatus* status = &registration_statuses_[model_type];
+ status->model_type = model_type;
+ status->registration_manager = this;
+ }
}
RegistrationManager::~RegistrationManager() {
@@ -25,76 +63,168 @@ void RegistrationManager::SetRegisteredTypes(
const syncable::ModelTypeSet& types) {
DCHECK(non_thread_safe_.CalledOnValidThread());
- for (int i = syncable::FIRST_REAL_MODEL_TYPE; i < syncable::MODEL_TYPE_COUNT;
- ++i) {
- syncable::ModelType type = syncable::ModelTypeFromInt(i);
- if (types.count(type) > 0) {
- RegisterType(type);
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
+ if (types.count(model_type) > 0) {
+ if (!IsTypeRegistered(model_type)) {
+ TryRegisterType(model_type, false /* is_retry */);
+ }
} else {
- UnregisterType(type);
+ if (IsTypeRegistered(model_type)) {
+ UnregisterType(model_type);
+ }
}
}
}
-bool RegistrationManager::IsRegistered(
- syncable::ModelType model_type) const {
+void RegistrationManager::MarkRegistrationLost(
+ syncable::ModelType model_type) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ registration_statuses_[model_type].state =
+ invalidation::RegistrationState_UNREGISTERED;
+ TryRegisterType(model_type, true /* is_retry */);
+}
+
+void RegistrationManager::MarkAllRegistrationsLost() {
DCHECK(non_thread_safe_.CalledOnValidThread());
- RegistrationStatusMap::const_iterator it =
- registration_status_.find(model_type);
- if (it == registration_status_.end()) {
- return false;
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
+ if (IsTypeRegistered(model_type)) {
+ MarkRegistrationLost(model_type);
+ }
}
- return it->second == invalidation::RegistrationState_REGISTERED;
}
-void RegistrationManager::MarkRegistrationLost(
- syncable::ModelType model_type) {
+syncable::ModelTypeSet RegistrationManager::GetRegisteredTypes() const {
DCHECK(non_thread_safe_.CalledOnValidThread());
- invalidation::ObjectId object_id;
- if (!RealModelTypeToObjectId(model_type, &object_id)) {
- LOG(ERROR) << "Invalid model type: " << model_type;
- return;
+ syncable::ModelTypeSet registered_types;
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
+ if (IsTypeRegistered(model_type)) {
+ registered_types.insert(model_type);
+ }
}
- RegistrationStatusMap::iterator it =
- registration_status_.find(model_type);
- if (it == registration_status_.end()) {
- LOG(ERROR) << "Unknown model type: " << model_type;
- return;
+ return registered_types;
+}
+
+RegistrationManager::PendingRegistrationMap
+ RegistrationManager::GetPendingRegistrations() const {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ PendingRegistrationMap pending_registrations;
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
+ const RegistrationStatus& status = registration_statuses_[model_type];
+ if (status.registration_timer.IsRunning()) {
+ pending_registrations[model_type].last_registration_request =
+ status.last_registration_request;
+ pending_registrations[model_type].registration_attempt =
+ status.last_registration_attempt;
+ pending_registrations[model_type].delay = status.delay;
+ pending_registrations[model_type].actual_delay =
+ status.registration_timer.GetCurrentDelay();
+ }
}
- it->second = invalidation::RegistrationState_UNREGISTERED;
- RegisterObject(object_id, it);
+ return pending_registrations;
}
-void RegistrationManager::MarkAllRegistrationsLost() {
+void RegistrationManager::FirePendingRegistrationsForTest() {
DCHECK(non_thread_safe_.CalledOnValidThread());
- for (RegistrationStatusMap::iterator it =
- registration_status_.begin();
- it != registration_status_.end(); ++it) {
- invalidation::ObjectId object_id;
- if (!RealModelTypeToObjectId(it->first, &object_id)) {
- LOG(DFATAL) << "Invalid model type: " << it->first;
- continue;
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
+ RegistrationStatus* status = &registration_statuses_[model_type];
+ if (status->registration_timer.IsRunning()) {
+ status->DoRegister();
}
- it->second = invalidation::RegistrationState_UNREGISTERED;
- RegisterObject(object_id, it);
}
}
-void RegistrationManager::RegisterType(syncable::ModelType model_type) {
+// static
+double RegistrationManager::CalculateBackoff(
+ double retry_interval,
+ double initial_retry_interval,
+ double min_retry_interval,
+ double max_retry_interval,
+ double backoff_exponent,
+ double jitter,
+ double max_jitter) {
+ // scaled_jitter lies in [-max_jitter, max_jitter].
+ double scaled_jitter = jitter * max_jitter;
+ double new_retry_interval =
+ (retry_interval == 0.0) ?
+ (initial_retry_interval * (1.0 + scaled_jitter)) :
+ (retry_interval * (backoff_exponent + scaled_jitter));
+ return std::max(min_retry_interval,
+ std::min(max_retry_interval, new_retry_interval));
+}
+
+double RegistrationManager::GetJitter() {
+ // |jitter| lies in [-1.0, 1.0), which is low-biased, but only
+ // barely.
+ //
+ // TODO(akalin): Fix the bias.
+ return 2.0 * base::RandDouble() - 1.0;
+}
+
+void RegistrationManager::TryRegisterType(syncable::ModelType model_type,
+ bool is_retry) {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ RegistrationStatus* status = &registration_statuses_[model_type];
+ status->last_registration_attempt = base::Time::Now();
+ if (is_retry) {
+ // If we're a retry, we must have tried at least once before.
+ DCHECK(!status->last_registration_request.is_null());
+ // delay = max(0, (now - last request) + next_delay)
+ status->delay =
+ (status->last_registration_request -
+ status->last_registration_attempt) +
+ status->next_delay;
+ base::TimeDelta delay =
+ (status->delay <= base::TimeDelta()) ?
+ base::TimeDelta() : status->delay;
+ VLOG(2) << "Registering "
+ << syncable::ModelTypeToString(model_type) << " in "
+ << delay.InMilliseconds() << " ms";
+ status->registration_timer.Stop();
+ status->registration_timer.Start(
+ delay, status, &RegistrationManager::RegistrationStatus::DoRegister);
+ double next_delay_seconds =
+ CalculateBackoff(static_cast<double>(status->next_delay.InSeconds()),
+ kInitialRegistrationDelaySeconds,
+ kMinRegistrationDelaySeconds,
+ kMaxRegistrationDelaySeconds,
+ kRegistrationDelayExponent,
+ GetJitter(),
+ kRegistrationDelayMaxJitter);
+ status->next_delay =
+ base::TimeDelta::FromSeconds(static_cast<int64>(next_delay_seconds));
+ VLOG(2) << "New next delay for "
+ << syncable::ModelTypeToString(model_type) << " is "
+ << status->next_delay.InSeconds() << " seconds";
+ } else {
+ VLOG(2) << "Not a retry -- registering "
+ << syncable::ModelTypeToString(model_type) << " immediately";
+ status->delay = base::TimeDelta();
+ status->next_delay = base::TimeDelta();
+ status->DoRegister();
+ }
+}
+
+void RegistrationManager::DoRegisterType(syncable::ModelType model_type) {
DCHECK(non_thread_safe_.CalledOnValidThread());
invalidation::ObjectId object_id;
if (!RealModelTypeToObjectId(model_type, &object_id)) {
LOG(DFATAL) << "Invalid model type: " << model_type;
return;
}
- RegistrationStatusMap::iterator it =
- registration_status_.insert(
- std::make_pair(
- model_type,
- invalidation::RegistrationState_UNREGISTERED)).first;
- if (it->second == invalidation::RegistrationState_UNREGISTERED) {
- RegisterObject(object_id, it);
- }
+ invalidation_client_->Register(object_id);
+ RegistrationStatus* status = &registration_statuses_[model_type];
+ status->state = invalidation::RegistrationState_REGISTERED;
+ status->last_registration_request = base::Time::Now();
}
void RegistrationManager::UnregisterType(syncable::ModelType model_type) {
@@ -104,21 +234,16 @@ void RegistrationManager::UnregisterType(syncable::ModelType model_type) {
LOG(DFATAL) << "Invalid model type: " << model_type;
return;
}
- RegistrationStatusMap::iterator it = registration_status_.find(model_type);
-
- if (it != registration_status_.end()) {
- if (it->second == invalidation::RegistrationState_REGISTERED) {
- invalidation_client_->Unregister(object_id);
- }
- registration_status_.erase(it);
- }
+ invalidation_client_->Unregister(object_id);
+ RegistrationStatus* status = &registration_statuses_[model_type];
+ status->state = invalidation::RegistrationState_UNREGISTERED;
}
-void RegistrationManager::RegisterObject(
- const invalidation::ObjectId& object_id,
- RegistrationStatusMap::iterator it) {
- DCHECK_EQ(it->second, invalidation::RegistrationState_UNREGISTERED);
- invalidation_client_->Register(object_id);
- it->second = invalidation::RegistrationState_REGISTERED;
+bool RegistrationManager::IsTypeRegistered(
+ syncable::ModelType model_type) const {
+ DCHECK(non_thread_safe_.CalledOnValidThread());
+ return registration_statuses_[model_type].state ==
+ invalidation::RegistrationState_REGISTERED;
}
+
} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/registration_manager.h b/chrome/browser/sync/notifier/registration_manager.h
index d7edb04..8111585 100644
--- a/chrome/browser/sync/notifier/registration_manager.h
+++ b/chrome/browser/sync/notifier/registration_manager.h
@@ -12,35 +12,61 @@
#include <map>
#include "base/basictypes.h"
-#include "base/gtest_prod_util.h"
+#include "base/time.h"
+#include "base/timer.h"
#include "base/threading/non_thread_safe.h"
#include "chrome/browser/sync/syncable/model_type.h"
+// For invalidation::RegistrationState.
#include "google/cacheinvalidation/invalidation-client.h"
namespace sync_notifier {
+// Manages the details of registering types for invalidation.
+// Implements exponential backoff for repeated registration attempts
+// to the invalidation client.
+//
+// TODO(akalin): Consolidate exponential backoff code. Other
+// implementations include the syncer thread (both versions) and XMPP
+// retries. The most sophisticated one is URLRequestThrottler; making
+// that generic should work for everyone.
class RegistrationManager {
-
- friend class RegistrationManagerTest;
-
- FRIEND_TEST_ALL_PREFIXES(RegistrationManagerTest, RegisterType);
- FRIEND_TEST_ALL_PREFIXES(RegistrationManagerTest, UnregisterType);
- FRIEND_TEST_ALL_PREFIXES(RegistrationManagerTest, MarkRegistrationLost);
- FRIEND_TEST_ALL_PREFIXES(RegistrationManagerTest, MarkAllRegistrationsLost);
public:
+ // Constants for exponential backoff (used by tests).
+ static const int kInitialRegistrationDelaySeconds;
+ static const int kRegistrationDelayExponent;
+ static const double kRegistrationDelayMaxJitter;
+ static const int kMinRegistrationDelaySeconds;
+ static const int kMaxRegistrationDelaySeconds;
+
+ // Types used by testing functions.
+ struct PendingRegistrationInfo {
+ PendingRegistrationInfo();
+
+ // Last time a registration request was actually sent.
+ base::Time last_registration_request;
+ // Time the registration was attempted.
+ base::Time registration_attempt;
+ // The calculated delay of the pending registration (which may be
+ // negative).
+ base::TimeDelta delay;
+ // The delay of the timer, which should be max(delay, 0).
+ base::TimeDelta actual_delay;
+ };
+ // Map from types with pending registrations to info about the
+ // pending registration.
+ typedef std::map<syncable::ModelType, PendingRegistrationInfo>
+ PendingRegistrationMap;
+
// Does not take ownership of |invalidation_client_|.
explicit RegistrationManager(
invalidation::InvalidationClient* invalidation_client);
- ~RegistrationManager();
+ virtual ~RegistrationManager();
+ // Registers all types included in the given set and sets all other
+ // types to be unregistered.
void SetRegisteredTypes(const syncable::ModelTypeSet& types);
- // Returns true iff |model_type| is currently registered.
- //
- // Currently only used by unit tests.
- bool IsRegistered(syncable::ModelType model_type) const;
-
// Marks the registration for the |model_type| lost and re-registers
// it.
void MarkRegistrationLost(syncable::ModelType model_type);
@@ -48,26 +74,84 @@ class RegistrationManager {
// Marks all registrations lost and re-registers them.
void MarkAllRegistrationsLost();
- private:
- typedef std::map<syncable::ModelType, invalidation::RegistrationState>
- RegistrationStatusMap;
+ // The functions below should only be used in tests.
- // Registers the given |model_type|, which must be valid.
- void RegisterType(syncable::ModelType model_type);
+ // Gets all currently-registered types.
+ syncable::ModelTypeSet GetRegisteredTypes() const;
- void UnregisterType(syncable::ModelType model_type);
+ // Gets all pending registrations and their next min delays.
+ PendingRegistrationMap GetPendingRegistrations() const;
+
+ // Run pending registrations immediately.
+ void FirePendingRegistrationsForTest();
+
+ // Calculate exponential backoff. |jitter| must be Uniform[-1.0,
+ // 1.0].
+ static double CalculateBackoff(double retry_interval,
+ double initial_retry_interval,
+ double min_retry_interval,
+ double max_retry_interval,
+ double backoff_exponent,
+ double jitter,
+ double max_jitter);
- // Calls invalidation_client_->Register() on |object_id|. sets
- // it->second to UNREGISTERED -> PENDING.
- void RegisterObject(const invalidation::ObjectId& object_id,
- RegistrationStatusMap::iterator it);
+ protected:
+ // Overrideable for testing purposes.
+ virtual double GetJitter();
+
+ private:
+ struct RegistrationStatus {
+ RegistrationStatus();
+ ~RegistrationStatus();
+
+ // Calls registration_manager->DoRegister(model_type). (needed by
+ // |registration_timer|).
+ void DoRegister();
+
+ // The model type for which this is the status.
+ syncable::ModelType model_type;
+ // The parent registration manager.
+ RegistrationManager* registration_manager;
+
+ // The current registration state.
+ invalidation::RegistrationState state;
+ // When we last sent a registration request.
+ base::Time last_registration_request;
+ // When we last tried to register.
+ base::Time last_registration_attempt;
+ // The calculated delay of any pending registration (which may be
+ // negative).
+ base::TimeDelta delay;
+ // The minimum time to wait until any next registration attempt.
+ // Increased after each consecutive failure.
+ base::TimeDelta next_delay;
+ // The actual timer for registration.
+ base::OneShotTimer<RegistrationStatus> registration_timer;
+ };
+
+ // If |is_retry| is not set, registers the given type immediately
+ // and resets all backoff parameters. If |is_retry| is set,
+ // registers the given type at some point in the future and
+ // increases the delay until the next retry.
+ void TryRegisterType(syncable::ModelType model_type,
+ bool is_retry);
+
+ // Registers the given type, which must be valid, immediately.
+ // Updates |last_registration| in the appropriate
+ // RegistrationStatus. Should only be called by
+ // RegistrationStatus::DoRegister().
+ void DoRegisterType(syncable::ModelType model_type);
+
+ // Unregisters the given type, which must be valid.
+ void UnregisterType(syncable::ModelType model_type);
- void OnRegister(const invalidation::RegistrationUpdateResult& result);
+ // Returns true iff the given type, which must be valid, is registered.
+ bool IsTypeRegistered(syncable::ModelType model_type) const;
base::NonThreadSafe non_thread_safe_;
+ RegistrationStatus registration_statuses_[syncable::MODEL_TYPE_COUNT];
// Weak pointer.
invalidation::InvalidationClient* invalidation_client_;
- RegistrationStatusMap registration_status_;
DISALLOW_COPY_AND_ASSIGN(RegistrationManager);
};
diff --git a/chrome/browser/sync/notifier/registration_manager_unittest.cc b/chrome/browser/sync/notifier/registration_manager_unittest.cc
index ac5b236..a452d75 100644
--- a/chrome/browser/sync/notifier/registration_manager_unittest.cc
+++ b/chrome/browser/sync/notifier/registration_manager_unittest.cc
@@ -5,17 +5,20 @@
#include "chrome/browser/sync/notifier/registration_manager.h"
#include <algorithm>
+#include <cmath>
#include <cstddef>
#include <deque>
#include <vector>
#include "base/basictypes.h"
+#include "base/message_loop.h"
#include "chrome/browser/sync/notifier/invalidation_util.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "google/cacheinvalidation/invalidation-client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace sync_notifier {
+namespace {
syncable::ModelType ObjectIdToModelType(
const invalidation::ObjectId& object_id) {
@@ -24,33 +27,62 @@ syncable::ModelType ObjectIdToModelType(
return model_type;
}
-// Fake invalidation client that just stores the args of calls to
-// Register().
+// Fake registration manager that lets you override jitter.
+class FakeRegistrationManager : public RegistrationManager {
+ public:
+ explicit FakeRegistrationManager(
+ invalidation::InvalidationClient* invalidation_client)
+ : RegistrationManager(invalidation_client),
+ jitter_(0.0) {}
+
+ virtual ~FakeRegistrationManager() {}
+
+ void SetJitter(double jitter) {
+ jitter_ = jitter;
+ }
+
+ protected:
+ virtual double GetJitter() {
+ return jitter_;
+ }
+
+ private:
+ double jitter_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeRegistrationManager);
+};
+
+// Fake invalidation client that just stores the currently-registered
+// model types.
class FakeInvalidationClient : public invalidation::InvalidationClient {
public:
FakeInvalidationClient() {}
virtual ~FakeInvalidationClient() {}
+ void LoseRegistration(syncable::ModelType model_type) {
+ EXPECT_GT(registered_types_.count(model_type), 0u);
+ registered_types_.erase(model_type);
+ }
+
+ void LoseAllRegistrations() {
+ registered_types_.clear();
+ }
+
+ // invalidation::InvalidationClient implementation.
+
virtual void Start(const std::string& state) {}
virtual void Register(const invalidation::ObjectId& oid) {
- registered_types.push_back(ObjectIdToModelType(oid));
+ syncable::ModelType model_type = ObjectIdToModelType(oid);
+ EXPECT_EQ(0u, registered_types_.count(model_type));
+ registered_types_.insert(model_type);
}
virtual void Unregister(const invalidation::ObjectId& oid) {
- syncable::ModelType type_to_unregister = ObjectIdToModelType(oid);
- std::vector<syncable::ModelType>::iterator it = std::find(
- registered_types.begin(),
- registered_types.end(),
- type_to_unregister);
-
- if(it == registered_types.end()) {
- // We should not be unregistering a thing that is not yet registered.
- ADD_FAILURE();
- } else {
- registered_types.erase(it);
- }
+ syncable::ModelType model_type = ObjectIdToModelType(oid);
+ EXPECT_GT(registered_types_.count(model_type), 0u);
+ registered_types_.erase(model_type);
}
virtual invalidation::NetworkEndpoint* network_endpoint() {
@@ -58,177 +90,283 @@ class FakeInvalidationClient : public invalidation::InvalidationClient {
return NULL;
}
- std::vector<syncable::ModelType> registered_types;
+ const syncable::ModelTypeSet GetRegisteredTypes() const {
+ return registered_types_;
+ }
private:
+ syncable::ModelTypeSet registered_types_;
+
DISALLOW_COPY_AND_ASSIGN(FakeInvalidationClient);
};
+const syncable::ModelType kModelTypes[] = {
+ syncable::BOOKMARKS,
+ syncable::PREFERENCES,
+ syncable::THEMES,
+ syncable::AUTOFILL,
+ syncable::EXTENSIONS,
+};
+const size_t kModelTypeCount = arraysize(kModelTypes);
+
+void ExpectPendingRegistrations(
+ const syncable::ModelTypeSet& expected_pending_types,
+ double expected_delay_seconds,
+ const RegistrationManager::PendingRegistrationMap& pending_registrations) {
+ syncable::ModelTypeSet pending_types;
+ for (RegistrationManager::PendingRegistrationMap::const_iterator it =
+ pending_registrations.begin(); it != pending_registrations.end();
+ ++it) {
+ SCOPED_TRACE(syncable::ModelTypeToString(it->first));
+ pending_types.insert(it->first);
+ base::TimeDelta offset =
+ it->second.last_registration_request -
+ it->second.registration_attempt;
+ base::TimeDelta expected_delay =
+ base::TimeDelta::FromSeconds(
+ static_cast<int64>(expected_delay_seconds)) + offset;
+ // TODO(akalin): Add base::PrintTo() for base::Time and
+ // base::TimeDeltas.
+ EXPECT_EQ(it->second.delay, expected_delay)
+ << it->second.delay.InMicroseconds()
+ << ", " << expected_delay.InMicroseconds();
+ if (it->second.delay <= base::TimeDelta()) {
+ EXPECT_EQ(it->second.actual_delay, base::TimeDelta());
+ } else {
+ EXPECT_EQ(it->second.delay, it->second.actual_delay);
+ }
+ }
+ EXPECT_EQ(expected_pending_types, pending_types);
+}
+
class RegistrationManagerTest : public testing::Test {
protected:
RegistrationManagerTest()
- : registration_manager_(&fake_invalidation_client_) {}
+ : fake_registration_manager_(&fake_invalidation_client_) {}
virtual ~RegistrationManagerTest() {}
+ void LoseRegistrations(const syncable::ModelTypeSet& types) {
+ for (syncable::ModelTypeSet::const_iterator it = types.begin();
+ it != types.end(); ++it) {
+ fake_invalidation_client_.LoseRegistration(*it);
+ fake_registration_manager_.MarkRegistrationLost(*it);
+ }
+ }
+
+ // Used by MarkRegistrationLostBackoff* tests.
+ void RunBackoffTest(double jitter) {
+ fake_registration_manager_.SetJitter(jitter);
+ syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount);
+ fake_registration_manager_.SetRegisteredTypes(types);
+
+ // Lose some types.
+ syncable::ModelTypeSet lost_types(kModelTypes, kModelTypes + 2);
+ LoseRegistrations(lost_types);
+ ExpectPendingRegistrations(
+ lost_types, 0.0,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Trigger another failure to start delaying.
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ LoseRegistrations(lost_types);
+
+ double scaled_jitter =
+ jitter * RegistrationManager::kRegistrationDelayMaxJitter;
+
+ double expected_delay =
+ RegistrationManager::kInitialRegistrationDelaySeconds *
+ (1.0 + scaled_jitter);
+ expected_delay = std::floor(expected_delay);
+ ExpectPendingRegistrations(
+ lost_types, expected_delay,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Trigger another failure.
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ LoseRegistrations(lost_types);
+ expected_delay *=
+ RegistrationManager::kRegistrationDelayExponent + scaled_jitter;
+ expected_delay = std::floor(expected_delay);
+ ExpectPendingRegistrations(
+ lost_types, expected_delay,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Trigger enough failures to hit the ceiling.
+ while (expected_delay < RegistrationManager::kMaxRegistrationDelaySeconds) {
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ LoseRegistrations(lost_types);
+ expected_delay *=
+ RegistrationManager::kRegistrationDelayExponent + scaled_jitter;
+ expected_delay = std::floor(expected_delay);
+ }
+ ExpectPendingRegistrations(
+ lost_types,
+ RegistrationManager::kMaxRegistrationDelaySeconds,
+ fake_registration_manager_.GetPendingRegistrations());
+ }
+
FakeInvalidationClient fake_invalidation_client_;
- RegistrationManager registration_manager_;
+ FakeRegistrationManager fake_registration_manager_;
private:
+ // Needed by timers in RegistrationManager.
+ MessageLoop message_loop_;
+
DISALLOW_COPY_AND_ASSIGN(RegistrationManagerTest);
};
-TEST_F(RegistrationManagerTest, RegisterType) {
- const syncable::ModelType kModelTypes[] = {
- syncable::BOOKMARKS,
- syncable::PREFERENCES,
- syncable::THEMES,
- syncable::AUTOFILL,
- syncable::EXTENSIONS,
- };
- const size_t kModelTypeCount = arraysize(kModelTypes);
-
- // Register types.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- // Register twice; it shouldn't matter.
- registration_manager_.RegisterType(kModelTypes[i]);
- registration_manager_.RegisterType(kModelTypes[i]);
- }
+TEST_F(RegistrationManagerTest, SetRegisteredTypes) {
+ syncable::ModelTypeSet no_types;
+ syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount);
- ASSERT_EQ(kModelTypeCount,
- fake_invalidation_client_.registered_types.size());
+ EXPECT_EQ(no_types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(no_types, fake_invalidation_client_.GetRegisteredTypes());
- // Everything should be registered.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
- }
+ fake_registration_manager_.SetRegisteredTypes(types);
+ EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes());
- // Check object IDs.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_EQ(kModelTypes[i],
- fake_invalidation_client_.registered_types[i]);
- }
+ types.insert(syncable::APPS);
+ types.erase(syncable::BOOKMARKS);
+ fake_registration_manager_.SetRegisteredTypes(types);
+ EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes());
}
-TEST_F(RegistrationManagerTest, UnregisterType) {
- const syncable::ModelType kModelTypes[] = {
- syncable::BOOKMARKS,
- syncable::PREFERENCES,
- syncable::THEMES,
- syncable::AUTOFILL,
- syncable::EXTENSIONS,
- };
- const size_t kModelTypeCount = arraysize(kModelTypes);
-
- // Register types.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- // Register twice; it shouldn't matter.
- registration_manager_.RegisterType(kModelTypes[i]);
- }
-
- ASSERT_EQ(kModelTypeCount,
- fake_invalidation_client_.registered_types.size());
-
- // Everything should be registered.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
- }
-
- // Check object IDs.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_EQ(kModelTypes[i],
- fake_invalidation_client_.registered_types[i]);
- }
-
- // Now unregister the extension.
- registration_manager_.UnregisterType(syncable::EXTENSIONS);
-
- // Check the count and the types currently registered to ensure extensions
- // is unregistered.
- ASSERT_EQ(kModelTypeCount - 1,
- fake_invalidation_client_.registered_types.size());
+int GetRoundedBackoff(double retry_interval, double jitter) {
+ const double kInitialRetryInterval = 3.0;
+ const double kMinRetryInterval = 2.0;
+ const double kMaxRetryInterval = 20.0;
+ const double kBackoffExponent = 2.0;
+ const double kMaxJitter = 0.5;
+
+ return static_cast<int>(
+ RegistrationManager::CalculateBackoff(retry_interval,
+ kInitialRetryInterval,
+ kMinRetryInterval,
+ kMaxRetryInterval,
+ kBackoffExponent,
+ jitter,
+ kMaxJitter));
+}
- // Check object IDs.
- for (size_t i = 0; i < kModelTypeCount - 1; ++i) {
- EXPECT_EQ(kModelTypes[i],
- fake_invalidation_client_.registered_types[i]);
- }
+TEST_F(RegistrationManagerTest, CalculateBackoff) {
+ // Test initial.
+ EXPECT_EQ(2, GetRoundedBackoff(0.0, -1.0));
+ EXPECT_EQ(3, GetRoundedBackoff(0.0, 0.0));
+ EXPECT_EQ(4, GetRoundedBackoff(0.0, +1.0));
+
+ // Test non-initial.
+ EXPECT_EQ(4, GetRoundedBackoff(3.0, -1.0));
+ EXPECT_EQ(6, GetRoundedBackoff(3.0, 0.0));
+ EXPECT_EQ(7, GetRoundedBackoff(3.0, +1.0));
+
+ EXPECT_EQ(7, GetRoundedBackoff(5.0, -1.0));
+ EXPECT_EQ(10, GetRoundedBackoff(5.0, 0.0));
+ EXPECT_EQ(12, GetRoundedBackoff(5.0, +1.0));
+
+ // Test ceiling.
+ EXPECT_EQ(19, GetRoundedBackoff(13.0, -1.0));
+ EXPECT_EQ(20, GetRoundedBackoff(13.0, 0.0));
+ EXPECT_EQ(20, GetRoundedBackoff(13.0, +1.0));
}
TEST_F(RegistrationManagerTest, MarkRegistrationLost) {
- const syncable::ModelType kModelTypes[] = {
- syncable::BOOKMARKS,
- syncable::PREFERENCES,
- syncable::THEMES,
- syncable::AUTOFILL,
- syncable::EXTENSIONS,
- };
- const size_t kModelTypeCount = arraysize(kModelTypes);
-
- // Register types.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- registration_manager_.RegisterType(kModelTypes[i]);
- }
-
- ASSERT_EQ(kModelTypeCount,
- fake_invalidation_client_.registered_types.size());
+ syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount);
+
+ fake_registration_manager_.SetRegisteredTypes(types);
+ EXPECT_TRUE(fake_registration_manager_.GetPendingRegistrations().empty());
+
+ // Lose some types.
+ syncable::ModelTypeSet lost_types(
+ kModelTypes, kModelTypes + 3);
+ syncable::ModelTypeSet non_lost_types(
+ kModelTypes + 3, kModelTypes + kModelTypeCount);
+ LoseRegistrations(lost_types);
+ ExpectPendingRegistrations(
+ lost_types, 0.0,
+ fake_registration_manager_.GetPendingRegistrations());
+ EXPECT_EQ(non_lost_types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(non_lost_types, fake_invalidation_client_.GetRegisteredTypes());
+
+ // Pretend we waited long enough to re-register.
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes());
+}
- // All should be registered.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
- }
+TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffLow) {
+ RunBackoffTest(-1.0);
+}
- // Mark the registrations of all but the first one lost.
- for (size_t i = 1; i < kModelTypeCount; ++i) {
- registration_manager_.MarkRegistrationLost(kModelTypes[i]);
- }
+TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffMid) {
+ RunBackoffTest(0.0);
+}
- ASSERT_EQ(2 * kModelTypeCount - 1,
- fake_invalidation_client_.registered_types.size());
+TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffHigh) {
+ RunBackoffTest(+1.0);
+}
- // All should still be registered.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
- }
+TEST_F(RegistrationManagerTest, MarkRegistrationLostBackoffReset) {
+ syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount);
+
+ fake_registration_manager_.SetRegisteredTypes(types);
+
+ // Lose some types.
+ syncable::ModelTypeSet lost_types(kModelTypes, kModelTypes + 2);
+ LoseRegistrations(lost_types);
+ ExpectPendingRegistrations(
+ lost_types, 0.0,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Trigger another failure to start delaying.
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ LoseRegistrations(lost_types);
+ double expected_delay =
+ RegistrationManager::kInitialRegistrationDelaySeconds;
+ ExpectPendingRegistrations(
+ lost_types, expected_delay,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Set types again.
+ fake_registration_manager_.SetRegisteredTypes(types);
+ ExpectPendingRegistrations(
+ syncable::ModelTypeSet(), 0.0,
+ fake_registration_manager_.GetPendingRegistrations());
}
TEST_F(RegistrationManagerTest, MarkAllRegistrationsLost) {
- const syncable::ModelType kModelTypes[] = {
- syncable::BOOKMARKS,
- syncable::PREFERENCES,
- syncable::THEMES,
- syncable::AUTOFILL,
- syncable::EXTENSIONS,
- };
- const size_t kModelTypeCount = arraysize(kModelTypes);
-
- // Register types.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- registration_manager_.RegisterType(kModelTypes[i]);
- }
-
- ASSERT_EQ(kModelTypeCount,
- fake_invalidation_client_.registered_types.size());
-
- // All should be registered.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
- }
-
- // Mark the registrations of all but the first one lost. Then mark
- // everything lost.
- for (size_t i = 1; i < kModelTypeCount; ++i) {
- registration_manager_.MarkRegistrationLost(kModelTypes[i]);
- }
- registration_manager_.MarkAllRegistrationsLost();
-
- ASSERT_EQ(3 * kModelTypeCount - 1,
- fake_invalidation_client_.registered_types.size());
-
- // All should still be registered.
- for (size_t i = 0; i < kModelTypeCount; ++i) {
- EXPECT_TRUE(registration_manager_.IsRegistered(kModelTypes[i]));
- }
+ syncable::ModelTypeSet types(kModelTypes, kModelTypes + kModelTypeCount);
+
+ fake_registration_manager_.SetRegisteredTypes(types);
+
+ fake_invalidation_client_.LoseAllRegistrations();
+ fake_registration_manager_.MarkAllRegistrationsLost();
+
+ syncable::ModelTypeSet expected_types;
+ EXPECT_EQ(expected_types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(expected_types, fake_invalidation_client_.GetRegisteredTypes());
+
+ ExpectPendingRegistrations(
+ types, 0.0,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Trigger another failure to start delaying.
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ fake_invalidation_client_.LoseAllRegistrations();
+ fake_registration_manager_.MarkAllRegistrationsLost();
+ double expected_delay =
+ RegistrationManager::kInitialRegistrationDelaySeconds;
+ ExpectPendingRegistrations(
+ types, expected_delay,
+ fake_registration_manager_.GetPendingRegistrations());
+
+ // Pretend we waited long enough to re-register.
+ fake_registration_manager_.FirePendingRegistrationsForTest();
+ EXPECT_EQ(types, fake_registration_manager_.GetRegisteredTypes());
+ EXPECT_EQ(types, fake_invalidation_client_.GetRegisteredTypes());
}
+} // namespace
} // namespace notifier
diff --git a/chrome/browser/sync/notifier/server_notifier_thread.cc b/chrome/browser/sync/notifier/server_notifier_thread.cc
deleted file mode 100644
index 9dd62bf..0000000
--- a/chrome/browser/sync/notifier/server_notifier_thread.cc
+++ /dev/null
@@ -1,174 +0,0 @@
-// Copyright (c) 2010 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.
-
-#include "chrome/browser/sync/notifier/server_notifier_thread.h"
-
-#include <string>
-#include <vector>
-
-#include "base/logging.h"
-#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
-#include "googleurl/src/gurl.h"
-#include "jingle/notifier/base/notifier_options.h"
-#include "jingle/notifier/listener/notification_defines.h"
-#include "talk/xmpp/xmppclient.h"
-#include "webkit/glue/webkit_glue.h"
-
-namespace sync_notifier {
-
-ServerNotifierThread::ServerNotifierThread(
- const notifier::NotifierOptions& notifier_options,
- const std::string& state, StateWriter* state_writer)
- : notifier::MediatorThreadImpl(notifier_options),
- state_(state),
- state_writers_(new ObserverListThreadSafe<StateWriter>()),
- state_writer_(state_writer) {
- DCHECK_EQ(notifier::NOTIFICATION_SERVER,
- notifier_options.notification_method);
- DCHECK(state_writer_);
- state_writers_->AddObserver(state_writer_);
-}
-
-ServerNotifierThread::~ServerNotifierThread() {}
-
-void ServerNotifierThread::ListenForUpdates() {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this, &ServerNotifierThread::DoListenForUpdates));
-}
-
-void ServerNotifierThread::SubscribeForUpdates(
- const std::vector<std::string>& subscribed_services_list) {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(
- this, &ServerNotifierThread::RegisterTypes));
-
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(
- this, &ServerNotifierThread::SignalSubscribed));
-}
-
-void ServerNotifierThread::UpdateEnabledTypes(
- const syncable::ModelTypeSet& types) {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(
- this,
- &ServerNotifierThread::SetRegisteredTypes,
- types));
-
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(
- this, &ServerNotifierThread::RegisterTypes));
-}
-
-void ServerNotifierThread::Logout() {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- state_writers_->RemoveObserver(state_writer_);
- state_writer_ = NULL;
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &ServerNotifierThread::StopInvalidationListener));
- MediatorThreadImpl::Logout();
-}
-
-void ServerNotifierThread::SendNotification(
- const OutgoingNotificationData& data) {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- NOTREACHED() << "Shouldn't send notifications if ServerNotifierThread is "
- "used";
-}
-
-void ServerNotifierThread::OnInvalidate(
- syncable::ModelType model_type,
- const std::string& payload) {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- DCHECK_GE(model_type, syncable::FIRST_REAL_MODEL_TYPE);
- DCHECK_LT(model_type, syncable::MODEL_TYPE_COUNT);
- VLOG(1) << "OnInvalidate: " << syncable::ModelTypeToString(model_type);
-
- syncable::ModelTypeBitSet model_types;
- model_types[model_type] = true;
- IncomingNotificationData notification_data;
- notification_data.service_url = model_types.to_string();
- notification_data.service_specific_data = payload;
- observers_->Notify(&Observer::OnIncomingNotification, notification_data);
-}
-
-void ServerNotifierThread::OnInvalidateAll() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- VLOG(1) << "OnInvalidateAll";
-
- syncable::ModelTypeBitSet model_types;
- model_types.set(); // InvalidateAll, so set all datatypes to true.
- IncomingNotificationData notification_data;
- notification_data.service_url = model_types.to_string();
- notification_data.service_specific_data = std::string(); // No payload.
- observers_->Notify(&Observer::OnIncomingNotification, notification_data);
-}
-
-void ServerNotifierThread::WriteState(const std::string& state) {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- VLOG(1) << "WriteState";
- state_writers_->Notify(&StateWriter::WriteState, state);
-}
-
-void ServerNotifierThread::DoListenForUpdates() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- if (!base_task_.get()) {
- return;
- }
-
- if (chrome_invalidation_client_.get()) {
- // If we already have an invalidation client, simply change the
- // base task.
- chrome_invalidation_client_->ChangeBaseTask(base_task_);
- } else {
- // Otherwise, create the invalidation client.
- chrome_invalidation_client_.reset(new ChromeInvalidationClient());
-
- // TODO(akalin): Make cache_guid() part of the client ID. If we do
- // so and we somehow propagate it up to the server somehow, we can
- // make it so that we won't receive any notifications that were
- // generated from our own changes.
- const std::string kClientId = "server_notifier_thread";
- // Use user agent as |client_info| so we can use it for debugging
- // server-side.
- const std::string& client_info = webkit_glue::GetUserAgent(GURL());
- chrome_invalidation_client_->Start(
- kClientId, client_info, state_, this, this, base_task_);
- RegisterTypes();
- state_.clear();
- }
-}
-
-void ServerNotifierThread::RegisterTypes() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- if (!chrome_invalidation_client_.get()) {
- return;
- }
- chrome_invalidation_client_->RegisterTypes(registered_types_);
-}
-
-void ServerNotifierThread::SignalSubscribed() {
- observers_->Notify(&Observer::OnSubscriptionStateChange, true);
-}
-
-void ServerNotifierThread::StopInvalidationListener() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- chrome_invalidation_client_.reset();
-}
-
-void ServerNotifierThread::SetRegisteredTypes(syncable::ModelTypeSet types) {
- registered_types_ = types;
-}
-
-} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/server_notifier_thread.h b/chrome/browser/sync/notifier/server_notifier_thread.h
deleted file mode 100644
index 4800735..0000000
--- a/chrome/browser/sync/notifier/server_notifier_thread.h
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (c) 2010 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.
-//
-// This class is the (hackish) way to use the XMPP parts of
-// MediatorThread for server-issued notifications.
-//
-// TODO(akalin): Decomp MediatorThread into an XMPP service part and a
-// notifications-specific part and use the XMPP service part for
-// server-issued notifications.
-
-#ifndef CHROME_BROWSER_SYNC_NOTIFIER_SERVER_NOTIFIER_THREAD_H_
-#define CHROME_BROWSER_SYNC_NOTIFIER_SERVER_NOTIFIER_THREAD_H_
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "base/observer_list_threadsafe.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
-#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
-#include "chrome/browser/sync/notifier/state_writer.h"
-#include "chrome/browser/sync/syncable/model_type.h"
-#include "jingle/notifier/listener/mediator_thread_impl.h"
-
-namespace notifier {
-struct NotifierOptions;
-}
-
-namespace sync_notifier {
-
-class ServerNotifierThread
- : public notifier::MediatorThreadImpl,
- public ChromeInvalidationClient::Listener,
- public StateWriter {
- public:
- // Does not take ownership of |state_writer| (which may not
- // be NULL).
- explicit ServerNotifierThread(
- const notifier::NotifierOptions& notifier_options,
- const std::string& state, StateWriter* state_writer);
-
- virtual ~ServerNotifierThread();
-
- // Overridden to start listening to server notifications.
- virtual void ListenForUpdates();
-
- // Overridden to immediately notify the delegate that subscriptions
- // (i.e., notifications) are on. Must be called only after a call
- // to ListenForUpdates().
- virtual void SubscribeForUpdates(
- const std::vector<std::string>& subscribed_services_list);
-
- // Overridden to stop listening to server notifications.
- virtual void Logout();
-
- // Must not be called.
- virtual void SendNotification(const OutgoingNotificationData& data);
-
- // ChromeInvalidationClient::Listener implementation.
- // We pass on two pieces of information to observers through the
- // IncomingNotificationData.
- // - the model type being invalidated, through IncomingNotificationData's
- // service_url.
- // - the invalidation payload for that model type, through
- // IncomingNotificationData's service_specific_data.
- virtual void OnInvalidate(syncable::ModelType model_type,
- const std::string& payload);
- virtual void OnInvalidateAll();
-
- // StateWriter implementation.
- virtual void WriteState(const std::string& state);
-
- virtual void UpdateEnabledTypes(const syncable::ModelTypeSet& types);
-
- private:
- // Posted to the worker thread by ListenForUpdates().
- void DoListenForUpdates();
-
- // Posted to the worker thread by SubscribeForUpdates().
- void RegisterTypes();
-
- void SignalSubscribed();
-
- // Posted to the worker thread by Logout().
- void StopInvalidationListener();
-
- std::string state_;
- // Hack to get the nice thread-safe behavior for |state_writer_|.
- scoped_refptr<ObserverListThreadSafe<StateWriter> > state_writers_;
- // We still need to keep |state_writer_| around to remove it from
- // |state_writers_|.
- StateWriter* state_writer_;
- scoped_ptr<ChromeInvalidationClient> chrome_invalidation_client_;
-
- syncable::ModelTypeSet registered_types_;
-
- void SetRegisteredTypes(syncable::ModelTypeSet types);
-};
-
-} // namespace sync_notifier
-
-DISABLE_RUNNABLE_METHOD_REFCOUNT(sync_notifier::ServerNotifierThread);
-
-#endif // CHROME_BROWSER_SYNC_NOTIFIER_SERVER_NOTIFIER_THREAD_H_
diff --git a/chrome/browser/sync/notifier/server_notifier_thread_unittest.cc b/chrome/browser/sync/notifier/server_notifier_thread_unittest.cc
deleted file mode 100644
index 3873c42..0000000
--- a/chrome/browser/sync/notifier/server_notifier_thread_unittest.cc
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright (c) 2010 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.
-
-#include <string>
-
-#include "base/compiler_specific.h"
-#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/task.h"
-#include "chrome/browser/sync/notifier/server_notifier_thread.h"
-#include "chrome/browser/sync/notifier/state_writer.h"
-#include "jingle/notifier/base/fake_base_task.h"
-#include "jingle/notifier/base/notifier_options.h"
-#include "talk/xmpp/xmppclientsettings.h"
-#include "testing/gmock/include/gmock/gmock.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace sync_notifier {
-
-class FakeServerNotifierThread : public ServerNotifierThread {
- public:
- FakeServerNotifierThread()
- : ServerNotifierThread(notifier::NotifierOptions(), "fake state",
- ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
-
- virtual ~FakeServerNotifierThread() {}
-
- virtual void Start() {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- ServerNotifierThread::Start();
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &FakeServerNotifierThread::InitFakes));
- }
-
- virtual void Logout() {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &FakeServerNotifierThread::DestroyFakes));
- ServerNotifierThread::Logout();
- }
-
- // We prevent the real connection attempt from happening and use
- // SimulateConnection()/SimulateDisconnection() instead.
- virtual void Login(const buzz::XmppClientSettings& settings) {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- }
-
- // We pass ourselves as the StateWriter in the constructor, so shim
- // out WriteState() to prevent an infinite loop.
- virtual void WriteState(const std::string& state) {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- }
-
- void SimulateConnect() {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &FakeServerNotifierThread::DoSimulateConnect));
- }
-
- void SimulateDisconnect() {
- DCHECK_EQ(MessageLoop::current(), parent_message_loop_);
- worker_message_loop()->PostTask(
- FROM_HERE,
- NewRunnableMethod(this,
- &FakeServerNotifierThread::DoSimulateDisconnect));
- }
-
- private:
- void InitFakes() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- fake_base_task_.reset(new notifier::FakeBaseTask());
- }
-
- void DestroyFakes() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- fake_base_task_.reset();
- }
-
- void DoSimulateConnect() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- OnConnect(fake_base_task_->AsWeakPtr());
- }
-
- void DoSimulateDisconnect() {
- DCHECK_EQ(MessageLoop::current(), worker_message_loop());
- OnDisconnect();
- }
-
- // Used only on the worker thread.
- scoped_ptr<notifier::FakeBaseTask> fake_base_task_;
-};
-
-} // namespace sync_notifier
-
-DISABLE_RUNNABLE_METHOD_REFCOUNT(sync_notifier::FakeServerNotifierThread);
-
-namespace sync_notifier {
-
-namespace {
-
-class ServerNotifierThreadTest : public testing::Test {
- protected:
- MessageLoop message_loop_;
-};
-
-syncable::ModelTypeSet GetTypeSetWithAllTypes() {
- syncable::ModelTypeSet all_types;
-
- for (int i = syncable::FIRST_REAL_MODEL_TYPE;
- i < syncable::MODEL_TYPE_COUNT; ++i) {
- syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
- all_types.insert(model_type);
- }
-
- return all_types;
-}
-
-TEST_F(ServerNotifierThreadTest, Basic) {
- FakeServerNotifierThread server_notifier_thread;
-
- server_notifier_thread.Start();
- server_notifier_thread.Login(buzz::XmppClientSettings());
- server_notifier_thread.SimulateConnect();
- server_notifier_thread.ListenForUpdates();
- server_notifier_thread.SubscribeForUpdates(std::vector<std::string>());
- server_notifier_thread.Logout();
-}
-
-TEST_F(ServerNotifierThreadTest, DisconnectBeforeListen) {
- FakeServerNotifierThread server_notifier_thread;
-
- server_notifier_thread.Start();
- server_notifier_thread.Login(buzz::XmppClientSettings());
- server_notifier_thread.ListenForUpdates();
- server_notifier_thread.SubscribeForUpdates(std::vector<std::string>());
- server_notifier_thread.Logout();
-}
-
-TEST_F(ServerNotifierThreadTest, Disconnected) {
- FakeServerNotifierThread server_notifier_thread;
-
- server_notifier_thread.Start();
- server_notifier_thread.Login(buzz::XmppClientSettings());
- server_notifier_thread.SimulateConnect();
- server_notifier_thread.SimulateDisconnect();
- server_notifier_thread.ListenForUpdates();
- server_notifier_thread.SubscribeForUpdates(std::vector<std::string>());
- server_notifier_thread.Logout();
-}
-
-} // namespace
-
-} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/sync_notifier.h b/chrome/browser/sync/notifier/sync_notifier.h
new file mode 100644
index 0000000..5bf6c8c
--- /dev/null
+++ b/chrome/browser/sync/notifier/sync_notifier.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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.
+//
+// Interface to the sync notifier, which is an object that receives
+// notifications when updates are available for a set of sync types.
+// All the observers are notified when such an event happens.
+//
+// A SyncNotifier must be destroyed on the same thread it was created on,
+// and all its methods must be called on the same thread (not necessarily
+// the one it was created on). If the methods thread is different from the
+// creation thread, then the methods thread must not exist when the SyncNotifier
+// is created and destroyed.
+//
+// In particular, the SyncNotifier will be created on the UI thread, the syncer
+// core thread will be created, the SyncNotifier will be used on that core
+// thread, the syncer core thread will be destroyed, and then the SyncNotifier
+// will be destroyed.
+//
+// TODO(akalin): Remove the code to deal with this situation once the syncer
+// core thread goes away.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_H_
+
+#include <string>
+
+#include "chrome/browser/sync/syncable/model_type.h"
+
+namespace sync_notifier {
+class SyncNotifierObserver;
+
+class SyncNotifier {
+ public:
+ SyncNotifier() {}
+ virtual ~SyncNotifier() {}
+
+ virtual void AddObserver(SyncNotifierObserver* observer) = 0;
+ virtual void RemoveObserver(SyncNotifierObserver* observer) = 0;
+
+ // SetState must be called once, before any call to UpdateCredentials.
+ virtual void SetState(const std::string& state) = 0;
+
+ // The observers won't be notified of any notifications until
+ // UpdateCredentials is called at least once. It can be called more than
+ // once.
+ virtual void UpdateCredentials(
+ const std::string& email, const std::string& token) = 0;
+
+ virtual void UpdateEnabledTypes(const syncable::ModelTypeSet& types) = 0;
+
+ // This is here only to support the old p2p notification implementation,
+ // which is still used by sync integration tests.
+ // TODO(akalin): Remove this once we move the integration tests off p2p
+ // notifications.
+ virtual void SendNotification() = 0;
+};
+} // namespace sync_notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_H_
+
diff --git a/chrome/browser/sync/notifier/sync_notifier_factory.cc b/chrome/browser/sync/notifier/sync_notifier_factory.cc
new file mode 100644
index 0000000..1a1a531
--- /dev/null
+++ b/chrome/browser/sync/notifier/sync_notifier_factory.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/notifier/sync_notifier_factory.h"
+
+#include <string>
+
+#include "base/command_line.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "chrome/browser/sync/notifier/non_blocking_invalidation_notifier.h"
+#include "chrome/browser/sync/notifier/p2p_notifier.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/common/chrome_switches.h"
+#include "jingle/notifier/base/const_communicator.h"
+#include "jingle/notifier/base/notifier_options.h"
+#include "net/base/host_port_pair.h"
+
+namespace sync_notifier {
+namespace {
+
+// TODO(akalin): Figure out whether this should be a method of
+// HostPortPair.
+net::HostPortPair StringToHostPortPair(const std::string& host_port_str,
+ uint16 default_port) {
+ std::string::size_type colon_index = host_port_str.find(':');
+ if (colon_index == std::string::npos) {
+ return net::HostPortPair(host_port_str, default_port);
+ }
+
+ std::string host = host_port_str.substr(0, colon_index);
+ std::string port_str = host_port_str.substr(colon_index + 1);
+ int port = default_port;
+ if (!base::StringToInt(port_str, &port) ||
+ (port <= 0) || (port > kuint16max)) {
+ LOG(WARNING) << "Could not parse valid port from " << port_str
+ << "; using port " << default_port;
+ return net::HostPortPair(host, default_port);
+ }
+
+ return net::HostPortPair(host, port);
+}
+
+SyncNotifier* CreateDefaultSyncNotifier(
+ const CommandLine& command_line,
+ const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
+ const std::string& client_info) {
+ // Contains options specific to how sync clients send and listen to
+ // jingle notifications.
+ notifier::NotifierOptions notifier_options;
+ notifier_options.request_context_getter = request_context_getter;
+
+ // Override the notification server host from the command-line, if provided.
+ if (command_line.HasSwitch(switches::kSyncNotificationHost)) {
+ std::string value(command_line.GetSwitchValueASCII(
+ switches::kSyncNotificationHost));
+ if (!value.empty()) {
+ notifier_options.xmpp_host_port =
+ StringToHostPortPair(value, notifier::kDefaultXmppPort);
+ }
+ VLOG(1) << "Using " << notifier_options.xmpp_host_port.ToString()
+ << " for test sync notification server.";
+ }
+
+ notifier_options.try_ssltcp_first =
+ command_line.HasSwitch(switches::kSyncTrySsltcpFirstForXmpp);
+ if (notifier_options.try_ssltcp_first)
+ VLOG(1) << "Trying SSL/TCP port before XMPP port for notifications.";
+
+ notifier_options.invalidate_xmpp_login =
+ command_line.HasSwitch(switches::kSyncInvalidateXmppLogin);
+ if (notifier_options.invalidate_xmpp_login) {
+ VLOG(1) << "Invalidating sync XMPP login.";
+ }
+
+ notifier_options.allow_insecure_connection =
+ command_line.HasSwitch(switches::kSyncAllowInsecureXmppConnection);
+ if (notifier_options.allow_insecure_connection) {
+ VLOG(1) << "Allowing insecure XMPP connections.";
+ }
+
+ if (command_line.HasSwitch(switches::kSyncNotificationMethod)) {
+ const std::string notification_method_str(
+ command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod));
+ notifier_options.notification_method =
+ notifier::StringToNotificationMethod(notification_method_str);
+ }
+
+ if (notifier_options.notification_method == notifier::NOTIFICATION_P2P) {
+ return new P2PNotifier(notifier_options);
+ }
+
+ return new NonBlockingInvalidationNotifier(notifier_options, client_info);
+}
+} // namespace
+
+SyncNotifierFactory::SyncNotifierFactory(const std::string& client_info)
+ : client_info_(client_info) {}
+
+SyncNotifierFactory::~SyncNotifierFactory() {
+}
+
+SyncNotifier* SyncNotifierFactory::CreateSyncNotifier(
+ const CommandLine& command_line,
+ const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) {
+ return CreateDefaultSyncNotifier(command_line,
+ request_context_getter,
+ client_info_);
+}
+} // namespace sync_notifier
diff --git a/chrome/browser/sync/notifier/sync_notifier_factory.h b/chrome/browser/sync/notifier/sync_notifier_factory.h
new file mode 100644
index 0000000..5a0f618
--- /dev/null
+++ b/chrome/browser/sync/notifier/sync_notifier_factory.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_FACTORY_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_FACTORY_H_
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+
+class CommandLine;
+
+namespace net {
+class URLRequestContextGetter;
+}
+
+namespace sync_notifier {
+
+class SyncNotifier;
+
+// Class to instantiate various implementations of the SyncNotifier interface.
+class SyncNotifierFactory {
+ public:
+ // |client_info| is a string identifying the client, e.g. a user
+ // agent string.
+ explicit SyncNotifierFactory(const std::string& client_info);
+ ~SyncNotifierFactory();
+
+ // Creates the appropriate sync notifier. The caller should take ownership
+ // of the object returned and delete it when no longer used.
+ SyncNotifier* CreateSyncNotifier(
+ const CommandLine& command_line,
+ const scoped_refptr<net::URLRequestContextGetter>&
+ request_context_getter);
+
+ private:
+ const std::string client_info_;
+};
+
+} // namespace sync_notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_FACTORY_H_
diff --git a/chrome/browser/sync/notifier/sync_notifier_observer.h b/chrome/browser/sync/notifier/sync_notifier_observer.h
new file mode 100644
index 0000000..f2d7cda
--- /dev/null
+++ b/chrome/browser/sync/notifier/sync_notifier_observer.h
@@ -0,0 +1,31 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_OBSERVER_H_
+#define CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_OBSERVER_H_
+#pragma once
+
+#include <string>
+
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+
+namespace sync_notifier {
+
+class SyncNotifierObserver {
+ public:
+ SyncNotifierObserver() {}
+ virtual ~SyncNotifierObserver() {}
+
+ virtual void OnIncomingNotification(
+ const syncable::ModelTypePayloadMap& type_payloads) = 0;
+ virtual void OnNotificationStateChange(bool notifications_enabled) = 0;
+
+ // TODO(nileshagrawal): Find a way to hide state handling inside the
+ // sync notifier implementation.
+ virtual void StoreState(const std::string& state) = 0;
+};
+
+} // namespace sync_notifier
+
+#endif // CHROME_BROWSER_SYNC_NOTIFIER_SYNC_NOTIFIER_OBSERVER_H_
diff --git a/chrome/browser/sync/profile_sync_factory_impl.cc b/chrome/browser/sync/profile_sync_factory_impl.cc
index 6391aa5..9102e88 100644
--- a/chrome/browser/sync/profile_sync_factory_impl.cc
+++ b/chrome/browser/sync/profile_sync_factory_impl.cc
@@ -1,8 +1,9 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "base/command_line.h"
+#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/app_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_change_processor.h"
@@ -15,7 +16,6 @@
#include "chrome/browser/sync/glue/bookmark_data_type_controller.h"
#include "chrome/browser/sync/glue/bookmark_model_associator.h"
#include "chrome/browser/sync/glue/data_type_manager_impl.h"
-#include "chrome/browser/sync/glue/data_type_manager_impl2.h"
#include "chrome/browser/sync/glue/extension_change_processor.h"
#include "chrome/browser/sync/glue/extension_data_type_controller.h"
#include "chrome/browser/sync/glue/extension_model_associator.h"
@@ -54,7 +54,6 @@ using browser_sync::BookmarkModelAssociator;
using browser_sync::DataTypeController;
using browser_sync::DataTypeManager;
using browser_sync::DataTypeManagerImpl;
-using browser_sync::DataTypeManagerImpl2;
using browser_sync::ExtensionChangeProcessor;
using browser_sync::ExtensionDataTypeController;
using browser_sync::ExtensionModelAssociator;
@@ -116,13 +115,9 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService(
new ExtensionDataTypeController(this, profile_, pss));
}
-#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_CHROMEOS)
- if (command_line_->HasSwitch(switches::kEnableSyncPasswords)) {
-#else
// Password sync is enabled by default. Register unless explicitly
// disabled.
if (!command_line_->HasSwitch(switches::kDisableSyncPasswords)) {
-#endif
pss->RegisterDataTypeController(
new PasswordDataTypeController(this, profile_, pss));
}
@@ -131,7 +126,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService(
// disabled.
if (!command_line_->HasSwitch(switches::kDisableSyncPreferences)) {
pss->RegisterDataTypeController(
- new PreferenceDataTypeController(this, pss));
+ new PreferenceDataTypeController(this, profile_, pss));
}
// Theme sync is enabled by default. Register unless explicitly disabled.
@@ -151,7 +146,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService(
// enabled.
if (command_line_->HasSwitch(switches::kEnableSyncSessions)) {
pss->RegisterDataTypeController(
- new SessionDataTypeController(this, pss));
+ new SessionDataTypeController(this, profile_, pss));
}
if (!command_line_->HasSwitch(switches::kDisableSyncAutofillProfile)) {
@@ -164,10 +159,7 @@ ProfileSyncService* ProfileSyncFactoryImpl::CreateProfileSyncService(
DataTypeManager* ProfileSyncFactoryImpl::CreateDataTypeManager(
SyncBackendHost* backend,
const DataTypeController::TypeMap& controllers) {
- if (command_line_->HasSwitch(switches::kNewSyncerThread))
- return new DataTypeManagerImpl2(backend, controllers);
- else
- return new DataTypeManagerImpl(backend, controllers);
+ return new DataTypeManagerImpl(backend, controllers);
}
ProfileSyncFactory::SyncComponents
@@ -178,8 +170,11 @@ ProfileSyncFactoryImpl::CreateAppSyncComponents(
// For now we simply use extensions sync objects with the app sync
// traits. If apps become more than simply extensions, we may have
// to write our own apps model associator and/or change processor.
+ ExtensionServiceInterface* extension_service =
+ profile_sync_service->profile()->GetExtensionService();
+ sync_api::UserShare* user_share = profile_sync_service->GetUserShare();
ExtensionModelAssociator* model_associator =
- new ExtensionModelAssociator(traits, profile_sync_service);
+ new ExtensionModelAssociator(traits, extension_service, user_share);
ExtensionChangeProcessor* change_processor =
new ExtensionChangeProcessor(traits, error_handler);
return SyncComponents(model_associator, change_processor);
@@ -227,8 +222,12 @@ ProfileSyncFactory::SyncComponents
ProfileSyncFactoryImpl::CreateBookmarkSyncComponents(
ProfileSyncService* profile_sync_service,
UnrecoverableErrorHandler* error_handler) {
+ BookmarkModel* bookmark_model =
+ profile_sync_service->profile()->GetBookmarkModel();
+ sync_api::UserShare* user_share = profile_sync_service->GetUserShare();
BookmarkModelAssociator* model_associator =
- new BookmarkModelAssociator(profile_sync_service,
+ new BookmarkModelAssociator(bookmark_model,
+ user_share,
error_handler);
BookmarkChangeProcessor* change_processor =
new BookmarkChangeProcessor(model_associator,
@@ -242,8 +241,11 @@ ProfileSyncFactoryImpl::CreateExtensionSyncComponents(
UnrecoverableErrorHandler* error_handler) {
browser_sync::ExtensionSyncTraits traits =
browser_sync::GetExtensionSyncTraits();
+ ExtensionServiceInterface* extension_service =
+ profile_sync_service->profile()->GetExtensionService();
+ sync_api::UserShare* user_share = profile_sync_service->GetUserShare();
ExtensionModelAssociator* model_associator =
- new ExtensionModelAssociator(traits, profile_sync_service);
+ new ExtensionModelAssociator(traits, extension_service, user_share);
ExtensionChangeProcessor* change_processor =
new ExtensionChangeProcessor(traits, error_handler);
return SyncComponents(model_associator, change_processor);
diff --git a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc
index 30e6df0..907b2f6 100644
--- a/chrome/browser/sync/profile_sync_factory_impl_unittest.cc
+++ b/chrome/browser/sync/profile_sync_factory_impl_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -7,8 +7,8 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "base/command_line.h"
#include "base/file_path.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/profile_sync_service.h"
@@ -42,11 +42,7 @@ class ProfileSyncFactoryImplTest : public testing::Test {
datatypes.push_back(syncable::EXTENSIONS);
datatypes.push_back(syncable::APPS);
datatypes.push_back(syncable::AUTOFILL_PROFILE);
-// TODO(mdm): re-enable this test on Linux/BSD/etc. once we make password sync
-// compatible with GNOME Keyring.
-#if !defined(OS_POSIX) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
datatypes.push_back(syncable::PASSWORDS);
-#endif
return datatypes;
}
@@ -137,11 +133,7 @@ TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisableAutofillProfile) {
syncable::AUTOFILL_PROFILE);
}
-// TODO(mdm): re-enable this test on Linux/BSD/etc. once we make password sync
-// compatible with GNOME Keyring.
-#if !defined(OS_POSIX) || defined(OS_MACOSX) || defined(OS_CHROMEOS)
TEST_F(ProfileSyncFactoryImplTest, CreatePSSDisablePasswords) {
TestSwitchDisablesType(switches::kDisableSyncPasswords,
syncable::PASSWORDS);
}
-#endif
diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h
index a1d1a15..2d8d8e2 100644
--- a/chrome/browser/sync/profile_sync_factory_mock.h
+++ b/chrome/browser/sync/profile_sync_factory_mock.h
@@ -6,7 +6,7 @@
#define CHROME_BROWSER_SYNC_PROFILE_SYNC_FACTORY_MOCK_H__
#pragma once
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index 3815541..a75c1b8 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -4,28 +4,29 @@
#include "chrome/browser/sync/profile_sync_service.h"
+#include <stddef.h>
#include <map>
+#include <ostream>
#include <set>
+#include <utility>
#include "base/basictypes.h"
-#include "base/callback.h"
#include "base/command_line.h"
+#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
#include "base/metrics/histogram.h"
-#include "base/stl_util-inl.h"
#include "base/string16.h"
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
+#include "base/stringprintf.h"
#include "base/task.h"
#include "base/threading/thread_restrictions.h"
-#include "base/utf_string_conversions.h"
-#include "chrome/browser/browser_signin.h"
-#include "chrome/browser/history/history_types.h"
+#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/net/gaia/token_service.h"
-#include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h"
+#include "chrome/browser/sync/backend_migrator.h"
+#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/change_processor.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/glue/data_type_manager.h"
@@ -33,20 +34,19 @@
#include "chrome/browser/sync/js_arg_list.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/signin_manager.h"
-#include "chrome/browser/sync/token_migrator.h"
+#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_details.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/time_format.h"
#include "chrome/common/url_constants.h"
-#include "grit/chromium_strings.h"
+#include "content/common/notification_details.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
#include "grit/generated_resources.h"
-#include "jingle/notifier/communicator/const_communicator.h"
-#include "net/base/cookie_monster.h"
#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/native_widget_types.h"
using browser_sync::ChangeProcessor;
using browser_sync::DataTypeController;
@@ -68,8 +68,6 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
Profile* profile,
const std::string& cros_user)
: last_auth_error_(AuthError::None()),
- tried_creating_explicit_passphrase_(false),
- tried_setting_explicit_passphrase_(false),
observed_passphrase_required_(false),
passphrase_required_for_decryption_(false),
passphrase_migration_in_progress_(false),
@@ -82,10 +80,8 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
wizard_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
unrecoverable_error_detected_(false),
scoped_runnable_method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
- token_migrator_(NULL),
+ expect_sync_configuration_aborted_(false),
clear_server_data_state_(CLEAR_NOT_STARTED) {
- DCHECK(factory);
- DCHECK(profile);
registrar_.Add(this,
NotificationType::SYNC_DATA_TYPES_UPDATED,
Source<Profile>(profile));
@@ -110,19 +106,6 @@ ProfileSyncService::ProfileSyncService(ProfileSyncFactory* factory,
tried_implicit_gaia_remove_when_bug_62103_fixed_ = false;
}
-ProfileSyncService::ProfileSyncService()
- : last_auth_error_(AuthError::None()),
- factory_(NULL),
- profile_(NULL),
- sync_service_url_(kSyncServerUrl),
- backend_initialized_(false),
- is_auth_in_progress_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(wizard_(this)),
- unrecoverable_error_detected_(false),
- ALLOW_THIS_IN_INITIALIZER_LIST(scoped_runnable_method_factory_(this)),
- expect_sync_configuration_aborted_(false) {
-}
-
ProfileSyncService::~ProfileSyncService() {
Shutdown(false);
}
@@ -144,16 +127,6 @@ bool ProfileSyncService::AreCredentialsAvailable() {
return false;
}
-void ProfileSyncService::LoadMigratedCredentials(const std::string& username,
- const std::string& token) {
- signin_->SetUsername(username);
- profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, username);
- profile()->GetTokenService()->OnIssueAuthTokenSuccess(
- GaiaConstants::kSyncService, token);
- profile()->GetPrefs()->SetBoolean(prefs::kSyncCredentialsMigrated, true);
- token_migrator_.reset();
-}
-
void ProfileSyncService::Initialize() {
InitSettings();
RegisterPreferences();
@@ -195,18 +168,6 @@ void ProfileSyncService::Initialize() {
// Note that if we haven't finished setting up sync, backend bring up will
// be done by the wizard.
StartUp();
- } else {
- if (!cros_user_.empty()) {
- // We don't attempt migration on cros, as we should just get new
- // credentials from the login manager on startup.
- return;
- }
-
- // Try to migrate the tokens (if that hasn't already succeeded).
- if (!profile()->GetPrefs()->GetBoolean(prefs::kSyncCredentialsMigrated)) {
- token_migrator_.reset(new TokenMigrator(this, profile_->GetPath()));
- token_migrator_->TryMigration();
- }
}
}
@@ -262,32 +223,6 @@ void ProfileSyncService::GetDataTypeControllerStates(
(*state_map)[iter->first] = iter->second.get()->state();
}
-namespace {
-
-// TODO(akalin): Figure out whether this should be a method of
-// HostPortPair.
-net::HostPortPair StringToHostPortPair(const std::string& host_port_str,
- uint16 default_port) {
- std::string::size_type colon_index = host_port_str.find(':');
- if (colon_index == std::string::npos) {
- return net::HostPortPair(host_port_str, default_port);
- }
-
- std::string host = host_port_str.substr(0, colon_index);
- std::string port_str = host_port_str.substr(colon_index + 1);
- int port = default_port;
- if (!base::StringToInt(port_str, &port) ||
- (port <= 0) || (port > kuint16max)) {
- LOG(WARNING) << "Could not parse valid port from " << port_str
- << "; using port " << default_port;
- return net::HostPortPair(host, default_port);
- }
-
- return net::HostPortPair(host, port);
-}
-
-} // namespace
-
void ProfileSyncService::InitSettings() {
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
@@ -306,42 +241,6 @@ void ProfileSyncService::InitSettings() {
}
}
}
-
- // Override the notification server host from the command-line, if provided.
- if (command_line.HasSwitch(switches::kSyncNotificationHost)) {
- std::string value(command_line.GetSwitchValueASCII(
- switches::kSyncNotificationHost));
- if (!value.empty()) {
- notifier_options_.xmpp_host_port =
- StringToHostPortPair(value, notifier::kDefaultXmppPort);
- }
- VLOG(1) << "Using " << notifier_options_.xmpp_host_port.ToString()
- << " for test sync notification server.";
- }
-
- notifier_options_.try_ssltcp_first =
- command_line.HasSwitch(switches::kSyncTrySsltcpFirstForXmpp);
- if (notifier_options_.try_ssltcp_first)
- VLOG(1) << "Trying SSL/TCP port before XMPP port for notifications.";
-
- notifier_options_.invalidate_xmpp_login =
- command_line.HasSwitch(switches::kSyncInvalidateXmppLogin);
- if (notifier_options_.invalidate_xmpp_login) {
- VLOG(1) << "Invalidating sync XMPP login.";
- }
-
- notifier_options_.allow_insecure_connection =
- command_line.HasSwitch(switches::kSyncAllowInsecureXmppConnection);
- if (notifier_options_.allow_insecure_connection) {
- VLOG(1) << "Allowing insecure XMPP connections.";
- }
-
- if (command_line.HasSwitch(switches::kSyncNotificationMethod)) {
- const std::string notification_method_str(
- command_line.GetSwitchValueASCII(switches::kSyncNotificationMethod));
- notifier_options_.notification_method =
- notifier::StringToNotificationMethod(notification_method_str);
- }
}
void ProfileSyncService::RegisterPreferences() {
@@ -351,7 +250,6 @@ void ProfileSyncService::RegisterPreferences() {
pref_service->RegisterInt64Pref(prefs::kSyncLastSyncedTime, 0);
pref_service->RegisterBooleanPref(prefs::kSyncHasSetupCompleted, false);
pref_service->RegisterBooleanPref(prefs::kSyncSuppressStart, false);
- pref_service->RegisterBooleanPref(prefs::kSyncCredentialsMigrated, false);
// If you've never synced before, or if you're using Chrome OS, all datatypes
// are on by default.
@@ -424,8 +322,7 @@ void ProfileSyncService::InitializeBackend(bool delete_sync_data_folder) {
types,
profile_->GetRequestContext(),
credentials,
- delete_sync_data_folder,
- notifier_options_);
+ delete_sync_data_folder);
}
void ProfileSyncService::CreateBackend() {
@@ -521,10 +418,6 @@ void ProfileSyncService::SetSyncSetupCompleted() {
prefs->SetBoolean(prefs::kSyncHasSetupCompleted, true);
prefs->SetBoolean(prefs::kSyncSuppressStart, false);
- // Indicate that setup has been completed on the new credentials store
- // so that we don't try to migrate.
- prefs->SetBoolean(prefs::kSyncCredentialsMigrated, true);
-
prefs->ScheduleSavePersistentPrefs();
}
@@ -617,7 +510,7 @@ void ProfileSyncService::OnBackendInitialized() {
if (!cros_user_.empty()) {
if (profile_->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart)) {
- ShowConfigure(NULL);
+ ShowConfigure(NULL, true);
} else {
SetSyncSetupCompleted();
}
@@ -630,6 +523,7 @@ void ProfileSyncService::OnBackendInitialized() {
void ProfileSyncService::OnSyncCycleCompleted() {
UpdateLastSyncedTime();
+ VLOG(2) << "Notifying observers sync cycle completed";
NotifyObservers();
}
@@ -705,9 +599,25 @@ void ProfileSyncService::OnClearServerDataSucceeded() {
void ProfileSyncService::OnPassphraseRequired(bool for_decryption) {
DCHECK(backend_.get());
DCHECK(backend_->IsNigoriEnabled());
+
+ // TODO(lipalani) : add this check to other locations as well.
+ if (unrecoverable_error_detected_) {
+ // When unrecoverable error is detected we post a task to shutdown the
+ // backend. The task might not have executed yet.
+ return;
+ }
observed_passphrase_required_ = true;
passphrase_required_for_decryption_ = for_decryption;
+ // First try supplying gaia password as the passphrase.
+ if (!gaia_password_.empty()) {
+ SetPassphrase(gaia_password_, false, true);
+ gaia_password_ = std::string();
+ return;
+ }
+
+ // If the above failed then try the custom passphrase the user might have
+ // entered in setup.
if (!cached_passphrase_.value.empty()) {
SetPassphrase(cached_passphrase_.value,
cached_passphrase_.is_explicit,
@@ -727,11 +637,6 @@ void ProfileSyncService::OnPassphraseRequired(bool for_decryption) {
if (WizardIsVisible() && for_decryption) {
wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE);
- } else if (WizardIsVisible() && !for_decryption) {
- // The user is enabling an encrypted data type for the first
- // time, and we don't even have a default passphrase. We need
- // to refresh credentials and show the passphrase migration.
- SigninForPassphraseMigration(NULL);
}
NotifyObservers();
@@ -742,12 +647,13 @@ void ProfileSyncService::OnPassphraseAccepted() {
// this time.
syncable::ModelTypeSet types;
GetPreferredDataTypes(&types);
- data_type_manager_->Configure(types);
+ // Reset "passphrase_required" flag before configuring the DataTypeManager
+ // since we know we no longer require the passphrase.
+ observed_passphrase_required_ = false;
+ if (data_type_manager_.get())
+ data_type_manager_->Configure(types);
NotifyObservers();
- observed_passphrase_required_ = false;
- tried_setting_explicit_passphrase_ = false;
- tried_creating_explicit_passphrase_ = false;
wizard_.Step(SyncSetupWizard::DONE);
}
@@ -760,15 +666,17 @@ void ProfileSyncService::OnEncryptionComplete(
}
}
-void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
- if (!cros_user_.empty()) {
- // For ChromeOS, any login UI needs to be handled by the settings page.
- Browser* browser = BrowserList::GetLastActiveWithProfile(profile());
- if (browser)
- browser->ShowOptionsTab(chrome::kPersonalOptionsSubPage);
- return;
- }
+void ProfileSyncService::OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types) {
+ DCHECK(backend_initialized_);
+ DCHECK(data_type_manager_.get());
+
+ // Migrator must be valid, because we don't sync until it is created and this
+ // callback originates from a sync cycle.
+ migrator_->MigrateTypes(types);
+}
+void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
if (WizardIsVisible()) {
wizard_.Focus();
// Force the wizard to step to the login screen (which will only actually
@@ -783,7 +691,6 @@ void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
auth_error_time_ = base::TimeTicks(); // Reset auth_error_time_ to null.
}
- wizard_.SetParent(parent_window);
wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
NotifyObservers();
@@ -808,13 +715,17 @@ void ProfileSyncService::ShowErrorUI(gfx::NativeWindow parent_window) {
}
-void ProfileSyncService::ShowConfigure(gfx::NativeWindow parent_window) {
+void ProfileSyncService::ShowConfigure(
+ gfx::NativeWindow parent_window, bool sync_everything) {
if (WizardIsVisible()) {
wizard_.Focus();
return;
}
- wizard_.SetParent(parent_window);
- wizard_.Step(SyncSetupWizard::CONFIGURE);
+
+ if (sync_everything)
+ wizard_.Step(SyncSetupWizard::SYNC_EVERYTHING);
+ else
+ wizard_.Step(SyncSetupWizard::CONFIGURE);
}
void ProfileSyncService::PromptForExistingPassphrase(
@@ -823,7 +734,7 @@ void ProfileSyncService::PromptForExistingPassphrase(
wizard_.Focus();
return;
}
- wizard_.SetParent(parent_window);
+
wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE);
}
@@ -904,7 +815,7 @@ void ProfileSyncService::OnUserSubmittedAuth(
if (!signin_.get()) {
// In ChromeOS we sign in during login, so we do not instantiate signin_.
// If this function gets called, we need to re-authenticate (e.g. for
- // two factor signin), so instantiante signin_ here.
+ // two factor signin), so instantiate signin_ here.
signin_.reset(new SigninManager());
signin_->Initialize(profile_);
}
@@ -936,6 +847,7 @@ void ProfileSyncService::OnUserChoseDatatypes(bool sync_everything,
NOTREACHED();
return;
}
+
profile_->GetPrefs()->SetBoolean(prefs::kKeepEverythingSynced,
sync_everything);
@@ -950,7 +862,6 @@ void ProfileSyncService::OnUserCancelledDialog() {
expect_sync_configuration_aborted_ = true;
DisableForUser();
}
- wizard_.SetParent(NULL);
// Though an auth could still be in progress, once the dialog is closed we
// don't want the UI to stay stuck in the "waiting for authentication" state
@@ -1045,8 +956,9 @@ bool ProfileSyncService::IsUsingSecondaryPassphrase() const {
observed_passphrase_required_));
}
-bool ProfileSyncService::IsCryptographerReady() const {
- return backend_.get() && backend_->IsCryptographerReady();
+bool ProfileSyncService::IsCryptographerReady(
+ const sync_api::BaseTransaction* trans) const {
+ return backend_.get() && backend_->IsCryptographerReady(trans);
}
SyncBackendHost* ProfileSyncService::GetBackendForTest() {
@@ -1066,6 +978,10 @@ void ProfileSyncService::ConfigureDataTypeManager() {
registrar_.Add(this,
NotificationType::SYNC_CONFIGURE_DONE,
Source<DataTypeManager>(data_type_manager_.get()));
+
+ // We create the migrator at the same time.
+ migrator_.reset(
+ new browser_sync::BackendMigrator(this, data_type_manager_.get()));
}
syncable::ModelTypeSet types;
@@ -1078,6 +994,20 @@ void ProfileSyncService::ConfigureDataTypeManager() {
encrypted_types_.clear();
if (types.count(syncable::PASSWORDS) > 0)
encrypted_types_.insert(syncable::PASSWORDS);
+ if (observed_passphrase_required_ && passphrase_required_for_decryption_) {
+ if (IsEncryptedDatatypeEnabled()) {
+ // We need a passphrase still. Prompt the user for a passphrase, and
+ // DataTypeManager::Configure() will get called once the passphrase is
+ // accepted.
+ OnPassphraseRequired(true);
+ return;
+ } else {
+ // We've been informed that a passphrase is required for decryption, but
+ // now there are no encrypted data types enabled, so clear the flag
+ // (NotifyObservers() will be called when configuration completes).
+ observed_passphrase_required_ = false;
+ }
+ }
data_type_manager_->Configure(types);
}
@@ -1179,15 +1109,14 @@ void ProfileSyncService::SetPassphrase(const std::string& passphrase,
if (ShouldPushChanges() || observed_passphrase_required_) {
backend_->SetPassphrase(passphrase, is_explicit);
} else {
- cached_passphrase_.value = passphrase;
- cached_passphrase_.is_explicit = is_explicit;
- cached_passphrase_.is_creation = is_creation;
+ if (is_explicit) {
+ cached_passphrase_.value = passphrase;
+ cached_passphrase_.is_explicit = is_explicit;
+ cached_passphrase_.is_creation = is_creation;
+ } else {
+ gaia_password_ = passphrase;
+ }
}
-
- if (is_explicit && is_creation)
- tried_creating_explicit_passphrase_ = true;
- else if (is_explicit)
- tried_setting_explicit_passphrase_ = true;
}
void ProfileSyncService::EncryptDataTypes(
@@ -1210,18 +1139,27 @@ void ProfileSyncService::Observe(NotificationType type,
break;
}
case NotificationType::SYNC_CONFIGURE_DONE: {
- DataTypeManager::ConfigureResult result =
- *(Details<DataTypeManager::ConfigureResult>(details).ptr());
+ DataTypeManager::ConfigureResultWithErrorLocation* result_with_location =
+ Details<DataTypeManager::ConfigureResultWithErrorLocation>(
+ details).ptr();
+
+ DataTypeManager::ConfigureResult result = result_with_location->result;
if (result == DataTypeManager::ABORTED &&
expect_sync_configuration_aborted_) {
expect_sync_configuration_aborted_ = false;
return;
}
+ // Clear out the gaia password if it is already there.
+ gaia_password_ = std::string();
if (result != DataTypeManager::OK) {
- OnUnrecoverableError(FROM_HERE, "Sync Configuration failed.");
+ std::string message = StringPrintf("Sync Configuration failed with %d",
+ result);
+ OnUnrecoverableError(*(result_with_location->location), message);
+ cached_passphrase_ = CachedPassphrase();
return;
}
+ // If the user had entered a custom passphrase use it now.
if (!cached_passphrase_.value.empty()) {
// Don't hold on to the passphrase in raw form longer than needed.
SetPassphrase(cached_passphrase_.value,
@@ -1230,9 +1168,14 @@ void ProfileSyncService::Observe(NotificationType type,
cached_passphrase_ = CachedPassphrase();
}
+ // We should never get in a state where we have no encrypted datatypes
+ // enabled, and yet we still think we require a passphrase.
+ DCHECK(!(observed_passphrase_required_ &&
+ passphrase_required_for_decryption_ &&
+ !IsEncryptedDatatypeEnabled()));
+
// TODO(sync): Less wizard, more toast.
- if (!observed_passphrase_required_)
- wizard_.Step(SyncSetupWizard::DONE);
+ wizard_.Step(SyncSetupWizard::DONE);
NotifyObservers();
// In the old world, this would be a no-op. With new syncer thread,
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index f0f057e..412d165 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,40 +10,50 @@
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
-#include "base/scoped_ptr.h"
#include "base/string16.h"
+#include "base/task.h"
#include "base/time.h"
#include "base/timer.h"
+#include "base/tracked.h"
#include "chrome/browser/prefs/pref_member.h"
-#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/browser/sync/glue/data_type_manager.h"
-#include "chrome/browser/sync/glue/session_model_associator.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/js_event_handler_list.h"
#include "chrome/browser/sync/profile_sync_service_observer.h"
-#include "chrome/browser/sync/signin_manager.h"
#include "chrome/browser/sync/sync_setup_wizard.h"
+#include "chrome/browser/sync/syncable/autofill_migration.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/unrecoverable_error_handler.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_type.h"
#include "googleurl/src/gurl.h"
-#include "jingle/notifier/base/notifier_options.h"
+#include "ui/gfx/native_widget_types.h"
class NotificationDetails;
class NotificationSource;
-class NotificationType;
class Profile;
class ProfileSyncFactory;
-class TabContents;
-class TokenMigrator;
+class SigninManager;
namespace browser_sync {
+class BackendMigrator;
+class ChangeProcessor;
+class DataTypeManager;
class JsFrontend;
-} // namespace browser_sync
+class SessionModelAssociator;
+namespace sessions { struct SyncSessionSnapshot; }
+}
+
+namespace sync_api {
+class BaseTransaction;
+struct SyncCredentials;
+struct UserShare;
+}
// ProfileSyncService is the layer between browser subsystems like bookmarks,
// and the sync backend. Each subsystem is logically thought of as being
@@ -145,10 +155,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// available for the backend to start up.
bool AreCredentialsAvailable();
- // Loads credentials migrated from the old user settings db.
- void LoadMigratedCredentials(const std::string& username,
- const std::string& token);
-
// Registers a data type controller with the sync service. This
// makes the data type controller available for use, it does not
// enable or activate the synchronization of the data type (see
@@ -192,6 +198,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
virtual void OnPassphraseAccepted();
virtual void OnEncryptionComplete(
const syncable::ModelTypeSet& encrypted_types);
+ virtual void OnMigrationNeededForTypes(
+ const syncable::ModelTypeSet& types);
// Called when a user enters credentials through UI.
virtual void OnUserSubmittedAuth(const std::string& username,
@@ -232,12 +240,18 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
return wizard_.IsVisible();
}
virtual void ShowLoginDialog(gfx::NativeWindow parent_window);
+ SyncSetupWizard& get_wizard() { return wizard_; }
// This method handles clicks on "sync error" UI, showing the appropriate
// dialog for the error condition (relogin / enter passphrase).
virtual void ShowErrorUI(gfx::NativeWindow parent_window);
- void ShowConfigure(gfx::NativeWindow parent_window);
+ // Shows the configure screen of the Sync setup wizard. If |sync_everything|
+ // is true, shows the corresponding page in the customize screen; otherwise,
+ // displays the page that gives the user the ability to select which data
+ // types to sync.
+ void ShowConfigure(gfx::NativeWindow parent_window, bool sync_everything);
+
void PromptForExistingPassphrase(gfx::NativeWindow parent_window);
void SigninForPassphraseMigration(gfx::NativeWindow parent_window);
@@ -267,14 +281,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
return is_auth_in_progress_;
}
- bool tried_creating_explicit_passphrase() const {
- return tried_creating_explicit_passphrase_;
- }
-
- bool tried_setting_explicit_passphrase() const {
- return tried_setting_explicit_passphrase_;
- }
-
bool observed_passphrase_required() const {
return observed_passphrase_required_;
}
@@ -283,13 +289,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
return passphrase_required_for_decryption_;
}
- // A timestamp marking the last time the service observed a transition from
- // the SYNCING state to the READY state. Note that this does not reflect the
- // last time we polled the server to see if there were any changes; the
- // timestamp is only snapped when syncing takes place and we download or
- // upload some bookmark entity.
- const base::Time& last_synced_time() const { return last_synced_time_; }
-
// Returns a user-friendly string form of last synced time (in minutes).
virtual string16 GetLastSyncedTimeString() const;
@@ -348,7 +347,7 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// ProfileSyncServiceHarness. Figure out a different way to expose
// this info to that class, and remove these functions.
- const browser_sync::sessions::SyncSessionSnapshot*
+ virtual const browser_sync::sessions::SyncSessionSnapshot*
GetLastSessionSnapshot() const;
// Returns whether or not the underlying sync engine has made any
@@ -415,8 +414,10 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
syncable::ModelTypeSet* registered_types) const;
// Checks whether the Cryptographer is ready to encrypt and decrypt updates
- // for sensitive data types.
- virtual bool IsCryptographerReady() const;
+ // for sensitive data types. Caller must be holding a
+ // syncapi::BaseTransaction to ensure thread safety.
+ virtual bool IsCryptographerReady(
+ const sync_api::BaseTransaction* trans) const;
// Returns true if a secondary passphrase is being used.
virtual bool IsUsingSecondaryPassphrase() const;
@@ -458,13 +459,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
const std::string& cros_user() const { return cros_user_; }
protected:
- // Used by ProfileSyncServiceMock only.
- //
- // TODO(akalin): Separate this class out into an abstract
- // ProfileSyncService interface and a ProfileSyncServiceImpl class
- // so we don't need this hack anymore.
- ProfileSyncService();
-
// Used by test classes that derive from ProfileSyncService.
virtual browser_sync::SyncBackendHost* GetBackendForTest();
@@ -510,14 +504,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// Cache of the last name the client attempted to authenticate.
std::string last_attempted_user_email_;
- // Whether the user has tried creating an explicit passphrase on this
- // machine.
- bool tried_creating_explicit_passphrase_;
-
- // Whether the user has tried setting an explicit passphrase on this
- // machine.
- bool tried_setting_explicit_passphrase_;
-
// Whether we have seen a SYNC_PASSPHRASE_REQUIRED since initializing the
// backend, telling us that it is safe to send a passphrase down ASAP.
bool observed_passphrase_required_;
@@ -597,10 +583,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
std::string unrecoverable_error_message_;
scoped_ptr<tracked_objects::Location> unrecoverable_error_location_;
- // Contains options specific to how sync clients send and listen to
- // notifications.
- notifier::NotifierOptions notifier_options_;
-
// Manages the start and stop of the various data types.
scoped_ptr<browser_sync::DataTypeManager> data_type_manager_;
@@ -622,8 +604,6 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// desist syncing immediately.
bool expect_sync_configuration_aborted_;
- scoped_ptr<TokenMigrator> token_migrator_;
-
// Sometimes we need to temporarily hold on to a passphrase because we don't
// yet have a backend to send it to. This happens during initialization as
// we don't StartUp until we have a valid token, which happens after valid
@@ -636,6 +616,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
};
CachedPassphrase cached_passphrase_;
+ // TODO(lipalani): Bug 82221 unify this with the CachedPassphrase struct.
+ std::string gaia_password_;
+
// TODO(tim): Remove this once new 'explicit passphrase' code flushes through
// dev channel. See bug 62103.
// To "migrate" early adopters of password sync on dev channel to the new
@@ -659,6 +642,8 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// encrypted through the OnEncryptionComplete callback of SyncFrontend.
syncable::ModelTypeSet encrypted_types_;
+ scoped_ptr<browser_sync::BackendMigrator> migrator_;
+
DISALLOW_COPY_AND_ASSIGN(ProfileSyncService);
};
diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
index 64753fb..2b13528 100644
--- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,14 +9,14 @@
#include "testing/gtest/include/gtest/gtest.h"
#include "base/callback.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
#include "base/string16.h"
+#include "base/synchronization/waitable_event.h"
#include "base/task.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
-#include "base/synchronization/waitable_event.h"
#include "chrome/browser/autofill/autofill_common_test.h"
#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
#include "chrome/browser/sync/engine/model_changing_syncer_command.h"
@@ -34,18 +34,19 @@
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
#include "chrome/browser/sync/syncable/autofill_migration.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
-#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
#include "chrome/browser/webdata/autofill_change.h"
#include "chrome/browser/webdata/autofill_entry.h"
+#include "chrome/browser/webdata/autofill_table.h"
#include "chrome/browser/webdata/web_database.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
#include "chrome/test/profile_mock.h"
#include "chrome/test/sync/engine/test_id_factory.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
using base::Time;
@@ -65,7 +66,6 @@ using syncable::CREATE_NEW_UPDATE_ITEM;
using syncable::AUTOFILL;
using syncable::BASE_VERSION;
using syncable::CREATE;
-using syncable::DirectoryChangeEvent;
using syncable::GET_BY_SERVER_TAG;
using syncable::INVALID;
using syncable::MutableEntry;
@@ -91,8 +91,9 @@ namespace syncable {
class Id;
}
-class WebDatabaseMock : public WebDatabase {
+class AutofillTableMock : public AutofillTable {
public:
+ AutofillTableMock() : AutofillTable(NULL, NULL) {}
MOCK_METHOD2(RemoveFormElement,
bool(const string16& name, const string16& value)); // NOLINT
MOCK_METHOD1(GetAllAutofillEntries,
@@ -103,19 +104,33 @@ class WebDatabaseMock : public WebDatabase {
std::vector<base::Time>* timestamps));
MOCK_METHOD1(UpdateAutofillEntries,
bool(const std::vector<AutofillEntry>&)); // NOLINT
- MOCK_METHOD1(GetAutoFillProfiles,
- bool(std::vector<AutoFillProfile*>*)); // NOLINT
- MOCK_METHOD1(UpdateAutoFillProfile,
- bool(const AutoFillProfile&)); // NOLINT
- MOCK_METHOD1(AddAutoFillProfile,
- bool(const AutoFillProfile&)); // NOLINT
- MOCK_METHOD1(RemoveAutoFillProfile,
+ MOCK_METHOD1(GetAutofillProfiles,
+ bool(std::vector<AutofillProfile*>*)); // NOLINT
+ MOCK_METHOD1(UpdateAutofillProfile,
+ bool(const AutofillProfile&)); // NOLINT
+ MOCK_METHOD1(AddAutofillProfile,
+ bool(const AutofillProfile&)); // NOLINT
+ MOCK_METHOD1(RemoveAutofillProfile,
bool(const std::string&)); // NOLINT
};
+class WebDatabaseFake : public WebDatabase {
+ public:
+ explicit WebDatabaseFake(AutofillTable* autofill_table)
+ : autofill_table_(autofill_table) {}
+
+ virtual AutofillTable* GetAutofillTable() {
+ return autofill_table_;
+ }
+
+ private:
+ AutofillTable* autofill_table_;
+};
+
+
class ProfileSyncServiceAutofillTest;
-template<class AutoFillProfile>
+template<class AutofillProfile>
syncable::ModelType GetModelType() {
return syncable::UNSPECIFIED;
}
@@ -126,7 +141,7 @@ syncable::ModelType GetModelType<AutofillEntry>() {
}
template<>
-syncable::ModelType GetModelType<AutoFillProfile>() {
+syncable::ModelType GetModelType<AutofillProfile>() {
return syncable::AUTOFILL_PROFILE;
}
@@ -214,7 +229,7 @@ class AutofillProfileFactory : public AbstractAutofillFactory {
return new AutofillProfileDataTypeController(factory,
profile,
service);
- }
+ }
void SetExpectation(ProfileSyncFactoryMock* factory,
ProfileSyncService* service,
@@ -237,8 +252,7 @@ template <class T> class AddAutofillTask;
class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
protected:
- ProfileSyncServiceAutofillTest() : db_thread_(BrowserThread::DB) {
- }
+ ProfileSyncServiceAutofillTest() : db_thread_(BrowserThread::DB) {}
AutofillProfileFactory profile_factory_;
AutofillEntryFactory entry_factory_;
@@ -254,7 +268,9 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
}
}
virtual void SetUp() {
- web_data_service_ = new WebDataServiceFake(&web_database_);
+ profile_.CreateRequestContext();
+ web_database_.reset(new WebDatabaseFake(&autofill_table_));
+ web_data_service_ = new WebDataServiceFake(web_database_.get());
personal_data_manager_ = new PersonalDataManagerMock();
EXPECT_CALL(*personal_data_manager_, LoadProfiles()).Times(1);
EXPECT_CALL(*personal_data_manager_, LoadCreditCards()).Times(1);
@@ -269,6 +285,12 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
service_.reset();
notification_service_->TearDown();
db_thread_.Stop();
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile_.ResetRequestContext();
+ }
MessageLoop::current()->RunAllPending();
}
@@ -283,14 +305,14 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
factory->CreateDataTypeController(&factory_,
&profile_,
service_.get());
- SyncBackendHostForProfileSyncTest::
- SetDefaultExpectationsForWorkerCreation(&profile_);
+ SyncBackendHostForProfileSyncTest::
+ SetDefaultExpectationsForWorkerCreation(&profile_);
- factory->SetExpectation(&factory_,
- service_.get(),
- &web_database_,
- personal_data_manager_.get(),
- data_type_controller);
+ factory->SetExpectation(&factory_,
+ service_.get(),
+ web_database_.get(),
+ personal_data_manager_.get(),
+ data_type_controller);
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
WillOnce(ReturnNewDataTypeManager());
@@ -311,7 +333,6 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
EXPECT_CALL(profile_, GetTokenService()).
WillRepeatedly(Return(&token_service_));
- service_->set_num_expected_resumes(will_fail_association ? 0 : 1);
service_->RegisterDataTypeController(data_type_controller);
service_->Initialize();
MessageLoop::current()->Run();
@@ -333,7 +354,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
return true;
}
- bool AddAutofillSyncNode(const AutoFillProfile& profile) {
+ bool AddAutofillSyncNode(const AutofillProfile& profile) {
sync_api::WriteTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag))
@@ -348,7 +369,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
}
bool GetAutofillEntriesFromSyncDB(std::vector<AutofillEntry>* entries,
- std::vector<AutoFillProfile>* profiles) {
+ std::vector<AutofillProfile>* profiles) {
sync_api::ReadTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag))
@@ -373,7 +394,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
}
entries->push_back(AutofillEntry(key, timestamps));
} else if (autofill.has_profile()) {
- AutoFillProfile p;
+ AutofillProfile p;
p.set_guid(autofill.profile().guid());
AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
autofill.profile());
@@ -385,7 +406,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
}
bool GetAutofillProfilesFromSyncDBUnderProfileNode(
- std::vector<AutoFillProfile>* profiles) {
+ std::vector<AutofillProfile>* profiles) {
sync_api::ReadTransaction trans(service_->GetUserShare());
sync_api::ReadNode autofill_root(&trans);
if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag))
@@ -399,7 +420,7 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
const sync_pb::AutofillProfileSpecifics& autofill(
child_node.GetAutofillProfileSpecifics());
- AutoFillProfile p;
+ AutofillProfile p;
p.set_guid(autofill.guid());
AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
autofill);
@@ -410,9 +431,9 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
}
void SetIdleChangeProcessorExpectations() {
- EXPECT_CALL(web_database_, RemoveFormElement(_, _)).Times(0);
- EXPECT_CALL(web_database_, GetAutofillTimestamps(_, _, _)).Times(0);
- EXPECT_CALL(web_database_, UpdateAutofillEntries(_)).Times(0);
+ EXPECT_CALL(autofill_table_, RemoveFormElement(_, _)).Times(0);
+ EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).Times(0);
+ EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)).Times(0);
}
static AutofillEntry MakeAutofillEntry(const char* name,
@@ -435,14 +456,15 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
}
friend class AddAutofillTask<AutofillEntry>;
- friend class AddAutofillTask<AutoFillProfile>;
+ friend class AddAutofillTask<AutofillProfile>;
friend class FakeServerUpdater;
BrowserThread db_thread_;
scoped_refptr<ThreadNotificationService> notification_service_;
ProfileMock profile_;
- WebDatabaseMock web_database_;
+ AutofillTableMock autofill_table_;
+ scoped_ptr<WebDatabaseFake> web_database_;
scoped_refptr<WebDataService> web_data_service_;
scoped_refptr<PersonalDataManagerMock> personal_data_manager_;
};
@@ -483,12 +505,12 @@ class WriteTransactionTest: public WriteTransaction {
: WriteTransaction(directory, writer, source_file, line),
wait_for_syncapi_(wait_for_syncapi) { }
- virtual void NotifyTransactionComplete() {
+ virtual void NotifyTransactionComplete(syncable::ModelTypeBitSet types) {
// This is where we differ. Force a thread change here, giving another
// thread a chance to create a WriteTransaction
(*wait_for_syncapi_)->Wait();
- WriteTransaction::NotifyTransactionComplete();
+ WriteTransaction::NotifyTransactionComplete(types);
}
private:
@@ -613,15 +635,15 @@ TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) {
}
TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) {
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
EXPECT_EQ(0U, sync_entries.size());
EXPECT_EQ(0U, sync_profiles.size());
@@ -630,16 +652,16 @@ TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) {
TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) {
std::vector<AutofillEntry> entries;
entries.push_back(MakeAutofillEntry("foo", "bar", 1));
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
ASSERT_EQ(1U, entries.size());
EXPECT_TRUE(entries[0] == sync_entries[0]);
@@ -648,10 +670,10 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) {
TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) {
- std::vector<AutoFillProfile*> profiles;
- std::vector<AutoFillProfile> expected_profiles;
- // Owned by GetAutoFillProfiles caller.
- AutoFillProfile* profile0 = new AutoFillProfile;
+ std::vector<AutofillProfile*> profiles;
+ std::vector<AutofillProfile> expected_profiles;
+ // Owned by GetAutofillProfiles caller.
+ AutofillProfile* profile0 = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(profile0,
"54B3F9AA-335E-4F71-A27D-719C41564230", "Billing",
"Mitchell", "Morrison",
@@ -659,14 +681,14 @@ TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) {
"91601", "US", "12345678910", "01987654321");
profiles.push_back(profile0);
expected_profiles.push_back(*profile0);
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true)));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL_PROFILE);
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles));
EXPECT_EQ(1U, sync_profiles.size());
EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0]));
@@ -679,16 +701,16 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) {
entries.push_back(MakeAutofillEntry("foo", "bar", 1));
entries.push_back(MakeAutofillEntry("dup", "", 2));
entries.push_back(MakeAutofillEntry("dup", "", 3));
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
EXPECT_EQ(2U, sync_entries.size());
}
@@ -700,17 +722,17 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) {
std::vector<AutofillEntry> native_entries;
native_entries.push_back(native_entry);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
std::vector<AutofillEntry> sync_entries;
sync_entries.push_back(sync_entry);
AddAutofillTask<AutofillEntry> task(this, sync_entries);
- EXPECT_CALL(web_database_, UpdateAutofillEntries(ElementsAre(sync_entry))).
+ EXPECT_CALL(autofill_table_, UpdateAutofillEntries(ElementsAre(sync_entry))).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
@@ -722,7 +744,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) {
expected_entries.insert(sync_entry);
std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
std::set<AutofillEntry> new_sync_entries_set(new_sync_entries.begin(),
@@ -738,22 +760,22 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) {
std::vector<AutofillEntry> native_entries;
native_entries.push_back(native_entry);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
std::vector<AutofillEntry> sync_entries;
sync_entries.push_back(sync_entry);
AddAutofillTask<AutofillEntry> task(this, sync_entries);
- EXPECT_CALL(web_database_, UpdateAutofillEntries(ElementsAre(merged_entry))).
- WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_,
+ UpdateAutofillEntries(ElementsAre(merged_entry))).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_entries.size());
@@ -761,35 +783,35 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) {
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) {
- AutoFillProfile sync_profile;
+ AutofillProfile sync_profile;
autofill_test::SetProfileInfoWithGuid(&sync_profile,
"23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
"Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
- AutoFillProfile* native_profile = new AutoFillProfile;
+ AutofillProfile* native_profile = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(native_profile,
"23355099-1170-4B71-8ED4-144470CC9EBE", "Billing", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
- std::vector<AutoFillProfile*> native_profiles;
+ std::vector<AutofillProfile*> native_profiles;
native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
- AddAutofillTask<AutoFillProfile> task(this, sync_profiles);
+ AddAutofillTask<AutofillProfile> task(this, sync_profiles);
- EXPECT_CALL(web_database_, UpdateAutoFillProfile(_)).
+ EXPECT_CALL(autofill_table_, UpdateAutofillProfile(_)).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
@@ -797,7 +819,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) {
}
TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) {
- AutoFillProfile sync_profile;
+ AutofillProfile sync_profile;
autofill_test::SetProfileInfoWithGuid(&sync_profile,
"23355099-1170-4B71-8ED4-144470CC9EBE", "Billing",
@@ -806,31 +828,31 @@ TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) {
"91601", "US", "12345678910", "01987654321");
std::string native_guid = "EDC609ED-7EEE-4F27-B00C-423242A9C44B";
- AutoFillProfile* native_profile = new AutoFillProfile;
+ AutofillProfile* native_profile = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(native_profile,
native_guid.c_str(), "Billing",
"Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
- std::vector<AutoFillProfile*> native_profiles;
+ std::vector<AutofillProfile*> native_profiles;
native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
- AddAutofillTask<AutoFillProfile> task(this, sync_profiles);
+ AddAutofillTask<AutofillProfile> task(this, sync_profiles);
- EXPECT_CALL(web_database_, AddAutoFillProfile(_)).
+ EXPECT_CALL(autofill_table_, AddAutofillProfile(_)).
WillOnce(Return(true));
- EXPECT_CALL(web_database_, RemoveAutoFillProfile(native_guid)).
+ EXPECT_CALL(autofill_table_, RemoveAutofillProfile(native_guid)).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
@@ -839,8 +861,8 @@ TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) {
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
@@ -850,7 +872,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1));
std::vector<base::Time> timestamps(added_entry.timestamps());
- EXPECT_CALL(web_database_, GetAutofillTimestamps(_, _, _)).
+ EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).
WillOnce(DoAll(SetArgumentPointee<2>(timestamps), Return(true)));
AutofillChangeList changes;
@@ -861,7 +883,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
Details<AutofillChangeList>(&changes));
std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_entries.size());
@@ -869,14 +891,14 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) {
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL_PROFILE);
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- AutoFillProfile added_profile;
+ AutofillProfile added_profile;
autofill_test::SetProfileInfoWithGuid(&added_profile,
"D6ADA912-D374-4C0A-917D-F5C8EBE43011", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
@@ -889,7 +911,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) {
Source<WebDataService>(web_data_service_.get()),
Details<AutofillProfileChange>(&change));
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
@@ -901,9 +923,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
std::vector<AutofillEntry> original_entries;
original_entries.push_back(original_entry);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
@@ -912,7 +934,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2));
std::vector<base::Time> timestamps(updated_entry.timestamps());
- EXPECT_CALL(web_database_, GetAutofillTimestamps(_, _, _)).
+ EXPECT_CALL(autofill_table_, GetAutofillTimestamps(_, _, _)).
WillOnce(DoAll(SetArgumentPointee<2>(timestamps), Return(true)));
AutofillChangeList changes;
@@ -924,7 +946,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
Details<AutofillChangeList>(&changes));
std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(1U, new_sync_entries.size());
@@ -937,9 +959,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) {
std::vector<AutofillEntry> original_entries;
original_entries.push_back(original_entry);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(original_entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
@@ -954,32 +976,32 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) {
Details<AutofillChangeList>(&changes));
std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
&new_sync_profiles));
ASSERT_EQ(0U, new_sync_entries.size());
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) {
- AutoFillProfile sync_profile;
+ AutofillProfile sync_profile;
autofill_test::SetProfileInfoWithGuid(&sync_profile,
"3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
- AutoFillProfile* native_profile = new AutoFillProfile;
+ AutofillProfile* native_profile = new AutofillProfile;
autofill_test::SetProfileInfoWithGuid(native_profile,
"3BA5FA1B-1EC4-4BB3-9B57-EC92BE3C1A09", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
- std::vector<AutoFillProfile*> native_profiles;
+ std::vector<AutofillProfile*> native_profiles;
native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
- AddAutofillTask<AutoFillProfile> task(this, sync_profiles);
+ AddAutofillTask<AutofillProfile> task(this, sync_profiles);
EXPECT_CALL(*personal_data_manager_, Refresh());
StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
@@ -991,15 +1013,15 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) {
Source<WebDataService>(web_data_service_.get()),
Details<AutofillProfileChange>(&change));
- std::vector<AutoFillProfile> new_sync_profiles;
+ std::vector<AutofillProfile> new_sync_profiles;
ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
&new_sync_profiles));
ASSERT_EQ(0U, new_sync_profiles.size());
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) {
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
StartSyncService(&task, false, syncable::AUTOFILL);
@@ -1033,9 +1055,9 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) {
// Crashy, http://crbug.com/57884
TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) {
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
- EXPECT_CALL(web_database_, UpdateAutofillEntries(_)).
+ EXPECT_CALL(autofill_table_, GetAllAutofillEntries(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, GetAutofillProfiles(_)).WillOnce(Return(true));
+ EXPECT_CALL(autofill_table_, UpdateAutofillEntries(_)).
WillRepeatedly(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh()).Times(3);
CreateRootTask task(this, syncable::AUTOFILL);
@@ -1065,7 +1087,7 @@ TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) {
updater->CreateNewEntryAndWait(MakeAutofillEntry("server2", "entry2", 3));
std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
+ std::vector<AutofillProfile> sync_profiles;
ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
EXPECT_EQ(3U, sync_entries.size());
EXPECT_EQ(0U, sync_profiles.size());
diff --git a/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc b/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc
new file mode 100644
index 0000000..ee82421
--- /dev/null
+++ b/chrome/browser/sync/profile_sync_service_bookmark_unittest.cc
@@ -0,0 +1,1362 @@
+// Copyright (c) 2011 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.
+
+// TODO(akalin): This file is basically just a unit test for
+// BookmarkChangeProcessor. Write unit tests for
+// BookmarkModelAssociator separately.
+
+#include <stack>
+#include <vector>
+
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "base/string16.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
+#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/bookmark_change_processor.h"
+#include "chrome/browser/sync/glue/bookmark_model_associator.h"
+#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/test/sync/engine/test_id_factory.h"
+#include "chrome/test/sync/engine/test_user_share.h"
+#include "chrome/test/testing_profile.h"
+#include "content/browser/browser_thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+namespace {
+
+using testing::_;
+using testing::InvokeWithoutArgs;
+using testing::Mock;
+using testing::StrictMock;
+
+class TestBookmarkModelAssociator : public BookmarkModelAssociator {
+ public:
+ TestBookmarkModelAssociator(
+ BookmarkModel* bookmark_model,
+ sync_api::UserShare* user_share,
+ UnrecoverableErrorHandler* unrecoverable_error_handler)
+ : BookmarkModelAssociator(bookmark_model, user_share,
+ unrecoverable_error_handler),
+ user_share_(user_share) {}
+
+ // TODO(akalin): This logic lazily creates any tagged node that is
+ // requested. A better way would be to have utility functions to
+ // create sync nodes from some bookmark structure and to use that.
+ virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id) {
+ std::wstring tag_wide;
+ if (!UTF8ToWide(tag.c_str(), tag.length(), &tag_wide)) {
+ NOTREACHED() << "Unable to convert UTF8 to wide for string: " << tag;
+ return false;
+ }
+
+ bool root_exists = false;
+ syncable::ModelType type = model_type();
+ {
+ sync_api::WriteTransaction trans(user_share_);
+ sync_api::ReadNode uber_root(&trans);
+ uber_root.InitByRootLookup();
+
+ sync_api::ReadNode root(&trans);
+ root_exists = root.InitByTagLookup(
+ ProfileSyncServiceTestHelper::GetTagForType(type));
+ }
+
+ if (!root_exists) {
+ bool created = ProfileSyncServiceTestHelper::CreateRoot(
+ type,
+ user_share_,
+ &id_factory_);
+ if (!created)
+ return false;
+ }
+
+ sync_api::WriteTransaction trans(user_share_);
+ sync_api::ReadNode root(&trans);
+ EXPECT_TRUE(root.InitByTagLookup(
+ ProfileSyncServiceTestHelper::GetTagForType(type)));
+
+ // First, try to find a node with the title among the root's children.
+ // This will be the case if we are testing model persistence, and
+ // are reloading a sync repository created earlier in the test.
+ int64 last_child_id = sync_api::kInvalidId;
+ for (int64 id = root.GetFirstChildId(); id != sync_api::kInvalidId; /***/) {
+ sync_api::ReadNode child(&trans);
+ child.InitByIdLookup(id);
+ last_child_id = id;
+ if (tag_wide == child.GetTitle()) {
+ *sync_id = id;
+ return true;
+ }
+ id = child.GetSuccessorId();
+ }
+
+ sync_api::ReadNode predecessor_node(&trans);
+ sync_api::ReadNode* predecessor = NULL;
+ if (last_child_id != sync_api::kInvalidId) {
+ predecessor_node.InitByIdLookup(last_child_id);
+ predecessor = &predecessor_node;
+ }
+ sync_api::WriteNode node(&trans);
+ // Create new fake tagged nodes at the end of the ordering.
+ node.InitByCreation(type, root, predecessor);
+ node.SetIsFolder(true);
+ node.SetTitle(tag_wide);
+ node.SetExternalId(0);
+ *sync_id = node.GetId();
+ return true;
+ }
+
+ private:
+ sync_api::UserShare* user_share_;
+ browser_sync::TestIdFactory id_factory_;
+};
+
+// FakeServerChange constructs a list of sync_api::ChangeRecords while modifying
+// the sync model, and can pass the ChangeRecord list to a
+// sync_api::SyncObserver (i.e., the ProfileSyncService) to test the client
+// change-application behavior.
+// Tests using FakeServerChange should be careful to avoid back-references,
+// since FakeServerChange will send the edits in the order specified.
+class FakeServerChange {
+ public:
+ explicit FakeServerChange(sync_api::WriteTransaction* trans) : trans_(trans) {
+ }
+
+ // Pretend that the server told the syncer to add a bookmark object.
+ int64 Add(const std::wstring& title,
+ const std::string& url,
+ bool is_folder,
+ int64 parent_id,
+ int64 predecessor_id) {
+ sync_api::ReadNode parent(trans_);
+ EXPECT_TRUE(parent.InitByIdLookup(parent_id));
+ sync_api::WriteNode node(trans_);
+ if (predecessor_id == 0) {
+ EXPECT_TRUE(node.InitByCreation(syncable::BOOKMARKS, parent, NULL));
+ } else {
+ sync_api::ReadNode predecessor(trans_);
+ EXPECT_TRUE(predecessor.InitByIdLookup(predecessor_id));
+ EXPECT_EQ(predecessor.GetParentId(), parent.GetId());
+ EXPECT_TRUE(node.InitByCreation(syncable::BOOKMARKS, parent,
+ &predecessor));
+ }
+ EXPECT_EQ(node.GetPredecessorId(), predecessor_id);
+ EXPECT_EQ(node.GetParentId(), parent_id);
+ node.SetIsFolder(is_folder);
+ node.SetTitle(title);
+ if (!is_folder)
+ node.SetURL(GURL(url));
+ sync_api::SyncManager::ChangeRecord record;
+ record.action = sync_api::SyncManager::ChangeRecord::ACTION_ADD;
+ record.id = node.GetId();
+ changes_.push_back(record);
+ return node.GetId();
+ }
+
+ // Add a bookmark folder.
+ int64 AddFolder(const std::wstring& title,
+ int64 parent_id,
+ int64 predecessor_id) {
+ return Add(title, std::string(), true, parent_id, predecessor_id);
+ }
+
+ // Add a bookmark.
+ int64 AddURL(const std::wstring& title,
+ const std::string& url,
+ int64 parent_id,
+ int64 predecessor_id) {
+ return Add(title, url, false, parent_id, predecessor_id);
+ }
+
+ // Pretend that the server told the syncer to delete an object.
+ void Delete(int64 id) {
+ {
+ // Delete the sync node.
+ sync_api::WriteNode node(trans_);
+ EXPECT_TRUE(node.InitByIdLookup(id));
+ EXPECT_FALSE(node.GetFirstChildId());
+ node.Remove();
+ }
+ {
+ // Verify the deletion.
+ sync_api::ReadNode node(trans_);
+ EXPECT_FALSE(node.InitByIdLookup(id));
+ }
+
+ sync_api::SyncManager::ChangeRecord record;
+ record.action = sync_api::SyncManager::ChangeRecord::ACTION_DELETE;
+ record.id = id;
+ // Deletions are always first in the changelist, but we can't actually do
+ // WriteNode::Remove() on the node until its children are moved. So, as
+ // a practical matter, users of FakeServerChange must move or delete
+ // children before parents. Thus, we must insert the deletion record
+ // at the front of the vector.
+ changes_.insert(changes_.begin(), record);
+ }
+
+ // Set a new title value, and return the old value.
+ std::wstring ModifyTitle(int64 id, const std::wstring& new_title) {
+ sync_api::WriteNode node(trans_);
+ EXPECT_TRUE(node.InitByIdLookup(id));
+ std::wstring old_title = node.GetTitle();
+ node.SetTitle(new_title);
+ SetModified(id);
+ return old_title;
+ }
+
+ // Set a new parent and predecessor value. Return the old parent id.
+ // We could return the old predecessor id, but it turns out not to be
+ // very useful for assertions.
+ int64 ModifyPosition(int64 id, int64 parent_id, int64 predecessor_id) {
+ sync_api::ReadNode parent(trans_);
+ EXPECT_TRUE(parent.InitByIdLookup(parent_id));
+ sync_api::WriteNode node(trans_);
+ EXPECT_TRUE(node.InitByIdLookup(id));
+ int64 old_parent_id = node.GetParentId();
+ if (predecessor_id == 0) {
+ EXPECT_TRUE(node.SetPosition(parent, NULL));
+ } else {
+ sync_api::ReadNode predecessor(trans_);
+ EXPECT_TRUE(predecessor.InitByIdLookup(predecessor_id));
+ EXPECT_EQ(predecessor.GetParentId(), parent.GetId());
+ EXPECT_TRUE(node.SetPosition(parent, &predecessor));
+ }
+ SetModified(id);
+ return old_parent_id;
+ }
+
+ // Pass the fake change list to |service|.
+ void ApplyPendingChanges(ChangeProcessor* processor) {
+ processor->ApplyChangesFromSyncModel(trans_,
+ changes_.size() ? &changes_[0] : NULL, changes_.size());
+ }
+
+ const std::vector<sync_api::SyncManager::ChangeRecord>& changes() {
+ return changes_;
+ }
+
+ private:
+ // Helper function to push an ACTION_UPDATE record onto the back
+ // of the changelist.
+ void SetModified(int64 id) {
+ // Coalesce multi-property edits.
+ if (!changes_.empty() && changes_.back().id == id &&
+ changes_.back().action ==
+ sync_api::SyncManager::ChangeRecord::ACTION_UPDATE)
+ return;
+ sync_api::SyncManager::ChangeRecord record;
+ record.action = sync_api::SyncManager::ChangeRecord::ACTION_UPDATE;
+ record.id = id;
+ changes_.push_back(record);
+ }
+
+ // The transaction on which everything happens.
+ sync_api::WriteTransaction *trans_;
+
+ // The change list we construct.
+ std::vector<sync_api::SyncManager::ChangeRecord> changes_;
+};
+
+class MockUnrecoverableErrorHandler : public UnrecoverableErrorHandler {
+ public:
+ MOCK_METHOD2(OnUnrecoverableError,
+ void(const tracked_objects::Location&, const std::string&));
+};
+
+class ProfileSyncServiceBookmarkTest : public testing::Test {
+ protected:
+ enum LoadOption { LOAD_FROM_STORAGE, DELETE_EXISTING_STORAGE };
+ enum SaveOption { SAVE_TO_STORAGE, DONT_SAVE_TO_STORAGE };
+
+ ProfileSyncServiceBookmarkTest()
+ : ui_thread_(BrowserThread::UI, &message_loop_),
+ file_thread_(BrowserThread::FILE, &message_loop_),
+ model_(NULL) {
+ }
+
+ virtual ~ProfileSyncServiceBookmarkTest() {
+ StopSync();
+ UnloadBookmarkModel();
+ }
+
+ virtual void SetUp() {
+ test_user_share_.SetUp();
+ }
+
+ virtual void TearDown() {
+ test_user_share_.TearDown();
+ }
+
+ // Load (or re-load) the bookmark model. |load| controls use of the
+ // bookmarks file on disk. |save| controls whether the newly loaded
+ // bookmark model will write out a bookmark file as it goes.
+ void LoadBookmarkModel(LoadOption load, SaveOption save) {
+ bool delete_bookmarks = load == DELETE_EXISTING_STORAGE;
+ profile_.CreateBookmarkModel(delete_bookmarks);
+ model_ = profile_.GetBookmarkModel();
+ // Wait for the bookmarks model to load.
+ profile_.BlockUntilBookmarkModelLoaded();
+ // This noticeably speeds up the unit tests that request it.
+ if (save == DONT_SAVE_TO_STORAGE)
+ model_->ClearStore();
+ message_loop_.RunAllPending();
+ }
+
+ void StartSync() {
+ // Set up model associator.
+ model_associator_.reset(new TestBookmarkModelAssociator(
+ profile_.GetBookmarkModel(),
+ test_user_share_.user_share(),
+ &mock_unrecoverable_error_handler_));
+ EXPECT_TRUE(model_associator_->AssociateModels());
+ MessageLoop::current()->RunAllPending();
+
+ // Set up change processor.
+ change_processor_.reset(
+ new BookmarkChangeProcessor(model_associator_.get(),
+ &mock_unrecoverable_error_handler_));
+ change_processor_->Start(&profile_, test_user_share_.user_share());
+ }
+
+ void StopSync() {
+ change_processor_->Stop();
+ change_processor_.reset();
+
+ EXPECT_TRUE(model_associator_->DisassociateModels());
+ model_associator_.reset();
+
+ message_loop_.RunAllPending();
+
+ // TODO(akalin): Actually close the database and flush it to disk
+ // (and make StartSync reload from disk). This would require
+ // refactoring TestUserShare.
+ }
+
+ void UnloadBookmarkModel() {
+ profile_.CreateBookmarkModel(false /* delete_bookmarks */);
+ model_ = NULL;
+ message_loop_.RunAllPending();
+ }
+
+ bool InitSyncNodeFromChromeNode(const BookmarkNode* bnode,
+ sync_api::BaseNode* sync_node) {
+ return model_associator_->InitSyncNodeFromChromeId(bnode->id(),
+ sync_node);
+ }
+
+ void ExpectSyncerNodeMatching(sync_api::BaseTransaction* trans,
+ const BookmarkNode* bnode) {
+ sync_api::ReadNode gnode(trans);
+ ASSERT_TRUE(InitSyncNodeFromChromeNode(bnode, &gnode));
+ // Non-root node titles and parents must match.
+ if (bnode != model_->GetBookmarkBarNode() &&
+ bnode != model_->other_node()) {
+ EXPECT_EQ(bnode->GetTitle(), WideToUTF16Hack(gnode.GetTitle()));
+ EXPECT_EQ(
+ model_associator_->GetChromeNodeFromSyncId(gnode.GetParentId()),
+ bnode->parent());
+ }
+ EXPECT_EQ(bnode->is_folder(), gnode.GetIsFolder());
+ if (bnode->is_url())
+ EXPECT_EQ(bnode->GetURL(), gnode.GetURL());
+
+ // Check for position matches.
+ int browser_index = bnode->parent()->GetIndexOf(bnode);
+ if (browser_index == 0) {
+ EXPECT_EQ(gnode.GetPredecessorId(), 0);
+ } else {
+ const BookmarkNode* bprev =
+ bnode->parent()->GetChild(browser_index - 1);
+ sync_api::ReadNode gprev(trans);
+ ASSERT_TRUE(InitSyncNodeFromChromeNode(bprev, &gprev));
+ EXPECT_EQ(gnode.GetPredecessorId(), gprev.GetId());
+ EXPECT_EQ(gnode.GetParentId(), gprev.GetParentId());
+ }
+ if (browser_index == bnode->parent()->child_count() - 1) {
+ EXPECT_EQ(gnode.GetSuccessorId(), 0);
+ } else {
+ const BookmarkNode* bnext =
+ bnode->parent()->GetChild(browser_index + 1);
+ sync_api::ReadNode gnext(trans);
+ ASSERT_TRUE(InitSyncNodeFromChromeNode(bnext, &gnext));
+ EXPECT_EQ(gnode.GetSuccessorId(), gnext.GetId());
+ EXPECT_EQ(gnode.GetParentId(), gnext.GetParentId());
+ }
+ if (bnode->child_count()) {
+ EXPECT_TRUE(gnode.GetFirstChildId());
+ }
+ }
+
+ void ExpectSyncerNodeMatching(const BookmarkNode* bnode) {
+ sync_api::ReadTransaction trans(test_user_share_.user_share());
+ ExpectSyncerNodeMatching(&trans, bnode);
+ }
+
+ void ExpectBrowserNodeMatching(sync_api::BaseTransaction* trans,
+ int64 sync_id) {
+ EXPECT_TRUE(sync_id);
+ const BookmarkNode* bnode =
+ model_associator_->GetChromeNodeFromSyncId(sync_id);
+ ASSERT_TRUE(bnode);
+ int64 id = model_associator_->GetSyncIdFromChromeId(bnode->id());
+ EXPECT_EQ(id, sync_id);
+ ExpectSyncerNodeMatching(trans, bnode);
+ }
+
+ void ExpectBrowserNodeUnknown(int64 sync_id) {
+ EXPECT_FALSE(model_associator_->GetChromeNodeFromSyncId(sync_id));
+ }
+
+ void ExpectBrowserNodeKnown(int64 sync_id) {
+ EXPECT_TRUE(model_associator_->GetChromeNodeFromSyncId(sync_id));
+ }
+
+ void ExpectSyncerNodeKnown(const BookmarkNode* node) {
+ int64 sync_id = model_associator_->GetSyncIdFromChromeId(node->id());
+ EXPECT_NE(sync_id, sync_api::kInvalidId);
+ }
+
+ void ExpectSyncerNodeUnknown(const BookmarkNode* node) {
+ int64 sync_id = model_associator_->GetSyncIdFromChromeId(node->id());
+ EXPECT_EQ(sync_id, sync_api::kInvalidId);
+ }
+
+ void ExpectBrowserNodeTitle(int64 sync_id, const std::wstring& title) {
+ const BookmarkNode* bnode =
+ model_associator_->GetChromeNodeFromSyncId(sync_id);
+ ASSERT_TRUE(bnode);
+ EXPECT_EQ(bnode->GetTitle(), WideToUTF16Hack(title));
+ }
+
+ void ExpectBrowserNodeURL(int64 sync_id, const std::string& url) {
+ const BookmarkNode* bnode =
+ model_associator_->GetChromeNodeFromSyncId(sync_id);
+ ASSERT_TRUE(bnode);
+ EXPECT_EQ(GURL(url), bnode->GetURL());
+ }
+
+ void ExpectBrowserNodeParent(int64 sync_id, int64 parent_sync_id) {
+ const BookmarkNode* node =
+ model_associator_->GetChromeNodeFromSyncId(sync_id);
+ ASSERT_TRUE(node);
+ const BookmarkNode* parent =
+ model_associator_->GetChromeNodeFromSyncId(parent_sync_id);
+ EXPECT_TRUE(parent);
+ EXPECT_EQ(node->parent(), parent);
+ }
+
+ void ExpectModelMatch(sync_api::BaseTransaction* trans) {
+ const BookmarkNode* root = model_->root_node();
+ EXPECT_EQ(root->GetIndexOf(model_->GetBookmarkBarNode()), 0);
+ EXPECT_EQ(root->GetIndexOf(model_->other_node()), 1);
+
+ std::stack<int64> stack;
+ stack.push(bookmark_bar_id());
+ while (!stack.empty()) {
+ int64 id = stack.top();
+ stack.pop();
+ if (!id) continue;
+
+ ExpectBrowserNodeMatching(trans, id);
+
+ sync_api::ReadNode gnode(trans);
+ ASSERT_TRUE(gnode.InitByIdLookup(id));
+ stack.push(gnode.GetFirstChildId());
+ stack.push(gnode.GetSuccessorId());
+ }
+ }
+
+ void ExpectModelMatch() {
+ sync_api::ReadTransaction trans(test_user_share_.user_share());
+ ExpectModelMatch(&trans);
+ }
+
+ int64 other_bookmarks_id() {
+ return
+ model_associator_->GetSyncIdFromChromeId(model_->other_node()->id());
+ }
+
+ int64 bookmark_bar_id() {
+ return model_associator_->GetSyncIdFromChromeId(
+ model_->GetBookmarkBarNode()->id());
+ }
+
+ private:
+ // Used by both |ui_thread_| and |file_thread_|.
+ MessageLoop message_loop_;
+ BrowserThread ui_thread_;
+ // Needed by |model_|.
+ BrowserThread file_thread_;
+
+ TestingProfile profile_;
+ scoped_ptr<TestBookmarkModelAssociator> model_associator_;
+
+ protected:
+ BookmarkModel* model_;
+ TestUserShare test_user_share_;
+ scoped_ptr<BookmarkChangeProcessor> change_processor_;
+ StrictMock<MockUnrecoverableErrorHandler> mock_unrecoverable_error_handler_;
+};
+
+TEST_F(ProfileSyncServiceBookmarkTest, InitialState) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ EXPECT_TRUE(other_bookmarks_id());
+ EXPECT_TRUE(bookmark_bar_id());
+
+ ExpectModelMatch();
+}
+
+TEST_F(ProfileSyncServiceBookmarkTest, BookmarkModelOperations) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ // Test addition.
+ const BookmarkNode* folder =
+ model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("foobar"));
+ ExpectSyncerNodeMatching(folder);
+ ExpectModelMatch();
+ const BookmarkNode* folder2 =
+ model_->AddFolder(folder, 0, ASCIIToUTF16("nested"));
+ ExpectSyncerNodeMatching(folder2);
+ ExpectModelMatch();
+ const BookmarkNode* url1 = model_->AddURL(
+ folder, 0, ASCIIToUTF16("Internets #1 Pies Site"),
+ GURL("http://www.easypie.com/"));
+ ExpectSyncerNodeMatching(url1);
+ ExpectModelMatch();
+ const BookmarkNode* url2 = model_->AddURL(
+ folder, 1, ASCIIToUTF16("Airplanes"), GURL("http://www.easyjet.com/"));
+ ExpectSyncerNodeMatching(url2);
+ ExpectModelMatch();
+
+ // Test modification.
+ model_->SetTitle(url2, ASCIIToUTF16("EasyJet"));
+ ExpectModelMatch();
+ model_->Move(url1, folder2, 0);
+ ExpectModelMatch();
+ model_->Move(folder2, model_->GetBookmarkBarNode(), 0);
+ ExpectModelMatch();
+ model_->SetTitle(folder2, ASCIIToUTF16("Not Nested"));
+ ExpectModelMatch();
+ model_->Move(folder, folder2, 0);
+ ExpectModelMatch();
+ model_->SetTitle(folder, ASCIIToUTF16("who's nested now?"));
+ ExpectModelMatch();
+ model_->Copy(url2, model_->GetBookmarkBarNode(), 0);
+ ExpectModelMatch();
+
+ // Test deletion.
+ // Delete a single item.
+ model_->Remove(url2->parent(), url2->parent()->GetIndexOf(url2));
+ ExpectModelMatch();
+ // Delete an item with several children.
+ model_->Remove(folder2->parent(),
+ folder2->parent()->GetIndexOf(folder2));
+ ExpectModelMatch();
+}
+
+TEST_F(ProfileSyncServiceBookmarkTest, ServerChangeProcessing) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ sync_api::WriteTransaction trans(test_user_share_.user_share());
+
+ FakeServerChange adds(&trans);
+ int64 f1 = adds.AddFolder(L"Server Folder B", bookmark_bar_id(), 0);
+ int64 f2 = adds.AddFolder(L"Server Folder A", bookmark_bar_id(), f1);
+ int64 u1 = adds.AddURL(L"Some old site", "ftp://nifty.andrew.cmu.edu/",
+ bookmark_bar_id(), f2);
+ int64 u2 = adds.AddURL(L"Nifty", "ftp://nifty.andrew.cmu.edu/", f1, 0);
+ // u3 is a duplicate URL
+ int64 u3 = adds.AddURL(L"Nifty2", "ftp://nifty.andrew.cmu.edu/", f1, u2);
+ // u4 is a duplicate title, different URL.
+ adds.AddURL(L"Some old site", "http://slog.thestranger.com/",
+ bookmark_bar_id(), u1);
+ // u5 tests an empty-string title.
+ std::string javascript_url(
+ "javascript:(function(){var w=window.open(" \
+ "'about:blank','gnotesWin','location=0,menubar=0," \
+ "scrollbars=0,status=0,toolbar=0,width=300," \
+ "height=300,resizable');});");
+ adds.AddURL(L"", javascript_url, other_bookmarks_id(), 0);
+
+ std::vector<sync_api::SyncManager::ChangeRecord>::const_iterator it;
+ // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
+ for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
+ ExpectBrowserNodeUnknown(it->id);
+
+ adds.ApplyPendingChanges(change_processor_.get());
+
+ // Make sure the bookmark model received all of the nodes in |adds|.
+ for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
+ ExpectBrowserNodeMatching(&trans, it->id);
+ ExpectModelMatch(&trans);
+
+ // Part two: test modifications.
+ FakeServerChange mods(&trans);
+ // Mess with u2, and move it into empty folder f2
+ // TODO(ncarter): Determine if we allow ModifyURL ops or not.
+ /* std::wstring u2_old_url = mods.ModifyURL(u2, L"http://www.google.com"); */
+ std::wstring u2_old_title = mods.ModifyTitle(u2, L"The Google");
+ int64 u2_old_parent = mods.ModifyPosition(u2, f2, 0);
+
+ // Now move f1 after u2.
+ std::wstring f1_old_title = mods.ModifyTitle(f1, L"Server Folder C");
+ int64 f1_old_parent = mods.ModifyPosition(f1, f2, u2);
+
+ // Then add u3 after f1.
+ int64 u3_old_parent = mods.ModifyPosition(u3, f2, f1);
+
+ // Test that the property changes have not yet taken effect.
+ ExpectBrowserNodeTitle(u2, u2_old_title);
+ /* ExpectBrowserNodeURL(u2, u2_old_url); */
+ ExpectBrowserNodeParent(u2, u2_old_parent);
+
+ ExpectBrowserNodeTitle(f1, f1_old_title);
+ ExpectBrowserNodeParent(f1, f1_old_parent);
+
+ ExpectBrowserNodeParent(u3, u3_old_parent);
+
+ // Apply the changes.
+ mods.ApplyPendingChanges(change_processor_.get());
+
+ // Check for successful application.
+ for (it = mods.changes().begin(); it != mods.changes().end(); ++it)
+ ExpectBrowserNodeMatching(&trans, it->id);
+ ExpectModelMatch(&trans);
+
+ // Part 3: Test URL deletion.
+ FakeServerChange dels(&trans);
+ dels.Delete(u2);
+ dels.Delete(u3);
+
+ ExpectBrowserNodeKnown(u2);
+ ExpectBrowserNodeKnown(u3);
+
+ dels.ApplyPendingChanges(change_processor_.get());
+
+ ExpectBrowserNodeUnknown(u2);
+ ExpectBrowserNodeUnknown(u3);
+ ExpectModelMatch(&trans);
+}
+
+// Tests a specific case in ApplyModelChanges where we move the
+// children out from under a parent, and then delete the parent
+// in the same changelist. The delete shows up first in the changelist,
+// requiring the children to be moved to a temporary location.
+TEST_F(ProfileSyncServiceBookmarkTest, ServerChangeRequiringFosterParent) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ sync_api::WriteTransaction trans(test_user_share_.user_share());
+
+ // Stress the immediate children of other_node because that's where
+ // ApplyModelChanges puts a temporary foster parent node.
+ std::string url("http://dev.chromium.org/");
+ FakeServerChange adds(&trans);
+ int64 f0 = other_bookmarks_id(); // + other_node
+ int64 f1 = adds.AddFolder(L"f1", f0, 0); // + f1
+ int64 f2 = adds.AddFolder(L"f2", f1, 0); // + f2
+ int64 u3 = adds.AddURL( L"u3", url, f2, 0); // + u3 NOLINT
+ int64 u4 = adds.AddURL( L"u4", url, f2, u3); // + u4 NOLINT
+ int64 u5 = adds.AddURL( L"u5", url, f1, f2); // + u5 NOLINT
+ int64 f6 = adds.AddFolder(L"f6", f1, u5); // + f6
+ int64 u7 = adds.AddURL( L"u7", url, f0, f1); // + u7 NOLINT
+
+ std::vector<sync_api::SyncManager::ChangeRecord>::const_iterator it;
+ // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
+ for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
+ ExpectBrowserNodeUnknown(it->id);
+
+ adds.ApplyPendingChanges(change_processor_.get());
+
+ // Make sure the bookmark model received all of the nodes in |adds|.
+ for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
+ ExpectBrowserNodeMatching(&trans, it->id);
+ ExpectModelMatch(&trans);
+
+ // We have to do the moves before the deletions, but FakeServerChange will
+ // put the deletion at the front of the changelist.
+ FakeServerChange ops(&trans);
+ ops.ModifyPosition(f6, other_bookmarks_id(), 0);
+ ops.ModifyPosition(u3, other_bookmarks_id(), f1); // Prev == f1 is OK here.
+ ops.ModifyPosition(f2, other_bookmarks_id(), u7);
+ ops.ModifyPosition(u7, f2, 0);
+ ops.ModifyPosition(u4, other_bookmarks_id(), f2);
+ ops.ModifyPosition(u5, f6, 0);
+ ops.Delete(f1);
+
+ ops.ApplyPendingChanges(change_processor_.get());
+
+ ExpectModelMatch(&trans);
+}
+
+// Simulate a server change record containing a valid but non-canonical URL.
+TEST_F(ProfileSyncServiceBookmarkTest, ServerChangeWithNonCanonicalURL) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ {
+ sync_api::WriteTransaction trans(test_user_share_.user_share());
+
+ FakeServerChange adds(&trans);
+ std::string url("http://dev.chromium.org");
+ EXPECT_NE(GURL(url).spec(), url);
+ adds.AddURL(L"u1", url, other_bookmarks_id(), 0);
+
+ adds.ApplyPendingChanges(change_processor_.get());
+
+ EXPECT_TRUE(model_->other_node()->child_count() == 1);
+ ExpectModelMatch(&trans);
+ }
+
+ // Now reboot the sync service, forcing a merge step.
+ StopSync();
+ LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ // There should still be just the one bookmark.
+ EXPECT_TRUE(model_->other_node()->child_count() == 1);
+ ExpectModelMatch();
+}
+
+// Simulate a server change record containing an invalid URL (per GURL).
+// TODO(ncarter): Disabled due to crashes. Fix bug 1677563.
+TEST_F(ProfileSyncServiceBookmarkTest, DISABLED_ServerChangeWithInvalidURL) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ int child_count = 0;
+ {
+ sync_api::WriteTransaction trans(test_user_share_.user_share());
+
+ FakeServerChange adds(&trans);
+ std::string url("x");
+ EXPECT_FALSE(GURL(url).is_valid());
+ adds.AddURL(L"u1", url, other_bookmarks_id(), 0);
+
+ adds.ApplyPendingChanges(change_processor_.get());
+
+ // We're lenient about what should happen -- the model could wind up with
+ // the node or without it; but things should be consistent, and we
+ // shouldn't crash.
+ child_count = model_->other_node()->child_count();
+ EXPECT_TRUE(child_count == 0 || child_count == 1);
+ ExpectModelMatch(&trans);
+ }
+
+ // Now reboot the sync service, forcing a merge step.
+ StopSync();
+ LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ // Things ought not to have changed.
+ EXPECT_EQ(model_->other_node()->child_count(), child_count);
+ ExpectModelMatch();
+}
+
+
+// Test strings that might pose a problem if the titles ever became used as
+// file names in the sync backend.
+TEST_F(ProfileSyncServiceBookmarkTest, CornerCaseNames) {
+ // TODO(ncarter): Bug 1570238 explains the failure of this test.
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ const char* names[] = {
+ // The empty string.
+ "",
+ // Illegal Windows filenames.
+ "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
+ "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3",
+ "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+ // Current/parent directory markers.
+ ".", "..", "...",
+ // Files created automatically by the Windows shell.
+ "Thumbs.db", ".DS_Store",
+ // Names including Win32-illegal characters, and path separators.
+ "foo/bar", "foo\\bar", "foo?bar", "foo:bar", "foo|bar", "foo\"bar",
+ "foo'bar", "foo<bar", "foo>bar", "foo%bar", "foo*bar", "foo]bar",
+ "foo[bar",
+ };
+ // Create both folders and bookmarks using each name.
+ GURL url("http://www.doublemint.com");
+ for (size_t i = 0; i < arraysize(names); ++i) {
+ model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16(names[i]));
+ model_->AddURL(model_->other_node(), 0, ASCIIToUTF16(names[i]), url);
+ }
+
+ // Verify that the browser model matches the sync model.
+ EXPECT_TRUE(model_->other_node()->child_count() == 2*arraysize(names));
+ ExpectModelMatch();
+}
+
+// Stress the internal representation of position by sparse numbers. We want
+// to repeatedly bisect the range of available positions, to force the
+// syncer code to renumber its ranges. Pick a number big enough so that it
+// would exhaust 32bits of room between items a couple of times.
+TEST_F(ProfileSyncServiceBookmarkTest, RepeatedMiddleInsertion) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ static const int kTimesToInsert = 256;
+
+ // Create two book-end nodes to insert between.
+ model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("Alpha"));
+ model_->AddFolder(model_->other_node(), 1, ASCIIToUTF16("Omega"));
+ int count = 2;
+
+ // Test insertion in first half of range by repeatedly inserting in second
+ // position.
+ for (int i = 0; i < kTimesToInsert; ++i) {
+ string16 title = ASCIIToUTF16("Pre-insertion ") + base::IntToString16(i);
+ model_->AddFolder(model_->other_node(), 1, title);
+ count++;
+ }
+
+ // Test insertion in second half of range by repeatedly inserting in
+ // second-to-last position.
+ for (int i = 0; i < kTimesToInsert; ++i) {
+ string16 title = ASCIIToUTF16("Post-insertion ") + base::IntToString16(i);
+ model_->AddFolder(model_->other_node(), count - 1, title);
+ count++;
+ }
+
+ // Verify that the browser model matches the sync model.
+ EXPECT_EQ(model_->other_node()->child_count(), count);
+ ExpectModelMatch();
+}
+
+// Introduce a consistency violation into the model, and see that it
+// puts itself into a lame, error state.
+TEST_F(ProfileSyncServiceBookmarkTest, UnrecoverableErrorSuspendsService) {
+ EXPECT_CALL(mock_unrecoverable_error_handler_,
+ OnUnrecoverableError(_, _));
+
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ // Add a node which will be the target of the consistency violation.
+ const BookmarkNode* node =
+ model_->AddFolder(model_->other_node(), 0, ASCIIToUTF16("node"));
+ ExpectSyncerNodeMatching(node);
+
+ // Now destroy the syncer node as if we were the ProfileSyncService without
+ // updating the ProfileSyncService state. This should introduce
+ // inconsistency between the two models.
+ {
+ sync_api::WriteTransaction trans(test_user_share_.user_share());
+ sync_api::WriteNode sync_node(&trans);
+ ASSERT_TRUE(InitSyncNodeFromChromeNode(node, &sync_node));
+ sync_node.Remove();
+ }
+ // The models don't match at this point, but the ProfileSyncService
+ // doesn't know it yet.
+ ExpectSyncerNodeKnown(node);
+
+ // Add a child to the inconsistent node. This should cause detection of the
+ // problem and the syncer should stop processing changes.
+ model_->AddFolder(node, 0, ASCIIToUTF16("nested"));
+}
+
+// See what happens if we run model association when there are two exact URL
+// duplicate bookmarks. The BookmarkModelAssociator should not fall over when
+// this happens.
+TEST_F(ProfileSyncServiceBookmarkTest, MergeDuplicates) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("Dup"),
+ GURL("http://dup.com/"));
+ model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("Dup"),
+ GURL("http://dup.com/"));
+
+ EXPECT_EQ(2, model_->other_node()->child_count());
+
+ // Restart the sync service to trigger model association.
+ StopSync();
+ StartSync();
+
+ EXPECT_EQ(2, model_->other_node()->child_count());
+ ExpectModelMatch();
+}
+
+struct TestData {
+ const wchar_t* title;
+ const char* url;
+};
+
+// TODO(ncarter): Integrate the existing TestNode/PopulateNodeFromString code
+// in the bookmark model unittest, to make it simpler to set up test data
+// here (and reduce the amount of duplication among tests), and to reduce the
+// duplication.
+class ProfileSyncServiceBookmarkTestWithData
+ : public ProfileSyncServiceBookmarkTest {
+ protected:
+ // Populates or compares children of the given bookmark node from/with the
+ // given test data array with the given size.
+ void PopulateFromTestData(const BookmarkNode* node,
+ const TestData* data,
+ int size);
+ void CompareWithTestData(const BookmarkNode* node,
+ const TestData* data,
+ int size);
+
+ void ExpectBookmarkModelMatchesTestData();
+ void WriteTestDataToBookmarkModel();
+};
+
+namespace {
+
+// Constants for bookmark model that looks like:
+// |-- Bookmark bar
+// | |-- u2, http://www.u2.com/
+// | |-- f1
+// | | |-- f1u4, http://www.f1u4.com/
+// | | |-- f1u2, http://www.f1u2.com/
+// | | |-- f1u3, http://www.f1u3.com/
+// | | +-- f1u1, http://www.f1u1.com/
+// | |-- u1, http://www.u1.com/
+// | +-- f2
+// | |-- f2u2, http://www.f2u2.com/
+// | |-- f2u4, http://www.f2u4.com/
+// | |-- f2u3, http://www.f2u3.com/
+// | +-- f2u1, http://www.f2u1.com/
+// +-- Other bookmarks
+// |-- f3
+// | |-- f3u4, http://www.f3u4.com/
+// | |-- f3u2, http://www.f3u2.com/
+// | |-- f3u3, http://www.f3u3.com/
+// | +-- f3u1, http://www.f3u1.com/
+// |-- u4, http://www.u4.com/
+// |-- u3, http://www.u3.com/
+// --- f4
+// | |-- f4u1, http://www.f4u1.com/
+// | |-- f4u2, http://www.f4u2.com/
+// | |-- f4u3, http://www.f4u3.com/
+// | +-- f4u4, http://www.f4u4.com/
+// |-- dup
+// | +-- dupu1, http://www.dupu1.com/
+// +-- dup
+// +-- dupu2, http://www.dupu1.com/
+//
+static TestData kBookmarkBarChildren[] = {
+ { L"u2", "http://www.u2.com/" },
+ { L"f1", NULL },
+ { L"u1", "http://www.u1.com/" },
+ { L"f2", NULL },
+};
+static TestData kF1Children[] = {
+ { L"f1u4", "http://www.f1u4.com/" },
+ { L"f1u2", "http://www.f1u2.com/" },
+ { L"f1u3", "http://www.f1u3.com/" },
+ { L"f1u1", "http://www.f1u1.com/" },
+};
+static TestData kF2Children[] = {
+ { L"f2u2", "http://www.f2u2.com/" },
+ { L"f2u4", "http://www.f2u4.com/" },
+ { L"f2u3", "http://www.f2u3.com/" },
+ { L"f2u1", "http://www.f2u1.com/" },
+};
+
+static TestData kOtherBookmarkChildren[] = {
+ { L"f3", NULL },
+ { L"u4", "http://www.u4.com/" },
+ { L"u3", "http://www.u3.com/" },
+ { L"f4", NULL },
+ { L"dup", NULL },
+ { L"dup", NULL },
+};
+static TestData kF3Children[] = {
+ { L"f3u4", "http://www.f3u4.com/" },
+ { L"f3u2", "http://www.f3u2.com/" },
+ { L"f3u3", "http://www.f3u3.com/" },
+ { L"f3u1", "http://www.f3u1.com/" },
+};
+static TestData kF4Children[] = {
+ { L"f4u1", "http://www.f4u1.com/" },
+ { L"f4u2", "http://www.f4u2.com/" },
+ { L"f4u3", "http://www.f4u3.com/" },
+ { L"f4u4", "http://www.f4u4.com/" },
+};
+static TestData kDup1Children[] = {
+ { L"dupu1", "http://www.dupu1.com/" },
+};
+static TestData kDup2Children[] = {
+ { L"dupu2", "http://www.dupu2.com/" },
+};
+
+} // anonymous namespace.
+
+void ProfileSyncServiceBookmarkTestWithData::PopulateFromTestData(
+ const BookmarkNode* node, const TestData* data, int size) {
+ DCHECK(node);
+ DCHECK(data);
+ DCHECK(node->is_folder());
+ for (int i = 0; i < size; ++i) {
+ const TestData& item = data[i];
+ if (item.url) {
+ model_->AddURL(node, i, WideToUTF16Hack(item.title), GURL(item.url));
+ } else {
+ model_->AddFolder(node, i, WideToUTF16Hack(item.title));
+ }
+ }
+}
+
+void ProfileSyncServiceBookmarkTestWithData::CompareWithTestData(
+ const BookmarkNode* node, const TestData* data, int size) {
+ DCHECK(node);
+ DCHECK(data);
+ DCHECK(node->is_folder());
+ ASSERT_EQ(size, node->child_count());
+ for (int i = 0; i < size; ++i) {
+ const BookmarkNode* child_node = node->GetChild(i);
+ const TestData& item = data[i];
+ EXPECT_EQ(child_node->GetTitle(), WideToUTF16Hack(item.title));
+ if (item.url) {
+ EXPECT_FALSE(child_node->is_folder());
+ EXPECT_TRUE(child_node->is_url());
+ EXPECT_EQ(child_node->GetURL(), GURL(item.url));
+ } else {
+ EXPECT_TRUE(child_node->is_folder());
+ EXPECT_FALSE(child_node->is_url());
+ }
+ }
+}
+
+// TODO(munjal): We should implement some way of generating random data and can
+// use the same seed to generate the same sequence.
+void ProfileSyncServiceBookmarkTestWithData::WriteTestDataToBookmarkModel() {
+ const BookmarkNode* bookmarks_bar_node = model_->GetBookmarkBarNode();
+ PopulateFromTestData(bookmarks_bar_node,
+ kBookmarkBarChildren,
+ arraysize(kBookmarkBarChildren));
+
+ ASSERT_GE(bookmarks_bar_node->child_count(), 4);
+ const BookmarkNode* f1_node = bookmarks_bar_node->GetChild(1);
+ PopulateFromTestData(f1_node, kF1Children, arraysize(kF1Children));
+ const BookmarkNode* f2_node = bookmarks_bar_node->GetChild(3);
+ PopulateFromTestData(f2_node, kF2Children, arraysize(kF2Children));
+
+ const BookmarkNode* other_bookmarks_node = model_->other_node();
+ PopulateFromTestData(other_bookmarks_node,
+ kOtherBookmarkChildren,
+ arraysize(kOtherBookmarkChildren));
+
+ ASSERT_GE(other_bookmarks_node->child_count(), 6);
+ const BookmarkNode* f3_node = other_bookmarks_node->GetChild(0);
+ PopulateFromTestData(f3_node, kF3Children, arraysize(kF3Children));
+ const BookmarkNode* f4_node = other_bookmarks_node->GetChild(3);
+ PopulateFromTestData(f4_node, kF4Children, arraysize(kF4Children));
+ const BookmarkNode* dup_node = other_bookmarks_node->GetChild(4);
+ PopulateFromTestData(dup_node, kDup1Children, arraysize(kDup1Children));
+ dup_node = other_bookmarks_node->GetChild(5);
+ PopulateFromTestData(dup_node, kDup2Children, arraysize(kDup2Children));
+
+ ExpectBookmarkModelMatchesTestData();
+}
+
+void ProfileSyncServiceBookmarkTestWithData::
+ ExpectBookmarkModelMatchesTestData() {
+ const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
+ CompareWithTestData(bookmark_bar_node,
+ kBookmarkBarChildren,
+ arraysize(kBookmarkBarChildren));
+
+ ASSERT_GE(bookmark_bar_node->child_count(), 4);
+ const BookmarkNode* f1_node = bookmark_bar_node->GetChild(1);
+ CompareWithTestData(f1_node, kF1Children, arraysize(kF1Children));
+ const BookmarkNode* f2_node = bookmark_bar_node->GetChild(3);
+ CompareWithTestData(f2_node, kF2Children, arraysize(kF2Children));
+
+ const BookmarkNode* other_bookmarks_node = model_->other_node();
+ CompareWithTestData(other_bookmarks_node,
+ kOtherBookmarkChildren,
+ arraysize(kOtherBookmarkChildren));
+
+ ASSERT_GE(other_bookmarks_node->child_count(), 6);
+ const BookmarkNode* f3_node = other_bookmarks_node->GetChild(0);
+ CompareWithTestData(f3_node, kF3Children, arraysize(kF3Children));
+ const BookmarkNode* f4_node = other_bookmarks_node->GetChild(3);
+ CompareWithTestData(f4_node, kF4Children, arraysize(kF4Children));
+ const BookmarkNode* dup_node = other_bookmarks_node->GetChild(4);
+ CompareWithTestData(dup_node, kDup1Children, arraysize(kDup1Children));
+ dup_node = other_bookmarks_node->GetChild(5);
+ CompareWithTestData(dup_node, kDup2Children, arraysize(kDup2Children));
+}
+
+// Tests persistence of the profile sync service by unloading the
+// database and then reloading it from disk.
+TEST_F(ProfileSyncServiceBookmarkTestWithData, Persistence) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ WriteTestDataToBookmarkModel();
+
+ ExpectModelMatch();
+
+ // Force both models to discard their data and reload from disk. This
+ // simulates what would happen if the browser were to shutdown normally,
+ // and then relaunch.
+ StopSync();
+ UnloadBookmarkModel();
+ LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ ExpectBookmarkModelMatchesTestData();
+
+ // With the BookmarkModel contents verified, ExpectModelMatch will
+ // verify the contents of the sync model.
+ ExpectModelMatch();
+}
+
+// Tests the merge case when the BookmarkModel is non-empty but the
+// sync model is empty. This corresponds to uploading browser
+// bookmarks to an initially empty, new account.
+TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeWithEmptySyncModel) {
+ // Don't start the sync service until we've populated the bookmark model.
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+
+ WriteTestDataToBookmarkModel();
+
+ // Restart sync. This should trigger a merge step during
+ // initialization -- we expect the browser bookmarks to be written
+ // to the sync service during this call.
+ StartSync();
+
+ // Verify that the bookmark model hasn't changed, and that the sync model
+ // matches it exactly.
+ ExpectBookmarkModelMatchesTestData();
+ ExpectModelMatch();
+}
+
+// Tests the merge case when the BookmarkModel is empty but the sync model is
+// non-empty. This corresponds (somewhat) to a clean install of the browser,
+// with no bookmarks, connecting to a sync account that has some bookmarks.
+TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeWithEmptyBookmarkModel) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ WriteTestDataToBookmarkModel();
+
+ ExpectModelMatch();
+
+ // Force the databse to unload and write itself to disk.
+ StopSync();
+
+ // Blow away the bookmark model -- it should be empty afterwards.
+ UnloadBookmarkModel();
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ EXPECT_EQ(model_->GetBookmarkBarNode()->child_count(), 0);
+ EXPECT_EQ(model_->other_node()->child_count(), 0);
+
+ // Now restart the sync service. Starting it should populate the bookmark
+ // model -- test for consistency.
+ StartSync();
+ ExpectBookmarkModelMatchesTestData();
+ ExpectModelMatch();
+}
+
+// Tests the merge cases when both the models are expected to be identical
+// after the merge.
+TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeExpectedIdenticalModels) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+ WriteTestDataToBookmarkModel();
+ ExpectModelMatch();
+ StopSync();
+ UnloadBookmarkModel();
+
+ // At this point both the bookmark model and the server should have the
+ // exact same data and it should match the test data.
+ LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+ ExpectBookmarkModelMatchesTestData();
+ ExpectModelMatch();
+ StopSync();
+ UnloadBookmarkModel();
+
+ // Now reorder some bookmarks in the bookmark model and then merge. Make
+ // sure we get the order of the server after merge.
+ LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE);
+ ExpectBookmarkModelMatchesTestData();
+ const BookmarkNode* bookmark_bar = model_->GetBookmarkBarNode();
+ ASSERT_TRUE(bookmark_bar);
+ ASSERT_GT(bookmark_bar->child_count(), 1);
+ model_->Move(bookmark_bar->GetChild(0), bookmark_bar, 1);
+ StartSync();
+ ExpectModelMatch();
+ ExpectBookmarkModelMatchesTestData();
+}
+
+// Tests the merge cases when both the models are expected to be identical
+// after the merge.
+TEST_F(ProfileSyncServiceBookmarkTestWithData, MergeModelsWithSomeExtras) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ WriteTestDataToBookmarkModel();
+ ExpectBookmarkModelMatchesTestData();
+
+ // Remove some nodes and reorder some nodes.
+ const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
+ int remove_index = 2;
+ ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
+ const BookmarkNode* child_node = bookmark_bar_node->GetChild(remove_index);
+ ASSERT_TRUE(child_node);
+ ASSERT_TRUE(child_node->is_url());
+ model_->Remove(bookmark_bar_node, remove_index);
+ ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
+ child_node = bookmark_bar_node->GetChild(remove_index);
+ ASSERT_TRUE(child_node);
+ ASSERT_TRUE(child_node->is_folder());
+ model_->Remove(bookmark_bar_node, remove_index);
+
+ const BookmarkNode* other_node = model_->other_node();
+ ASSERT_GE(other_node->child_count(), 1);
+ const BookmarkNode* f3_node = other_node->GetChild(0);
+ ASSERT_TRUE(f3_node);
+ ASSERT_TRUE(f3_node->is_folder());
+ remove_index = 2;
+ ASSERT_GT(f3_node->child_count(), remove_index);
+ model_->Remove(f3_node, remove_index);
+ ASSERT_GT(f3_node->child_count(), remove_index);
+ model_->Remove(f3_node, remove_index);
+
+ StartSync();
+ ExpectModelMatch();
+ StopSync();
+
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ WriteTestDataToBookmarkModel();
+ ExpectBookmarkModelMatchesTestData();
+
+ // Remove some nodes and reorder some nodes.
+ bookmark_bar_node = model_->GetBookmarkBarNode();
+ remove_index = 0;
+ ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
+ child_node = bookmark_bar_node->GetChild(remove_index);
+ ASSERT_TRUE(child_node);
+ ASSERT_TRUE(child_node->is_url());
+ model_->Remove(bookmark_bar_node, remove_index);
+ ASSERT_GT(bookmark_bar_node->child_count(), remove_index);
+ child_node = bookmark_bar_node->GetChild(remove_index);
+ ASSERT_TRUE(child_node);
+ ASSERT_TRUE(child_node->is_folder());
+ model_->Remove(bookmark_bar_node, remove_index);
+
+ ASSERT_GE(bookmark_bar_node->child_count(), 2);
+ model_->Move(bookmark_bar_node->GetChild(0), bookmark_bar_node, 1);
+
+ other_node = model_->other_node();
+ ASSERT_GE(other_node->child_count(), 1);
+ f3_node = other_node->GetChild(0);
+ ASSERT_TRUE(f3_node);
+ ASSERT_TRUE(f3_node->is_folder());
+ remove_index = 0;
+ ASSERT_GT(f3_node->child_count(), remove_index);
+ model_->Remove(f3_node, remove_index);
+ ASSERT_GT(f3_node->child_count(), remove_index);
+ model_->Remove(f3_node, remove_index);
+
+ ASSERT_GE(other_node->child_count(), 4);
+ model_->Move(other_node->GetChild(0), other_node, 1);
+ model_->Move(other_node->GetChild(2), other_node, 3);
+
+ StartSync();
+ ExpectModelMatch();
+
+ // After the merge, the model should match the test data.
+ ExpectBookmarkModelMatchesTestData();
+}
+
+// Tests that when persisted model associations are used, things work fine.
+TEST_F(ProfileSyncServiceBookmarkTestWithData, ModelAssociationPersistence) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ WriteTestDataToBookmarkModel();
+ StartSync();
+ ExpectModelMatch();
+ // Force sync to shut down and write itself to disk.
+ StopSync();
+ // Now restart sync. This time it should use the persistent
+ // associations.
+ StartSync();
+ ExpectModelMatch();
+}
+
+// Tests that when persisted model associations are used, things work fine.
+TEST_F(ProfileSyncServiceBookmarkTestWithData,
+ ModelAssociationInvalidPersistence) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ WriteTestDataToBookmarkModel();
+ StartSync();
+ ExpectModelMatch();
+ // Force sync to shut down and write itself to disk.
+ StopSync();
+ // Change the bookmark model before restarting sync service to simulate
+ // the situation where bookmark model is different from sync model and
+ // make sure model associator correctly rebuilds associations.
+ const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
+ model_->AddURL(bookmark_bar_node, 0, ASCIIToUTF16("xtra"),
+ GURL("http://www.xtra.com"));
+ // Now restart sync. This time it will try to use the persistent
+ // associations and realize that they are invalid and hence will rebuild
+ // associations.
+ StartSync();
+ ExpectModelMatch();
+}
+
+TEST_F(ProfileSyncServiceBookmarkTestWithData, SortChildren) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+ StartSync();
+
+ // Write test data to bookmark model and verify that the models match.
+ WriteTestDataToBookmarkModel();
+ const BookmarkNode* folder_added = model_->other_node()->GetChild(0);
+ ASSERT_TRUE(folder_added);
+ ASSERT_TRUE(folder_added->is_folder());
+
+ ExpectModelMatch();
+
+ // Sort the other-bookmarks children and expect that hte models match.
+ model_->SortChildren(folder_added);
+ ExpectModelMatch();
+}
+
+// See what happens if we enable sync but then delete the "Sync Data"
+// folder.
+TEST_F(ProfileSyncServiceBookmarkTestWithData,
+ RecoverAfterDeletingSyncDataDirectory) {
+ LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
+ StartSync();
+
+ WriteTestDataToBookmarkModel();
+
+ StopSync();
+
+ // Nuke the sync DB and reload.
+ test_user_share_.TearDown();
+ test_user_share_.SetUp();
+
+ StartSync();
+
+ // Make sure we're back in sync. In real life, the user would need
+ // to reauthenticate before this happens, but in the test, authentication
+ // is sidestepped.
+ ExpectBookmarkModelMatchesTestData();
+ ExpectModelMatch();
+}
+
+} // namespace
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc
index 72bb584..ae13c6d 100644
--- a/chrome/browser/sync/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/profile_sync_service_harness.cc
@@ -1,20 +1,26 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/profile_sync_service_harness.h"
+#include <stddef.h>
#include <algorithm>
+#include <iterator>
+#include <ostream>
+#include <set>
#include <vector>
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
+#include "base/task.h"
+#include "base/tracked.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/net/gaia/token_service.h"
-#include "chrome/browser/sync/glue/sync_backend_host.h"
#include "chrome/browser/sync/sessions/session_state.h"
-#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/net/gaia/google_service_auth_error.h"
-#include "chrome/common/notification_source.h"
+#include "chrome/browser/sync/signin_manager.h"
+
+using browser_sync::sessions::SyncSessionSnapshot;
// The amount of time for which we wait for a live sync operation to complete.
static const int kLiveSyncOperationTimeoutMs = 45000;
@@ -160,28 +166,8 @@ bool ProfileSyncServiceHarness::SetupSync(
(syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE));
service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
- // Wait for a passphrase to be required.
- DCHECK_EQ(wait_state_, WAITING_FOR_PASSPHRASE_REQUIRED);
- if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
- "Waiting for Passphrase required.")) {
- LOG(ERROR) << "Passphrase required not seen after "
- << kLiveSyncOperationTimeoutMs / 1000
- << " seconds.";
- return false;
- }
-
- // Wait for initial gaia passphrase to be accepted.
- DCHECK_EQ(wait_state_, WAITING_FOR_PASSPHRASE_ACCEPTED);
- if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
- "Waiting for Passphrase accept.")) {
- LOG(ERROR) << "Passphrase accept not seen after "
- << kLiveSyncOperationTimeoutMs / 1000
- << " seconds.";
- return false;
- }
-
- DCHECK_EQ(wait_state_, WAITING_FOR_INITIAL_SYNC);
// Wait for initial sync cycle to be completed.
+ DCHECK_EQ(wait_state_, WAITING_FOR_INITIAL_SYNC);
if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
"Waiting for initial sync cycle to complete.")) {
LOG(ERROR) << "Initial sync cycle did not complete after "
@@ -212,27 +198,9 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() {
case WAITING_FOR_ON_BACKEND_INITIALIZED: {
LogClientInfo("WAITING_FOR_ON_BACKEND_INITIALIZED");
if (service()->sync_initialized()) {
- // The sync backend is initialized. We now wait for passphrase events.
- SignalStateCompleteWithNextState(WAITING_FOR_PASSPHRASE_REQUIRED);
- }
- break;
- }
- case WAITING_FOR_PASSPHRASE_REQUIRED: {
- LogClientInfo("WAITING_FOR_PASSPHRASE_REQUIRED");
- if (service()->observed_passphrase_required()) {
- // Special case when the first client signs in to sync.
- if (id_ == 0)
- DCHECK(!service()->passphrase_required_for_decryption());
- // The SYNC_PASSPHRASE_REQUIRED notification has been seen.
- SignalStateCompleteWithNextState(WAITING_FOR_PASSPHRASE_ACCEPTED);
- }
- break;
- }
- case WAITING_FOR_PASSPHRASE_ACCEPTED: {
- LogClientInfo("WAITING_FOR_PASSPHRASE_ACCEPTED");
- if (service()->ShouldPushChanges())
- // The SYNC_PASSPHRASE_ACCEPTED notification has been seen.
+ // The sync backend is initialized.
SignalStateCompleteWithNextState(WAITING_FOR_INITIAL_SYNC);
+ }
break;
}
case WAITING_FOR_INITIAL_SYNC: {
@@ -270,6 +238,25 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() {
SignalStateCompleteWithNextState(FULLY_SYNCED);
break;
}
+ case WAITING_FOR_PASSPHRASE_ACCEPTED: {
+ LogClientInfo("WAITING_FOR_PASSPHRASE_ACCEPTED");
+ // TODO(atwilson): After ProfileSyncService::OnPassphraseAccepted() is
+ // fixed, add an extra check to make sure that the value of
+ // service()->observed_passphrase_required() is false.
+ if (service()->ShouldPushChanges()) {
+ // The passphrase has been accepted, and sync has been restarted.
+ SignalStateCompleteWithNextState(FULLY_SYNCED);
+ }
+ break;
+ }
+ case WAITING_FOR_ENCRYPTION: {
+ LogClientInfo("WAITING_FOR_ENCRYPTION");
+ if (IsTypeEncrypted(waiting_for_encryption_type_)) {
+ // Encryption is complete for the type we are waiting on.
+ SignalStateCompleteWithNextState(FULLY_SYNCED);
+ }
+ break;
+ }
case SERVER_UNREACHABLE: {
LogClientInfo("SERVER_UNREACHABLE");
if (GetStatus().server_reachable) {
@@ -289,14 +276,6 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() {
LogClientInfo("SYNC_DISABLED");
break;
}
- case WAITING_FOR_ENCRYPTION: {
- // If the type whose encryption we are waiting for is now complete, there
- // is nothing to do.
- LogClientInfo("WAITING_FOR_ENCRYPTION");
- if (IsTypeEncrypted(waiting_for_encryption_type_))
- SignalStateCompleteWithNextState(FULLY_SYNCED);
- break;
- }
default:
// Invalid state during observer callback which may be triggered by other
// classes using the the UI message loop. Defer to their handling.
@@ -315,6 +294,15 @@ bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() {
LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
return false;
}
+
+ // TODO(atwilson): After ProfileSyncService::OnPassphraseAccepted() is
+ // fixed, add an extra check to make sure that the value of
+ // service()->observed_passphrase_required() is false.
+ if (service()->ShouldPushChanges()) {
+ // Passphrase is already accepted; don't wait.
+ return true;
+ }
+
wait_state_ = WAITING_FOR_PASSPHRASE_ACCEPTED;
return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
"Waiting for passphrase accepted.");
@@ -327,31 +315,32 @@ bool ProfileSyncServiceHarness::AwaitSyncCycleCompletion(
LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
return false;
}
- if (!IsSynced()) {
- if (wait_state_ == SERVER_UNREACHABLE) {
- // Client was offline; wait for it to go online, and then wait for sync.
- AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
- DCHECK_EQ(wait_state_, WAITING_FOR_SYNC_TO_FINISH);
- return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
- } else {
- DCHECK(service()->sync_initialized());
- wait_state_ = WAITING_FOR_SYNC_TO_FINISH;
- AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
- if (wait_state_ == FULLY_SYNCED) {
- // Client is online; sync was successful.
- return true;
- } else if (wait_state_ == SERVER_UNREACHABLE) {
- // Client is offline; sync was unsuccessful.
- return false;
- } else {
- LOG(ERROR) << "Invalid wait state:" << wait_state_;
- return false;
- }
- }
- } else {
+
+ if (IsSynced()) {
// Client is already synced; don't wait.
return true;
}
+
+ if (wait_state_ == SERVER_UNREACHABLE) {
+ // Client was offline; wait for it to go online, and then wait for sync.
+ AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
+ DCHECK_EQ(wait_state_, WAITING_FOR_SYNC_TO_FINISH);
+ return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
+ } else {
+ DCHECK(service()->sync_initialized());
+ wait_state_ = WAITING_FOR_SYNC_TO_FINISH;
+ AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
+ if (wait_state_ == FULLY_SYNCED) {
+ // Client is online; sync was successful.
+ return true;
+ } else if (wait_state_ == SERVER_UNREACHABLE) {
+ // Client is offline; sync was unsuccessful.
+ return false;
+ } else {
+ LOG(ERROR) << "Invalid wait state:" << wait_state_;
+ return false;
+ }
+ }
}
bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
@@ -401,10 +390,13 @@ bool ProfileSyncServiceHarness::WaitUntilTimestampMatches(
LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
return false;
}
- DCHECK(!timestamp_match_partner_);
- if (MatchesOtherClient(partner))
+
+ if (MatchesOtherClient(partner)) {
+ // Timestamps already match; don't wait.
return true;
+ }
+ DCHECK(!timestamp_match_partner_);
timestamp_match_partner_ = partner;
partner->service()->AddObserver(this);
wait_state_ = WAITING_FOR_UPDATES;
@@ -580,7 +572,7 @@ std::string ProfileSyncServiceHarness::GetUpdatedTimestamp(
return snap->download_progress_markers[model_type];
}
-void ProfileSyncServiceHarness::LogClientInfo(std::string message) {
+void ProfileSyncServiceHarness::LogClientInfo(const std::string& message) {
if (service()) {
const SyncSessionSnapshot* snap = GetLastSessionSnapshot();
if (snap) {
diff --git a/chrome/browser/sync/profile_sync_service_harness.h b/chrome/browser/sync/profile_sync_service_harness.h
index d895e6a..c4470aa 100644
--- a/chrome/browser/sync/profile_sync_service_harness.h
+++ b/chrome/browser/sync/profile_sync_service_harness.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,13 +9,19 @@
#include <string>
#include <vector>
-#include "base/time.h"
+#include "base/basictypes.h"
#include "chrome/browser/sync/profile_sync_service.h"
-
-using browser_sync::sessions::SyncSessionSnapshot;
+#include "chrome/browser/sync/profile_sync_service_observer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
class Profile;
+namespace browser_sync {
+ namespace sessions {
+ struct SyncSessionSnapshot;
+ }
+}
+
// An instance of this class is basically our notion of a "sync client" for
// automation purposes. It harnesses the ProfileSyncService member of the
// profile passed to it on construction and automates certain things like setup
@@ -87,7 +93,7 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
std::vector<ProfileSyncServiceHarness*>& clients);
// If a SetPassphrase call has been issued with a valid passphrase, this
- // will wait until the Cryptographer broadcasts SYNC_PASSPHRASE_ACCEPTED.
+ // will wait until the passphrase has been accepted.
bool AwaitPassphraseAccepted();
// Returns the ProfileSyncService member of the the sync client.
@@ -112,7 +118,8 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
void DisableSyncForAllDatatypes();
// Returns a snapshot of the current sync session.
- const SyncSessionSnapshot* GetLastSessionSnapshot() const;
+ const browser_sync::sessions::SyncSessionSnapshot*
+ GetLastSessionSnapshot() const;
// Encrypt the datatype |type|. This method will block while the sync backend
// host performs the encryption or a timeout is reached. Returns false if
@@ -135,12 +142,6 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
// The sync client awaits the OnBackendInitialized() callback.
WAITING_FOR_ON_BACKEND_INITIALIZED,
- // Waiting for a passphrase to be required.
- WAITING_FOR_PASSPHRASE_REQUIRED,
-
- // Waiting for a set passphrase to be accepted by the cryptographer.
- WAITING_FOR_PASSPHRASE_ACCEPTED,
-
// The sync client is waiting for the first sync cycle to complete.
WAITING_FOR_INITIAL_SYNC,
@@ -150,6 +151,10 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
// The sync client anticipates incoming updates leading to a new sync cycle.
WAITING_FOR_UPDATES,
+ // The sync client is waiting for its passphrase to be accepted by the
+ // cryptographer.
+ WAITING_FOR_PASSPHRASE_ACCEPTED,
+
// The sync client anticipates encryption of new datatypes.
WAITING_FOR_ENCRYPTION,
@@ -187,7 +192,7 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
bool MatchesOtherClient(ProfileSyncServiceHarness* partner);
// Logs message with relevant info about client's sync state (if available).
- void LogClientInfo(std::string message);
+ void LogClientInfo(const std::string& message);
// Gets the current progress indicator of the current sync session
// for a particular datatype.
diff --git a/chrome/browser/sync/profile_sync_service_mock.cc b/chrome/browser/sync/profile_sync_service_mock.cc
index bf46922..36b3186 100644
--- a/chrome/browser/sync/profile_sync_service_mock.cc
+++ b/chrome/browser/sync/profile_sync_service_mock.cc
@@ -6,7 +6,9 @@
#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
#include "chrome/common/url_constants.h"
-ProfileSyncServiceMock::ProfileSyncServiceMock() {}
+ProfileSyncServiceMock::ProfileSyncServiceMock()
+ : ProfileSyncService(NULL, NULL, "") {
+}
ProfileSyncServiceMock::~ProfileSyncServiceMock() {
}
diff --git a/chrome/browser/sync/profile_sync_service_mock.h b/chrome/browser/sync/profile_sync_service_mock.h
index 0727ebe..5237897 100644
--- a/chrome/browser/sync/profile_sync_service_mock.h
+++ b/chrome/browser/sync/profile_sync_service_mock.h
@@ -53,6 +53,8 @@ class ProfileSyncServiceMock : public ProfileSyncService {
void(syncable::ModelTypeSet* preferred_types));
MOCK_CONST_METHOD1(GetRegisteredDataTypes,
void(syncable::ModelTypeSet* registered_types));
+ MOCK_CONST_METHOD0(GetLastSessionSnapshot,
+ const browser_sync::sessions::SyncSessionSnapshot*());
MOCK_METHOD0(QueryDetailedSyncStatus,
browser_sync::SyncBackendHost::Status());
diff --git a/chrome/browser/sync/profile_sync_service_password_unittest.cc b/chrome/browser/sync/profile_sync_service_password_unittest.cc
index 0856f5a..16cb99e 100644
--- a/chrome/browser/sync/profile_sync_service_password_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_password_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,7 +6,9 @@
#include "testing/gtest/include/gtest/gtest.h"
+#include "base/synchronization/waitable_event.h"
#include "base/task.h"
+#include "base/test/test_timeouts.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/password_manager/password_store.h"
@@ -28,6 +30,7 @@
#include "chrome/common/pref_names.h"
#include "chrome/test/sync/engine/test_id_factory.h"
#include "chrome/test/profile_mock.h"
+#include "content/browser/browser_thread.h"
#include "content/common/notification_observer_mock.h"
#include "content/common/notification_source.h"
#include "content/common/notification_type.h"
@@ -58,11 +61,13 @@ using syncable::UNIQUE_SERVER_TAG;
using syncable::UNITTEST;
using syncable::WriteTransaction;
using testing::_;
+using testing::AtLeast;
using testing::DoAll;
using testing::DoDefault;
using testing::ElementsAre;
using testing::Eq;
using testing::Invoke;
+using testing::InvokeWithoutArgs;
using testing::Return;
using testing::SaveArg;
using testing::SetArgumentPointee;
@@ -78,6 +83,17 @@ ACTION_P3(MakePasswordSyncComponents, service, ps, dtc) {
change_processor);
}
+ACTION_P(AcquireSyncTransaction, password_test_service) {
+ // Check to make sure we can aquire a transaction (will crash if a transaction
+ // is already held by this thread, deadlock if held by another thread).
+ sync_api::WriteTransaction trans(password_test_service->GetUserShare());
+ VLOG(1) << "Sync transaction acquired.";
+}
+
+static void QuitMessageLoop() {
+ MessageLoop::current()->Quit();
+}
+
class MockPasswordStore : public PasswordStore {
public:
MOCK_METHOD1(RemoveLogin, void(const PasswordForm&));
@@ -115,17 +131,12 @@ class PasswordTestProfileSyncService : public TestProfileSyncService {
virtual ~PasswordTestProfileSyncService() {}
- virtual void OnPassphraseRequired(bool for_decryption) {
- TestProfileSyncService::OnPassphraseRequired(for_decryption);
- ADD_FAILURE();
- }
-
virtual void OnPassphraseAccepted() {
- TestProfileSyncService::OnPassphraseAccepted();
-
if (passphrase_accept_task_) {
passphrase_accept_task_->Run();
}
+
+ TestProfileSyncService::OnPassphraseAccepted();
}
private:
@@ -133,12 +144,17 @@ class PasswordTestProfileSyncService : public TestProfileSyncService {
};
class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
+ public:
+ sync_api::UserShare* GetUserShare() {
+ return service_->GetUserShare();
+ }
protected:
ProfileSyncServicePasswordTest()
: db_thread_(BrowserThread::DB) {
}
virtual void SetUp() {
+ profile_.CreateRequestContext();
password_store_ = new MockPasswordStore();
db_thread_.Start();
@@ -147,38 +163,53 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
registrar_.Add(&observer_,
NotificationType::SYNC_CONFIGURE_DONE,
NotificationService::AllSources());
+ registrar_.Add(&observer_,
+ NotificationType::SYNC_CONFIGURE_BLOCKED,
+ NotificationService::AllSources());
}
virtual void TearDown() {
service_.reset();
notification_service_->TearDown();
db_thread_.Stop();
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile_.ResetRequestContext();
+ }
MessageLoop::current()->RunAllPending();
}
- void StartSyncService(Task* root_task, Task* node_task) {
- StartSyncService(root_task, node_task, 2, 2);
+ static void SignalEvent(base::WaitableEvent* done) {
+ done->Signal();
+ }
+
+ void FlushLastDBTask() {
+ base::WaitableEvent done(false, false);
+ BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
+ NewRunnableFunction(&ProfileSyncServicePasswordTest::SignalEvent,
+ &done));
+ done.TimedWait(base::TimeDelta::FromMilliseconds(
+ TestTimeouts::action_timeout_ms()));
}
- void StartSyncService(Task* root_task, Task* node_task,
- int num_resume_expectations,
- int num_pause_expectations) {
+ void StartSyncService(Task* root_task, Task* node_task) {
if (!service_.get()) {
service_.reset(new PasswordTestProfileSyncService(
&factory_, &profile_, "test_user", false, root_task, node_task));
service_->RegisterPreferences();
profile_.GetPrefs()->SetBoolean(prefs::kSyncPasswords, true);
- service_->set_num_expected_resumes(num_resume_expectations);
- service_->set_num_expected_pauses(num_pause_expectations);
PasswordDataTypeController* data_type_controller =
new PasswordDataTypeController(&factory_,
&profile_,
service_.get());
EXPECT_CALL(factory_, CreatePasswordSyncComponents(_, _, _)).
- WillOnce(MakePasswordSyncComponents(service_.get(),
- password_store_.get(),
- data_type_controller));
+ Times(AtLeast(1)). // Can be more if we hit NEEDS_CRYPTO.
+ WillRepeatedly(MakePasswordSyncComponents(service_.get(),
+ password_store_.get(),
+ data_type_controller));
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
WillOnce(ReturnNewDataTypeManager());
@@ -190,23 +221,23 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
WillRepeatedly(Return(&token_service_));
EXPECT_CALL(profile_, GetPasswordStore(_)).
- Times(3).
+ Times(AtLeast(2)). // Can be more if we hit NEEDS_CRYPTO.
WillRepeatedly(Return(password_store_.get()));
EXPECT_CALL(observer_,
Observe(
NotificationType(NotificationType::SYNC_CONFIGURE_DONE),_,_));
+ EXPECT_CALL(observer_,
+ Observe(
+ NotificationType(
+ NotificationType::SYNC_CONFIGURE_BLOCKED),_,_))
+ .WillOnce(InvokeWithoutArgs(QuitMessageLoop));
service_->RegisterDataTypeController(data_type_controller);
service_->Initialize();
MessageLoop::current()->Run();
+ FlushLastDBTask();
- EXPECT_CALL(
- observer_,
- Observe(
- NotificationType(NotificationType::SYNC_CONFIGURE_DONE),
- _,_)).
- WillOnce(QuitUIMessageLoop());
service_->SetPassphrase("foo", false, true);
MessageLoop::current()->Run();
}
@@ -276,7 +307,6 @@ class ProfileSyncServicePasswordTest : public AbstractProfileSyncServiceTest {
ProfileMock profile_;
scoped_refptr<MockPasswordStore> password_store_;
NotificationRegistrar registrar_;
-
};
class AddPasswordEntriesTask : public Task {
@@ -298,7 +328,7 @@ class AddPasswordEntriesTask : public Task {
};
TEST_F(ProfileSyncServicePasswordTest, FailModelAssociation) {
- StartSyncService(NULL, NULL, 1, 2);
+ StartSyncService(NULL, NULL);
EXPECT_TRUE(service_->unrecoverable_error_detected());
}
@@ -457,6 +487,71 @@ TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncNoMerge) {
EXPECT_TRUE(ComparePasswords(expected_forms[1], new_sync_forms[1]));
}
+// Same as HasNativeHasEmptyNoMerge, but we attempt to aquire a sync transaction
+// every time the password store is accessed.
+TEST_F(ProfileSyncServicePasswordTest, EnsureNoTransactions) {
+ std::vector<PasswordForm*> native_forms;
+ std::vector<PasswordForm> sync_forms;
+ std::vector<PasswordForm> expected_forms;
+ {
+ PasswordForm* new_form = new PasswordForm;
+ new_form->scheme = PasswordForm::SCHEME_HTML;
+ new_form->signon_realm = "pie";
+ new_form->origin = GURL("http://pie.com");
+ new_form->action = GURL("http://pie.com/submit");
+ new_form->username_element = UTF8ToUTF16("name");
+ new_form->username_value = UTF8ToUTF16("tom");
+ new_form->password_element = UTF8ToUTF16("cork");
+ new_form->password_value = UTF8ToUTF16("password1");
+ new_form->ssl_valid = true;
+ new_form->preferred = false;
+ new_form->date_created = base::Time::FromInternalValue(1234);
+ new_form->blacklisted_by_user = false;
+
+ native_forms.push_back(new_form);
+ expected_forms.push_back(*new_form);
+ }
+
+ {
+ PasswordForm new_form;
+ new_form.scheme = PasswordForm::SCHEME_HTML;
+ new_form.signon_realm = "pie2";
+ new_form.origin = GURL("http://pie2.com");
+ new_form.action = GURL("http://pie2.com/submit");
+ new_form.username_element = UTF8ToUTF16("name2");
+ new_form.username_value = UTF8ToUTF16("tom2");
+ new_form.password_element = UTF8ToUTF16("cork2");
+ new_form.password_value = UTF8ToUTF16("password12");
+ new_form.ssl_valid = false;
+ new_form.preferred = true;
+ new_form.date_created = base::Time::FromInternalValue(12345);
+ new_form.blacklisted_by_user = false;
+ sync_forms.push_back(new_form);
+ expected_forms.push_back(new_form);
+ }
+
+ EXPECT_CALL(*password_store_, FillAutofillableLogins(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(native_forms),
+ AcquireSyncTransaction(this),
+ Return(true)));
+ EXPECT_CALL(*password_store_, FillBlacklistLogins(_))
+ .WillOnce(DoAll(AcquireSyncTransaction(this),
+ Return(true)));
+ EXPECT_CALL(*password_store_, AddLoginImpl(_))
+ .WillOnce(AcquireSyncTransaction(this));
+
+ CreateRootTask root_task(this, syncable::PASSWORDS);
+ AddPasswordEntriesTask node_task(this, sync_forms);
+ StartSyncService(&root_task, &node_task);
+
+ std::vector<PasswordForm> new_sync_forms;
+ GetPasswordEntriesFromSyncDB(&new_sync_forms);
+
+ EXPECT_EQ(2U, new_sync_forms.size());
+ EXPECT_TRUE(ComparePasswords(expected_forms[0], new_sync_forms[0]));
+ EXPECT_TRUE(ComparePasswords(expected_forms[1], new_sync_forms[1]));
+}
+
TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncMergeEntry) {
std::vector<PasswordForm*> native_forms;
std::vector<PasswordForm> sync_forms;
diff --git a/chrome/browser/sync/profile_sync_service_preference_unittest.cc b/chrome/browser/sync/profile_sync_service_preference_unittest.cc
index 8c44e17..4a99ba6 100644
--- a/chrome/browser/sync/profile_sync_service_preference_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_preference_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,6 +9,7 @@
#include "base/stl_util-inl.h"
#include "base/string_piece.h"
#include "base/task.h"
+#include "chrome/browser/prefs/scoped_user_pref_update.h"
#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/preference_change_processor.h"
@@ -20,10 +21,10 @@
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/json_value_serializer.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_pref_service.h"
#include "chrome/test/testing_profile.h"
+#include "content/common/json_value_serializer.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -51,7 +52,7 @@ class ProfileSyncServicePreferenceTest
virtual void SetUp() {
profile_.reset(new TestingProfile());
- profile_->set_has_history_service(true);
+ profile_->CreateRequestContext();
prefs_ = profile_->GetTestingPrefService();
prefs_->RegisterStringPref(not_synced_preference_name_.c_str(),
@@ -60,7 +61,12 @@ class ProfileSyncServicePreferenceTest
virtual void TearDown() {
service_.reset();
- profile_.reset();
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile_.reset();
+ }
MessageLoop::current()->RunAllPending();
}
@@ -83,10 +89,9 @@ class ProfileSyncServicePreferenceTest
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
WillOnce(ReturnNewDataTypeManager());
- service_->set_num_expected_resumes(will_fail_association ? 0 : 1);
-
service_->RegisterDataTypeController(
new PreferenceDataTypeController(&factory_,
+ profile_.get(),
service_.get()));
profile_->GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "token");
@@ -119,12 +124,12 @@ class ProfileSyncServicePreferenceTest
return reader.JsonToValue(specifics.value(), false, false);
}
- int64 WriteSyncedValue(sync_api::WriteNode& node,
- const std::string& name,
- const Value& value) {
- if (!PreferenceModelAssociator::WritePreferenceToNode(name, value, &node))
+ int64 WriteSyncedValue(const std::string& name,
+ const Value& value,
+ sync_api::WriteNode* node) {
+ if (!PreferenceModelAssociator::WritePreferenceToNode(name, value, node))
return sync_api::kInvalidId;
- return node.GetId();
+ return node->GetId();
}
int64 SetSyncedValue(const std::string& name, const Value& value) {
@@ -139,11 +144,11 @@ class ProfileSyncServicePreferenceTest
int64 node_id = model_associator_->GetSyncIdFromChromeId(name);
if (node_id == sync_api::kInvalidId) {
if (tag_node.InitByClientTagLookup(syncable::PREFERENCES, name))
- return WriteSyncedValue(tag_node, name, value);
+ return WriteSyncedValue(name, value, &tag_node);
if (node.InitUniqueByCreation(syncable::PREFERENCES, root, name))
- return WriteSyncedValue(node, name, value);
+ return WriteSyncedValue(name, value, &node);
} else if (node.InitByIdLookup(node_id)) {
- return WriteSyncedValue(node, name, value);
+ return WriteSyncedValue(name, value, &node);
}
return sync_api::kInvalidId;
}
@@ -248,9 +253,12 @@ TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationDoNotSyncDefaults) {
TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationEmptyCloud) {
prefs_->SetString(prefs::kHomePage, example_url0_);
- ListValue* url_list = prefs_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- url_list->Append(Value::CreateStringValue(example_url0_));
- url_list->Append(Value::CreateStringValue(example_url1_));
+ {
+ ListPrefUpdate update(prefs_, prefs::kURLsToRestoreOnStartup);
+ ListValue* url_list = update.Get();
+ url_list->Append(Value::CreateStringValue(example_url0_));
+ url_list->Append(Value::CreateStringValue(example_url1_));
+ }
CreateRootTask task(this, syncable::PREFERENCES);
ASSERT_TRUE(StartSyncService(&task, false));
ASSERT_TRUE(task.success());
@@ -266,9 +274,12 @@ TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationEmptyCloud) {
TEST_F(ProfileSyncServicePreferenceTest, ModelAssociationCloudHasData) {
prefs_->SetString(prefs::kHomePage, example_url0_);
- ListValue* url_list = prefs_->GetMutableList(prefs::kURLsToRestoreOnStartup);
- url_list->Append(Value::CreateStringValue(example_url0_));
- url_list->Append(Value::CreateStringValue(example_url1_));
+ {
+ ListPrefUpdate update(prefs_, prefs::kURLsToRestoreOnStartup);
+ ListValue* url_list = update.Get();
+ url_list->Append(Value::CreateStringValue(example_url0_));
+ url_list->Append(Value::CreateStringValue(example_url1_));
+ }
PreferenceValues cloud_data;
cloud_data[prefs::kHomePage] = Value::CreateStringValue(example_url1_);
diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc
index 1570ff3..87aa5ad 100644
--- a/chrome/browser/sync/profile_sync_service_session_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc
@@ -1,13 +1,13 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include <map>
#include <string>
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_temp_dir.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/scoped_temp_dir.h"
#include "base/stl_util-inl.h"
#include "base/task.h"
#include "chrome/browser/sessions/session_service.h"
@@ -18,8 +18,8 @@
#include "chrome/browser/sync/glue/session_data_type_controller.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
+#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/sync/protocol/session_specifics.pb.h"
#include "chrome/browser/sync/protocol/sync.pb.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
@@ -27,14 +27,14 @@
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
-#include "chrome/common/notification_service.h"
#include "chrome/test/browser_with_test_window_test.h"
#include "chrome/test/file_test_utils.h"
#include "chrome/test/profile_mock.h"
-#include "chrome/test/testing_profile.h"
#include "chrome/test/sync/engine/test_id_factory.h"
+#include "chrome/test/testing_profile.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
+#include "content/common/notification_service.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -66,8 +66,7 @@ class ProfileSyncServiceSessionTest
virtual void SetUp() {
// BrowserWithTestWindowTest implementation.
BrowserWithTestWindowTest::SetUp();
-
- profile()->set_has_history_service(true);
+ profile()->CreateRequestContext();
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
SessionService* session_service = new SessionService(temp_dir_.path());
helper_.set_service(session_service);
@@ -94,6 +93,13 @@ class ProfileSyncServiceSessionTest
helper_.set_service(NULL);
profile()->set_session_service(NULL);
sync_service_.reset();
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile()->ResetRequestContext();
+ }
+ MessageLoop::current()->RunAllPending();
}
bool StartSyncService(Task* task, bool will_fail_association) {
@@ -115,9 +121,10 @@ class ProfileSyncServiceSessionTest
model_associator_, change_processor_)));
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
WillOnce(ReturnNewDataTypeManager());
- sync_service_->set_num_expected_resumes(will_fail_association ? 0 : 1);
sync_service_->RegisterDataTypeController(
- new SessionDataTypeController(&factory_, sync_service_.get()));
+ new SessionDataTypeController(&factory_,
+ profile(),
+ sync_service_.get()));
profile()->GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "token");
sync_service_->Initialize();
diff --git a/chrome/browser/sync/profile_sync_service_startup_unittest.cc b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
index 4bc486a..e1b732a 100644
--- a/chrome/browser/sync/profile_sync_service_startup_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_startup_unittest.cc
@@ -1,11 +1,11 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "testing/gtest/include/gtest/gtest.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/sync/glue/data_type_manager.h"
@@ -15,15 +15,16 @@
#include "chrome/browser/sync/test_profile_sync_service.h"
#include "chrome/common/net/gaia/gaia_auth_consumer.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/testing_profile.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
using browser_sync::DataTypeManager;
using browser_sync::DataTypeManagerMock;
using testing::_;
+using testing::AnyNumber;
using testing::DoAll;
using testing::InvokeArgument;
using testing::Mock;
@@ -55,16 +56,22 @@ class ProfileSyncServiceStartupTest : public testing::Test {
}
virtual void SetUp() {
+ profile_.CreateRequestContext();
service_.reset(new TestProfileSyncService(&factory_, &profile_,
"test", true, NULL));
service_->AddObserver(&observer_);
- service_->set_num_expected_resumes(0);
- service_->set_num_expected_pauses(0);
service_->set_synchronous_sync_configuration();
}
virtual void TearDown() {
service_->RemoveObserver(&observer_);
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile_.ResetRequestContext();
+ }
+ MessageLoop::current()->RunAllPending();
}
protected:
@@ -92,7 +99,7 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) {
// Should not actually start, rather just clean things up and wait
// to be enabled.
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
service_->Initialize();
// Preferences should be back to defaults.
@@ -101,11 +108,11 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) {
Mock::VerifyAndClearExpectations(data_type_manager);
// Then start things up.
- EXPECT_CALL(*data_type_manager, Configure(_)).Times(2);
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(3);
EXPECT_CALL(*data_type_manager, state()).
WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged()).Times(5);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
// Create some tokens in the token service; the service will startup when
// it is notified that tokens are available.
@@ -119,12 +126,12 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFirstTime)) {
TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartNormal)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Configure(_)).Times(1);
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(2);
EXPECT_CALL(*data_type_manager, state()).
WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged()).Times(3);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
// Pre load the tokens
profile_.GetTokenService()->IssueAuthTokenForTest(
@@ -137,7 +144,7 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(ManagedStartup)) {
profile_.GetPrefs()->SetBoolean(prefs::kSyncManaged, true);
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).Times(0);
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
// Service should not be started by Initialize() since it's managed.
profile_.GetTokenService()->IssueAuthTokenForTest(
@@ -147,8 +154,8 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(ManagedStartup)) {
TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(SwitchManaged)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Configure(_)).Times(1);
- EXPECT_CALL(observer_, OnStateChanged()).Times(3);
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(2);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
profile_.GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "sync_token");
@@ -159,21 +166,21 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(SwitchManaged)) {
EXPECT_CALL(*data_type_manager, state()).
WillOnce(Return(DataTypeManager::CONFIGURED));
EXPECT_CALL(*data_type_manager, Stop()).Times(1);
- EXPECT_CALL(observer_, OnStateChanged()).Times(2);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
profile_.GetPrefs()->SetBoolean(prefs::kSyncManaged, true);
// When switching back to unmanaged, the state should change, but the service
// should not start up automatically (kSyncSetupCompleted will be false).
Mock::VerifyAndClearExpectations(data_type_manager);
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).Times(0);
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
profile_.GetPrefs()->ClearPref(prefs::kSyncManaged);
}
TEST_F(ProfileSyncServiceStartupTest, ClearServerData) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- EXPECT_CALL(*data_type_manager, Configure(_)).Times(1);
- EXPECT_CALL(observer_, OnStateChanged()).Times(3);
+ EXPECT_CALL(*data_type_manager, Configure(_)).Times(2);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
profile_.GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "sync_token");
@@ -187,12 +194,12 @@ TEST_F(ProfileSyncServiceStartupTest, ClearServerData) {
EXPECT_TRUE(ProfileSyncService::CLEAR_NOT_STARTED ==
service_->GetClearServerDataState());
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
service_->OnClearServerDataFailed();
EXPECT_TRUE(ProfileSyncService::CLEAR_FAILED ==
service_->GetClearServerDataState());
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
service_->OnClearServerDataSucceeded();
EXPECT_TRUE(ProfileSyncService::CLEAR_SUCCEEDED ==
service_->GetClearServerDataState());
@@ -206,12 +213,12 @@ TEST_F(ProfileSyncServiceStartupTest, ClearServerData) {
EXPECT_TRUE(ProfileSyncService::CLEAR_NOT_STARTED ==
service_->GetClearServerDataState());
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
service_->OnClearServerDataTimeout();
EXPECT_TRUE(ProfileSyncService::CLEAR_FAILED ==
service_->GetClearServerDataState());
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
service_->OnClearServerDataSucceeded();
EXPECT_TRUE(ProfileSyncService::CLEAR_SUCCEEDED ==
service_->GetClearServerDataState());
@@ -230,17 +237,19 @@ TEST_F(ProfileSyncServiceStartupTest, ClearServerData) {
service_->GetClearServerDataState());
// Stop the timer and reset the state
- EXPECT_CALL(observer_, OnStateChanged()).Times(1);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
service_->OnClearServerDataSucceeded();
service_->ResetClearServerDataState();
}
TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFailure)) {
DataTypeManagerMock* data_type_manager = SetUpDataTypeManager();
- DataTypeManager::ConfigureResult result =
+ DataTypeManager::ConfigureResult configure_result =
DataTypeManager::ASSOCIATION_FAILED;
+ browser_sync::DataTypeManager::ConfigureResultWithErrorLocation result(
+ configure_result, FROM_HERE, syncable::ModelTypeSet());
EXPECT_CALL(*data_type_manager, Configure(_)).
- WillOnce(DoAll(NotifyFromDataTypeManager(data_type_manager,
+ WillRepeatedly(DoAll(NotifyFromDataTypeManager(data_type_manager,
NotificationType::SYNC_CONFIGURE_START),
NotifyFromDataTypeManagerWithResult(data_type_manager,
NotificationType::SYNC_CONFIGURE_DONE,
@@ -248,7 +257,7 @@ TEST_F(ProfileSyncServiceStartupTest, SKIP_MACOSX(StartFailure)) {
EXPECT_CALL(*data_type_manager, state()).
WillOnce(Return(DataTypeManager::STOPPED));
- EXPECT_CALL(observer_, OnStateChanged()).Times(3);
+ EXPECT_CALL(observer_, OnStateChanged()).Times(AnyNumber());
profile_.GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "sync_token");
diff --git a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
index 4e9b98d..7125cce 100644
--- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,7 +6,7 @@
#include "testing/gtest/include/gtest/gtest.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "base/string16.h"
#include "base/threading/thread.h"
#include "base/time.h"
@@ -28,10 +28,10 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_service.h"
#include "chrome/test/profile_mock.h"
#include "chrome/test/sync/engine/test_id_factory.h"
#include "chrome/test/testing_profile.h"
+#include "content/common/notification_service.h"
#include "googleurl/src/gurl.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -130,6 +130,7 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest {
}
virtual void SetUp() {
+ profile_.CreateRequestContext();
history_backend_ = new HistoryBackendMock();
history_service_ = new HistoryServiceMock();
EXPECT_CALL((*history_service_.get()), ScheduleDBTask(_, _))
@@ -148,6 +149,12 @@ class ProfileSyncServiceTypedUrlTest : public AbstractProfileSyncServiceTest {
service_.reset();
notification_service_->TearDown();
history_thread_.Stop();
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile_.ResetRequestContext();
+ }
MessageLoop::current()->RunAllPending();
}
diff --git a/chrome/browser/sync/profile_sync_service_unittest.cc b/chrome/browser/sync/profile_sync_service_unittest.cc
index 5226715..376eea1 100644
--- a/chrome/browser/sync/profile_sync_service_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_unittest.cc
@@ -1,305 +1,45 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
-#include <stack>
-#include <vector>
-
-#include "testing/gtest/include/gtest/gtest.h"
#include "base/file_util.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
-#include "base/string16.h"
-#include "base/utf_string_conversions.h"
#include "base/values.h"
-#include "chrome/browser/bookmarks/bookmark_model.h"
#include "chrome/browser/net/gaia/token_service.h"
-#include "chrome/browser/prefs/pref_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
-#include "chrome/browser/sync/engine/syncapi.h"
-#include "chrome/browser/sync/glue/change_processor.h"
-#include "chrome/browser/sync/glue/bookmark_change_processor.h"
#include "chrome/browser/sync/glue/bookmark_data_type_controller.h"
-#include "chrome/browser/sync/glue/bookmark_model_associator.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
-#include "chrome/browser/sync/glue/model_associator.h"
-#include "chrome/browser/sync/glue/sync_backend_host.h"
-#include "chrome/browser/sync/glue/sync_backend_host_mock.h"
#include "chrome/browser/sync/js_arg_list.h"
#include "chrome/browser/sync/js_test_util.h"
-#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
-#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/common/net/gaia/gaia_constants.h"
#include "chrome/common/pref_names.h"
-#include "chrome/test/testing_profile.h"
#include "chrome/test/testing_pref_service.h"
+#include "chrome/test/testing_profile.h"
#include "content/browser/browser_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// TODO(akalin): Add tests here that exercise the whole
+// ProfileSyncService/SyncBackendHost stack while mocking out as
+// little as possible.
+
+namespace browser_sync {
+
+namespace {
-using std::vector;
-using browser_sync::AssociatorInterface;
-using browser_sync::BookmarkChangeProcessor;
-using browser_sync::BookmarkModelAssociator;
-using browser_sync::ChangeProcessor;
-using browser_sync::DataTypeController;
-using browser_sync::HasArgs;
-using browser_sync::JsArgList;
-using browser_sync::MockJsEventHandler;
-using browser_sync::ModelAssociator;
-using browser_sync::SyncBackendHost;
-using browser_sync::SyncBackendHostMock;
-using browser_sync::UnrecoverableErrorHandler;
using testing::_;
+using testing::AtLeast;
using testing::AtMost;
using testing::Return;
using testing::StrictMock;
-using testing::WithArg;
-using testing::Invoke;
-
-// TODO(akalin): Bookmark-specific tests should be moved into their
-// own file.
-class TestBookmarkModelAssociator : public BookmarkModelAssociator {
- public:
- TestBookmarkModelAssociator(
- TestProfileSyncService* service,
- UnrecoverableErrorHandler* persist_ids_error_handler)
- : BookmarkModelAssociator(service, persist_ids_error_handler),
- id_factory_(service->id_factory()) {}
-
- // TODO(akalin): This logic lazily creates any tagged node that is
- // requested. A better way would be to have utility functions to
- // create sync nodes from some bookmark structure and to use that.
- virtual bool GetSyncIdForTaggedNode(const std::string& tag, int64* sync_id) {
- std::wstring tag_wide;
- if (!UTF8ToWide(tag.c_str(), tag.length(), &tag_wide)) {
- NOTREACHED() << "Unable to convert UTF8 to wide for string: " << tag;
- return false;
- }
-
- bool root_exists = false;
- syncable::ModelType type = model_type();
- {
- sync_api::WriteTransaction trans(sync_service()->GetUserShare());
- sync_api::ReadNode uber_root(&trans);
- uber_root.InitByRootLookup();
-
- sync_api::ReadNode root(&trans);
- root_exists = root.InitByTagLookup(
- ProfileSyncServiceTestHelper::GetTagForType(type));
- }
-
- if (!root_exists) {
- bool created = ProfileSyncServiceTestHelper::CreateRoot(
- type,
- sync_service()->GetUserShare(),
- id_factory_);
- if (!created)
- return false;
- }
-
- sync_api::WriteTransaction trans(sync_service()->GetUserShare());
- sync_api::ReadNode root(&trans);
- EXPECT_TRUE(root.InitByTagLookup(
- ProfileSyncServiceTestHelper::GetTagForType(type)));
-
- // First, try to find a node with the title among the root's children.
- // This will be the case if we are testing model persistence, and
- // are reloading a sync repository created earlier in the test.
- int64 last_child_id = sync_api::kInvalidId;
- for (int64 id = root.GetFirstChildId(); id != sync_api::kInvalidId; /***/) {
- sync_api::ReadNode child(&trans);
- child.InitByIdLookup(id);
- last_child_id = id;
- if (tag_wide == child.GetTitle()) {
- *sync_id = id;
- return true;
- }
- id = child.GetSuccessorId();
- }
-
- sync_api::ReadNode predecessor_node(&trans);
- sync_api::ReadNode* predecessor = NULL;
- if (last_child_id != sync_api::kInvalidId) {
- predecessor_node.InitByIdLookup(last_child_id);
- predecessor = &predecessor_node;
- }
- sync_api::WriteNode node(&trans);
- // Create new fake tagged nodes at the end of the ordering.
- node.InitByCreation(type, root, predecessor);
- node.SetIsFolder(true);
- node.SetTitle(tag_wide);
- node.SetExternalId(0);
- *sync_id = node.GetId();
- return true;
- }
-
- private:
- browser_sync::TestIdFactory* id_factory_;
-};
-
-// FakeServerChange constructs a list of sync_api::ChangeRecords while modifying
-// the sync model, and can pass the ChangeRecord list to a
-// sync_api::SyncObserver (i.e., the ProfileSyncService) to test the client
-// change-application behavior.
-// Tests using FakeServerChange should be careful to avoid back-references,
-// since FakeServerChange will send the edits in the order specified.
-class FakeServerChange {
- public:
- explicit FakeServerChange(sync_api::WriteTransaction* trans) : trans_(trans) {
- }
-
- // Pretend that the server told the syncer to add a bookmark object.
- int64 Add(const std::wstring& title,
- const std::string& url,
- bool is_folder,
- int64 parent_id,
- int64 predecessor_id) {
- sync_api::ReadNode parent(trans_);
- EXPECT_TRUE(parent.InitByIdLookup(parent_id));
- sync_api::WriteNode node(trans_);
- if (predecessor_id == 0) {
- EXPECT_TRUE(node.InitByCreation(syncable::BOOKMARKS, parent, NULL));
- } else {
- sync_api::ReadNode predecessor(trans_);
- EXPECT_TRUE(predecessor.InitByIdLookup(predecessor_id));
- EXPECT_EQ(predecessor.GetParentId(), parent.GetId());
- EXPECT_TRUE(node.InitByCreation(syncable::BOOKMARKS, parent,
- &predecessor));
- }
- EXPECT_EQ(node.GetPredecessorId(), predecessor_id);
- EXPECT_EQ(node.GetParentId(), parent_id);
- node.SetIsFolder(is_folder);
- node.SetTitle(title);
- if (!is_folder)
- node.SetURL(GURL(url));
- sync_api::SyncManager::ChangeRecord record;
- record.action = sync_api::SyncManager::ChangeRecord::ACTION_ADD;
- record.id = node.GetId();
- changes_.push_back(record);
- return node.GetId();
- }
-
- // Add a bookmark folder.
- int64 AddFolder(const std::wstring& title,
- int64 parent_id,
- int64 predecessor_id) {
- return Add(title, std::string(), true, parent_id, predecessor_id);
- }
-
- // Add a bookmark.
- int64 AddURL(const std::wstring& title,
- const std::string& url,
- int64 parent_id,
- int64 predecessor_id) {
- return Add(title, url, false, parent_id, predecessor_id);
- }
-
- // Pretend that the server told the syncer to delete an object.
- void Delete(int64 id) {
- {
- // Delete the sync node.
- sync_api::WriteNode node(trans_);
- EXPECT_TRUE(node.InitByIdLookup(id));
- EXPECT_FALSE(node.GetFirstChildId());
- node.Remove();
- }
- {
- // Verify the deletion.
- sync_api::ReadNode node(trans_);
- EXPECT_FALSE(node.InitByIdLookup(id));
- }
-
- sync_api::SyncManager::ChangeRecord record;
- record.action = sync_api::SyncManager::ChangeRecord::ACTION_DELETE;
- record.id = id;
- // Deletions are always first in the changelist, but we can't actually do
- // WriteNode::Remove() on the node until its children are moved. So, as
- // a practical matter, users of FakeServerChange must move or delete
- // children before parents. Thus, we must insert the deletion record
- // at the front of the vector.
- changes_.insert(changes_.begin(), record);
- }
-
- // Set a new title value, and return the old value.
- std::wstring ModifyTitle(int64 id, const std::wstring& new_title) {
- sync_api::WriteNode node(trans_);
- EXPECT_TRUE(node.InitByIdLookup(id));
- std::wstring old_title = node.GetTitle();
- node.SetTitle(new_title);
- SetModified(id);
- return old_title;
- }
-
- // Set a new parent and predecessor value. Return the old parent id.
- // We could return the old predecessor id, but it turns out not to be
- // very useful for assertions.
- int64 ModifyPosition(int64 id, int64 parent_id, int64 predecessor_id) {
- sync_api::ReadNode parent(trans_);
- EXPECT_TRUE(parent.InitByIdLookup(parent_id));
- sync_api::WriteNode node(trans_);
- EXPECT_TRUE(node.InitByIdLookup(id));
- int64 old_parent_id = node.GetParentId();
- if (predecessor_id == 0) {
- EXPECT_TRUE(node.SetPosition(parent, NULL));
- } else {
- sync_api::ReadNode predecessor(trans_);
- EXPECT_TRUE(predecessor.InitByIdLookup(predecessor_id));
- EXPECT_EQ(predecessor.GetParentId(), parent.GetId());
- EXPECT_TRUE(node.SetPosition(parent, &predecessor));
- }
- SetModified(id);
- return old_parent_id;
- }
-
- // Pass the fake change list to |service|.
- void ApplyPendingChanges(browser_sync::ChangeProcessor* processor) {
- processor->ApplyChangesFromSyncModel(trans_,
- changes_.size() ? &changes_[0] : NULL, changes_.size());
- }
-
- const vector<sync_api::SyncManager::ChangeRecord>& changes() {
- return changes_;
- }
-
- private:
- // Helper function to push an ACTION_UPDATE record onto the back
- // of the changelist.
- void SetModified(int64 id) {
- // Coalesce multi-property edits.
- if (!changes_.empty() && changes_.back().id == id &&
- changes_.back().action ==
- sync_api::SyncManager::ChangeRecord::ACTION_UPDATE)
- return;
- sync_api::SyncManager::ChangeRecord record;
- record.action = sync_api::SyncManager::ChangeRecord::ACTION_UPDATE;
- record.id = id;
- changes_.push_back(record);
- }
-
- // The transaction on which everything happens.
- sync_api::WriteTransaction *trans_;
-
- // The change list we construct.
- vector<sync_api::SyncManager::ChangeRecord> changes_;
-};
class ProfileSyncServiceTest : public testing::Test {
protected:
- enum LoadOption { LOAD_FROM_STORAGE, DELETE_EXISTING_STORAGE };
- enum SaveOption { SAVE_TO_STORAGE, DONT_SAVE_TO_STORAGE };
ProfileSyncServiceTest()
- : ui_thread_(BrowserThread::UI, &message_loop_),
- file_thread_(BrowserThread::FILE, &message_loop_),
- model_(NULL),
- model_associator_(NULL),
- change_processor_(NULL) {
+ : ui_thread_(BrowserThread::UI, &message_loop_) {
profile_.reset(new TestingProfile());
- profile_->set_has_history_service(true);
}
virtual ~ProfileSyncServiceTest() {
// Kill the service before the profile.
@@ -309,21 +49,30 @@ class ProfileSyncServiceTest : public testing::Test {
// Ensure that the sync objects destruct to avoid memory leaks.
MessageLoop::current()->RunAllPending();
}
-
- BookmarkModelAssociator* associator() {
- return model_associator_;
+ virtual void SetUp() {
+ profile_->CreateRequestContext();
}
-
- BookmarkChangeProcessor* change_processor() {
- return change_processor_;
+ virtual void TearDown() {
+ {
+ // The request context gets deleted on the I/O thread. To prevent a leak
+ // supply one here.
+ BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
+ profile_->ResetRequestContext();
+ }
+ MessageLoop::current()->RunAllPending();
}
+ // TODO(akalin): Refactor the StartSyncService*() functions below.
+
void StartSyncService() {
- StartSyncServiceAndSetInitialSyncEnded(true, true);
+ StartSyncServiceAndSetInitialSyncEnded(true, true, false, true);
}
+
void StartSyncServiceAndSetInitialSyncEnded(
bool set_initial_sync_ended,
- bool issue_auth_token) {
+ bool issue_auth_token,
+ bool synchronous_sync_configuration,
+ bool sync_setup_completed) {
if (!service_.get()) {
// Set bootstrap to true and it will provide a logged in user for test
service_.reset(new TestProfileSyncService(&factory_,
@@ -331,23 +80,15 @@ class ProfileSyncServiceTest : public testing::Test {
"test", true, NULL));
if (!set_initial_sync_ended)
service_->dont_set_initial_sync_ended_on_init();
+ if (synchronous_sync_configuration)
+ service_->set_synchronous_sync_configuration();
+ if (!sync_setup_completed)
+ profile_->GetPrefs()->SetBoolean(prefs::kSyncHasSetupCompleted, false);
// Register the bookmark data type.
- model_associator_ = new TestBookmarkModelAssociator(service_.get(),
- service_.get());
- change_processor_ = new BookmarkChangeProcessor(model_associator_,
- service_.get());
- EXPECT_CALL(factory_, CreateBookmarkSyncComponents(_, _)).
- WillOnce(Return(ProfileSyncFactory::SyncComponents(
- model_associator_, change_processor_)));
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
WillOnce(ReturnNewDataTypeManager());
- service_->RegisterDataTypeController(
- new browser_sync::BookmarkDataTypeController(&factory_,
- profile_.get(),
- service_.get()));
-
if (issue_auth_token) {
profile_->GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "token");
@@ -356,162 +97,6 @@ class ProfileSyncServiceTest : public testing::Test {
}
}
- void StopSyncService(SaveOption save) {
- if (save == DONT_SAVE_TO_STORAGE)
- service_->DisableForUser();
- service_.reset();
- }
-
- // Load (or re-load) the bookmark model. |load| controls use of the
- // bookmarks file on disk. |save| controls whether the newly loaded
- // bookmark model will write out a bookmark file as it goes.
- void LoadBookmarkModel(LoadOption load, SaveOption save) {
- bool delete_bookmarks = load == DELETE_EXISTING_STORAGE;
- profile_->CreateBookmarkModel(delete_bookmarks);
- model_ = profile_->GetBookmarkModel();
- // Wait for the bookmarks model to load.
- profile_->BlockUntilBookmarkModelLoaded();
- // This noticeably speeds up the unit tests that request it.
- if (save == DONT_SAVE_TO_STORAGE)
- model_->ClearStore();
- }
-
- void ExpectSyncerNodeMatching(sync_api::BaseTransaction* trans,
- const BookmarkNode* bnode) {
- sync_api::ReadNode gnode(trans);
- EXPECT_TRUE(associator()->InitSyncNodeFromChromeId(bnode->id(), &gnode));
- // Non-root node titles and parents must match.
- if (bnode != model_->GetBookmarkBarNode() &&
- bnode != model_->other_node()) {
- EXPECT_EQ(bnode->GetTitle(), WideToUTF16Hack(gnode.GetTitle()));
- EXPECT_EQ(associator()->GetChromeNodeFromSyncId(gnode.GetParentId()),
- bnode->GetParent());
- }
- EXPECT_EQ(bnode->is_folder(), gnode.GetIsFolder());
- if (bnode->is_url())
- EXPECT_EQ(bnode->GetURL(), gnode.GetURL());
-
- // Check for position matches.
- int browser_index = bnode->GetParent()->IndexOfChild(bnode);
- if (browser_index == 0) {
- EXPECT_EQ(gnode.GetPredecessorId(), 0);
- } else {
- const BookmarkNode* bprev =
- bnode->GetParent()->GetChild(browser_index - 1);
- sync_api::ReadNode gprev(trans);
- ASSERT_TRUE(associator()->InitSyncNodeFromChromeId(bprev->id(),
- &gprev));
- EXPECT_EQ(gnode.GetPredecessorId(), gprev.GetId());
- EXPECT_EQ(gnode.GetParentId(), gprev.GetParentId());
- }
- if (browser_index == bnode->GetParent()->GetChildCount() - 1) {
- EXPECT_EQ(gnode.GetSuccessorId(), 0);
- } else {
- const BookmarkNode* bnext =
- bnode->GetParent()->GetChild(browser_index + 1);
- sync_api::ReadNode gnext(trans);
- ASSERT_TRUE(associator()->InitSyncNodeFromChromeId(bnext->id(),
- &gnext));
- EXPECT_EQ(gnode.GetSuccessorId(), gnext.GetId());
- EXPECT_EQ(gnode.GetParentId(), gnext.GetParentId());
- }
- if (bnode->GetChildCount()) {
- EXPECT_TRUE(gnode.GetFirstChildId());
- }
- }
-
- void ExpectSyncerNodeMatching(const BookmarkNode* bnode) {
- sync_api::ReadTransaction trans(service_->GetUserShare());
- ExpectSyncerNodeMatching(&trans, bnode);
- }
-
- void ExpectBrowserNodeMatching(sync_api::BaseTransaction* trans,
- int64 sync_id) {
- EXPECT_TRUE(sync_id);
- const BookmarkNode* bnode =
- associator()->GetChromeNodeFromSyncId(sync_id);
- ASSERT_TRUE(bnode);
- int64 id = associator()->GetSyncIdFromChromeId(bnode->id());
- EXPECT_EQ(id, sync_id);
- ExpectSyncerNodeMatching(trans, bnode);
- }
-
- void ExpectBrowserNodeUnknown(int64 sync_id) {
- EXPECT_FALSE(associator()->GetChromeNodeFromSyncId(sync_id));
- }
-
- void ExpectBrowserNodeKnown(int64 sync_id) {
- EXPECT_TRUE(associator()->GetChromeNodeFromSyncId(sync_id));
- }
-
- void ExpectSyncerNodeKnown(const BookmarkNode* node) {
- int64 sync_id = associator()->GetSyncIdFromChromeId(node->id());
- EXPECT_NE(sync_id, sync_api::kInvalidId);
- }
-
- void ExpectSyncerNodeUnknown(const BookmarkNode* node) {
- int64 sync_id = associator()->GetSyncIdFromChromeId(node->id());
- EXPECT_EQ(sync_id, sync_api::kInvalidId);
- }
-
- void ExpectBrowserNodeTitle(int64 sync_id, const std::wstring& title) {
- const BookmarkNode* bnode =
- associator()->GetChromeNodeFromSyncId(sync_id);
- ASSERT_TRUE(bnode);
- EXPECT_EQ(bnode->GetTitle(), WideToUTF16Hack(title));
- }
-
- void ExpectBrowserNodeURL(int64 sync_id, const std::string& url) {
- const BookmarkNode* bnode =
- associator()->GetChromeNodeFromSyncId(sync_id);
- ASSERT_TRUE(bnode);
- EXPECT_EQ(GURL(url), bnode->GetURL());
- }
-
- void ExpectBrowserNodeParent(int64 sync_id, int64 parent_sync_id) {
- const BookmarkNode* node = associator()->GetChromeNodeFromSyncId(sync_id);
- ASSERT_TRUE(node);
- const BookmarkNode* parent =
- associator()->GetChromeNodeFromSyncId(parent_sync_id);
- EXPECT_TRUE(parent);
- EXPECT_EQ(node->GetParent(), parent);
- }
-
- void ExpectModelMatch(sync_api::BaseTransaction* trans) {
- const BookmarkNode* root = model_->root_node();
- EXPECT_EQ(root->IndexOfChild(model_->GetBookmarkBarNode()), 0);
- EXPECT_EQ(root->IndexOfChild(model_->other_node()), 1);
-
- std::stack<int64> stack;
- stack.push(bookmark_bar_id());
- while (!stack.empty()) {
- int64 id = stack.top();
- stack.pop();
- if (!id) continue;
-
- ExpectBrowserNodeMatching(trans, id);
-
- sync_api::ReadNode gnode(trans);
- ASSERT_TRUE(gnode.InitByIdLookup(id));
- stack.push(gnode.GetFirstChildId());
- stack.push(gnode.GetSuccessorId());
- }
- }
-
- void ExpectModelMatch() {
- sync_api::ReadTransaction trans(service_->GetUserShare());
- ExpectModelMatch(&trans);
- }
-
- int64 other_bookmarks_id() {
- return associator()->GetSyncIdFromChromeId(model_->other_node()->id());
- }
-
- int64 bookmark_bar_id() {
- return associator()->GetSyncIdFromChromeId(
- model_->GetBookmarkBarNode()->id());
- }
-
// This serves as the "UI loop" on which the ProfileSyncService lives and
// operates. It is needed because the SyncBackend can post tasks back to
// the service, meaning it can't be null. It doesn't have to be running,
@@ -521,30 +106,20 @@ class ProfileSyncServiceTest : public testing::Test {
// and caller until the task is run).
MessageLoop message_loop_;
BrowserThread ui_thread_;
- BrowserThread file_thread_;
scoped_ptr<TestProfileSyncService> service_;
scoped_ptr<TestingProfile> profile_;
ProfileSyncFactoryMock factory_;
- BookmarkModel* model_;
- TestBookmarkModelAssociator* model_associator_;
- BookmarkChangeProcessor* change_processor_;
};
TEST_F(ProfileSyncServiceTest, InitialState) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
+ service_.reset(new TestProfileSyncService(&factory_, profile_.get(),
+ "", true, NULL));
EXPECT_TRUE(
- service_->sync_service_url_.spec() ==
+ service_->sync_service_url().spec() ==
ProfileSyncService::kSyncServerUrl ||
- service_->sync_service_url_.spec() ==
+ service_->sync_service_url().spec() ==
ProfileSyncService::kDevServerUrl);
-
- EXPECT_TRUE(other_bookmarks_id());
- EXPECT_TRUE(bookmark_bar_id());
-
- ExpectModelMatch();
}
TEST_F(ProfileSyncServiceTest, DisabledByPolicy) {
@@ -560,412 +135,27 @@ TEST_F(ProfileSyncServiceTest, DisabledByPolicy) {
TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
service_.reset(new TestProfileSyncService(&factory_, profile_.get(),
"test", true, NULL));
- service_->set_num_expected_resumes(0);
EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).Times(0);
EXPECT_CALL(factory_, CreateBookmarkSyncComponents(_, _)).Times(0);
service_->RegisterDataTypeController(
- new browser_sync::BookmarkDataTypeController(&factory_,
- profile_.get(),
- service_.get()));
+ new BookmarkDataTypeController(&factory_,
+ profile_.get(),
+ service_.get()));
service_->Initialize();
service_.reset();
}
-
-TEST_F(ProfileSyncServiceTest, BookmarkModelOperations) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- // Test addition.
- const BookmarkNode* folder =
- model_->AddGroup(model_->other_node(), 0, ASCIIToUTF16("foobar"));
- ExpectSyncerNodeMatching(folder);
- ExpectModelMatch();
- const BookmarkNode* folder2 =
- model_->AddGroup(folder, 0, ASCIIToUTF16("nested"));
- ExpectSyncerNodeMatching(folder2);
- ExpectModelMatch();
- const BookmarkNode* url1 = model_->AddURL(
- folder, 0, ASCIIToUTF16("Internets #1 Pies Site"),
- GURL("http://www.easypie.com/"));
- ExpectSyncerNodeMatching(url1);
- ExpectModelMatch();
- const BookmarkNode* url2 = model_->AddURL(
- folder, 1, ASCIIToUTF16("Airplanes"), GURL("http://www.easyjet.com/"));
- ExpectSyncerNodeMatching(url2);
- ExpectModelMatch();
-
- // Test modification.
- model_->SetTitle(url2, ASCIIToUTF16("EasyJet"));
- ExpectModelMatch();
- model_->Move(url1, folder2, 0);
- ExpectModelMatch();
- model_->Move(folder2, model_->GetBookmarkBarNode(), 0);
- ExpectModelMatch();
- model_->SetTitle(folder2, ASCIIToUTF16("Not Nested"));
- ExpectModelMatch();
- model_->Move(folder, folder2, 0);
- ExpectModelMatch();
- model_->SetTitle(folder, ASCIIToUTF16("who's nested now?"));
- ExpectModelMatch();
- model_->Copy(url2, model_->GetBookmarkBarNode(), 0);
- ExpectModelMatch();
-
- // Test deletion.
- // Delete a single item.
- model_->Remove(url2->GetParent(), url2->GetParent()->IndexOfChild(url2));
- ExpectModelMatch();
- // Delete an item with several children.
- model_->Remove(folder2->GetParent(),
- folder2->GetParent()->IndexOfChild(folder2));
- ExpectModelMatch();
-}
-
-TEST_F(ProfileSyncServiceTest, ServerChangeProcessing) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- sync_api::WriteTransaction trans(service_->GetUserShare());
-
- FakeServerChange adds(&trans);
- int64 f1 = adds.AddFolder(L"Server Folder B", bookmark_bar_id(), 0);
- int64 f2 = adds.AddFolder(L"Server Folder A", bookmark_bar_id(), f1);
- int64 u1 = adds.AddURL(L"Some old site", "ftp://nifty.andrew.cmu.edu/",
- bookmark_bar_id(), f2);
- int64 u2 = adds.AddURL(L"Nifty", "ftp://nifty.andrew.cmu.edu/", f1, 0);
- // u3 is a duplicate URL
- int64 u3 = adds.AddURL(L"Nifty2", "ftp://nifty.andrew.cmu.edu/", f1, u2);
- // u4 is a duplicate title, different URL.
- adds.AddURL(L"Some old site", "http://slog.thestranger.com/",
- bookmark_bar_id(), u1);
- // u5 tests an empty-string title.
- std::string javascript_url(
- "javascript:(function(){var w=window.open(" \
- "'about:blank','gnotesWin','location=0,menubar=0," \
- "scrollbars=0,status=0,toolbar=0,width=300," \
- "height=300,resizable');});");
- adds.AddURL(L"", javascript_url, other_bookmarks_id(), 0);
-
- vector<sync_api::SyncManager::ChangeRecord>::const_iterator it;
- // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
- for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
- ExpectBrowserNodeUnknown(it->id);
-
- adds.ApplyPendingChanges(change_processor());
-
- // Make sure the bookmark model received all of the nodes in |adds|.
- for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
- ExpectBrowserNodeMatching(&trans, it->id);
- ExpectModelMatch(&trans);
-
- // Part two: test modifications.
- FakeServerChange mods(&trans);
- // Mess with u2, and move it into empty folder f2
- // TODO(ncarter): Determine if we allow ModifyURL ops or not.
- /* std::wstring u2_old_url = mods.ModifyURL(u2, L"http://www.google.com"); */
- std::wstring u2_old_title = mods.ModifyTitle(u2, L"The Google");
- int64 u2_old_parent = mods.ModifyPosition(u2, f2, 0);
-
- // Now move f1 after u2.
- std::wstring f1_old_title = mods.ModifyTitle(f1, L"Server Folder C");
- int64 f1_old_parent = mods.ModifyPosition(f1, f2, u2);
-
- // Then add u3 after f1.
- int64 u3_old_parent = mods.ModifyPosition(u3, f2, f1);
-
- // Test that the property changes have not yet taken effect.
- ExpectBrowserNodeTitle(u2, u2_old_title);
- /* ExpectBrowserNodeURL(u2, u2_old_url); */
- ExpectBrowserNodeParent(u2, u2_old_parent);
-
- ExpectBrowserNodeTitle(f1, f1_old_title);
- ExpectBrowserNodeParent(f1, f1_old_parent);
-
- ExpectBrowserNodeParent(u3, u3_old_parent);
-
- // Apply the changes.
- mods.ApplyPendingChanges(change_processor());
-
- // Check for successful application.
- for (it = mods.changes().begin(); it != mods.changes().end(); ++it)
- ExpectBrowserNodeMatching(&trans, it->id);
- ExpectModelMatch(&trans);
-
- // Part 3: Test URL deletion.
- FakeServerChange dels(&trans);
- dels.Delete(u2);
- dels.Delete(u3);
-
- ExpectBrowserNodeKnown(u2);
- ExpectBrowserNodeKnown(u3);
-
- dels.ApplyPendingChanges(change_processor());
-
- ExpectBrowserNodeUnknown(u2);
- ExpectBrowserNodeUnknown(u3);
- ExpectModelMatch(&trans);
-}
-
-// Tests a specific case in ApplyModelChanges where we move the
-// children out from under a parent, and then delete the parent
-// in the same changelist. The delete shows up first in the changelist,
-// requiring the children to be moved to a temporary location.
-TEST_F(ProfileSyncServiceTest, ServerChangeRequiringFosterParent) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- sync_api::WriteTransaction trans(service_->GetUserShare());
-
- // Stress the immediate children of other_node because that's where
- // ApplyModelChanges puts a temporary foster parent node.
- std::string url("http://dev.chromium.org/");
- FakeServerChange adds(&trans);
- int64 f0 = other_bookmarks_id(); // + other_node
- int64 f1 = adds.AddFolder(L"f1", f0, 0); // + f1
- int64 f2 = adds.AddFolder(L"f2", f1, 0); // + f2
- int64 u3 = adds.AddURL( L"u3", url, f2, 0); // + u3 NOLINT
- int64 u4 = adds.AddURL( L"u4", url, f2, u3); // + u4 NOLINT
- int64 u5 = adds.AddURL( L"u5", url, f1, f2); // + u5 NOLINT
- int64 f6 = adds.AddFolder(L"f6", f1, u5); // + f6
- int64 u7 = adds.AddURL( L"u7", url, f0, f1); // + u7 NOLINT
-
- vector<sync_api::SyncManager::ChangeRecord>::const_iterator it;
- // The bookmark model shouldn't yet have seen any of the nodes of |adds|.
- for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
- ExpectBrowserNodeUnknown(it->id);
-
- adds.ApplyPendingChanges(change_processor());
-
- // Make sure the bookmark model received all of the nodes in |adds|.
- for (it = adds.changes().begin(); it != adds.changes().end(); ++it)
- ExpectBrowserNodeMatching(&trans, it->id);
- ExpectModelMatch(&trans);
-
- // We have to do the moves before the deletions, but FakeServerChange will
- // put the deletion at the front of the changelist.
- FakeServerChange ops(&trans);
- ops.ModifyPosition(f6, other_bookmarks_id(), 0);
- ops.ModifyPosition(u3, other_bookmarks_id(), f1); // Prev == f1 is OK here.
- ops.ModifyPosition(f2, other_bookmarks_id(), u7);
- ops.ModifyPosition(u7, f2, 0);
- ops.ModifyPosition(u4, other_bookmarks_id(), f2);
- ops.ModifyPosition(u5, f6, 0);
- ops.Delete(f1);
-
- ops.ApplyPendingChanges(change_processor());
-
- ExpectModelMatch(&trans);
-}
-
-// Simulate a server change record containing a valid but non-canonical URL.
-TEST_F(ProfileSyncServiceTest, ServerChangeWithNonCanonicalURL) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- {
- sync_api::WriteTransaction trans(service_->GetUserShare());
-
- FakeServerChange adds(&trans);
- std::string url("http://dev.chromium.org");
- EXPECT_NE(GURL(url).spec(), url);
- adds.AddURL(L"u1", url, other_bookmarks_id(), 0);
-
- adds.ApplyPendingChanges(change_processor());
-
- EXPECT_TRUE(model_->other_node()->GetChildCount() == 1);
- ExpectModelMatch(&trans);
- }
-
- // Now reboot the sync service, forcing a merge step.
- StopSyncService(SAVE_TO_STORAGE);
- LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- // There should still be just the one bookmark.
- EXPECT_TRUE(model_->other_node()->GetChildCount() == 1);
- ExpectModelMatch();
-}
-
-// Simulate a server change record containing an invalid URL (per GURL).
-// TODO(ncarter): Disabled due to crashes. Fix bug 1677563.
-TEST_F(ProfileSyncServiceTest, DISABLED_ServerChangeWithInvalidURL) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- int child_count = 0;
- {
- sync_api::WriteTransaction trans(service_->GetUserShare());
-
- FakeServerChange adds(&trans);
- std::string url("x");
- EXPECT_FALSE(GURL(url).is_valid());
- adds.AddURL(L"u1", url, other_bookmarks_id(), 0);
-
- adds.ApplyPendingChanges(change_processor());
-
- // We're lenient about what should happen -- the model could wind up with
- // the node or without it; but things should be consistent, and we
- // shouldn't crash.
- child_count = model_->other_node()->GetChildCount();
- EXPECT_TRUE(child_count == 0 || child_count == 1);
- ExpectModelMatch(&trans);
- }
-
- // Now reboot the sync service, forcing a merge step.
- StopSyncService(SAVE_TO_STORAGE);
- LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- // Things ought not to have changed.
- EXPECT_EQ(model_->other_node()->GetChildCount(), child_count);
- ExpectModelMatch();
-}
-
-
-// Test strings that might pose a problem if the titles ever became used as
-// file names in the sync backend.
-TEST_F(ProfileSyncServiceTest, CornerCaseNames) {
- // TODO(ncarter): Bug 1570238 explains the failure of this test.
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- const char* names[] = {
- // The empty string.
- "",
- // Illegal Windows filenames.
- "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4",
- "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2", "LPT3",
- "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
- // Current/parent directory markers.
- ".", "..", "...",
- // Files created automatically by the Windows shell.
- "Thumbs.db", ".DS_Store",
- // Names including Win32-illegal characters, and path separators.
- "foo/bar", "foo\\bar", "foo?bar", "foo:bar", "foo|bar", "foo\"bar",
- "foo'bar", "foo<bar", "foo>bar", "foo%bar", "foo*bar", "foo]bar",
- "foo[bar",
- };
- // Create both folders and bookmarks using each name.
- GURL url("http://www.doublemint.com");
- for (size_t i = 0; i < arraysize(names); ++i) {
- model_->AddGroup(model_->other_node(), 0, ASCIIToUTF16(names[i]));
- model_->AddURL(model_->other_node(), 0, ASCIIToUTF16(names[i]), url);
- }
-
- // Verify that the browser model matches the sync model.
- EXPECT_TRUE(model_->other_node()->GetChildCount() == 2*arraysize(names));
- ExpectModelMatch();
-}
-
-// Stress the internal representation of position by sparse numbers. We want
-// to repeatedly bisect the range of available positions, to force the
-// syncer code to renumber its ranges. Pick a number big enough so that it
-// would exhaust 32bits of room between items a couple of times.
-TEST_F(ProfileSyncServiceTest, RepeatedMiddleInsertion) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- static const int kTimesToInsert = 256;
-
- // Create two book-end nodes to insert between.
- model_->AddGroup(model_->other_node(), 0, ASCIIToUTF16("Alpha"));
- model_->AddGroup(model_->other_node(), 1, ASCIIToUTF16("Omega"));
- int count = 2;
-
- // Test insertion in first half of range by repeatedly inserting in second
- // position.
- for (int i = 0; i < kTimesToInsert; ++i) {
- string16 title = ASCIIToUTF16("Pre-insertion ") + base::IntToString16(i);
- model_->AddGroup(model_->other_node(), 1, title);
- count++;
- }
-
- // Test insertion in second half of range by repeatedly inserting in
- // second-to-last position.
- for (int i = 0; i < kTimesToInsert; ++i) {
- string16 title = ASCIIToUTF16("Post-insertion ") + base::IntToString16(i);
- model_->AddGroup(model_->other_node(), count - 1, title);
- count++;
- }
-
- // Verify that the browser model matches the sync model.
- EXPECT_EQ(model_->other_node()->GetChildCount(), count);
- ExpectModelMatch();
-}
-
-// Introduce a consistency violation into the model, and see that it
-// puts itself into a lame, error state.
-TEST_F(ProfileSyncServiceTest, UnrecoverableErrorSuspendsService) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- // Synchronization should be up and running at this point.
- EXPECT_TRUE(service_->ShouldPushChanges());
-
- // Add a node which will be the target of the consistency violation.
- const BookmarkNode* node =
- model_->AddGroup(model_->other_node(), 0, ASCIIToUTF16("node"));
- ExpectSyncerNodeMatching(node);
-
- // Now destroy the syncer node as if we were the ProfileSyncService without
- // updating the ProfileSyncService state. This should introduce
- // inconsistency between the two models.
- {
- sync_api::WriteTransaction trans(service_->GetUserShare());
- sync_api::WriteNode sync_node(&trans);
- EXPECT_TRUE(associator()->InitSyncNodeFromChromeId(node->id(),
- &sync_node));
- sync_node.Remove();
- }
- // The models don't match at this point, but the ProfileSyncService
- // doesn't know it yet.
- ExpectSyncerNodeKnown(node);
- EXPECT_TRUE(service_->ShouldPushChanges());
-
- // Add a child to the inconsistent node. This should cause detection of the
- // problem and the syncer should stop processing changes.
- model_->AddGroup(node, 0, ASCIIToUTF16("nested"));
- EXPECT_FALSE(service_->ShouldPushChanges());
-
- // Try to add a node under a totally different parent. This should also
- // fail -- the ProfileSyncService should stop processing changes after
- // encountering a consistency violation.
- model_->AddGroup(model_->GetBookmarkBarNode(), 0, ASCIIToUTF16("unrelated"));
- EXPECT_FALSE(service_->ShouldPushChanges());
-
- // TODO(ncarter): We ought to test the ProfileSyncService state machine
- // directly here once that's formalized and exposed.
-}
-
-// See what happens if we run model association when there are two exact URL
-// duplicate bookmarks. The BookmarkModelAssociator should not fall over when
-// this happens.
-TEST_F(ProfileSyncServiceTest, MergeDuplicates) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("Dup"),
- GURL("http://dup.com/"));
- model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("Dup"),
- GURL("http://dup.com/"));
-
- EXPECT_EQ(2, model_->other_node()->GetChildCount());
-
- // Restart the sync service to trigger model association.
- StopSyncService(SAVE_TO_STORAGE);
- StartSyncService();
-
- EXPECT_EQ(2, model_->other_node()->GetChildCount());
- ExpectModelMatch();
-}
-
-TEST_F(ProfileSyncServiceTest, JsFrontendHandlersBasic) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
+#if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD)
+#define MAYBE_JsFrontendHandlersBasic DISABLED_JsFrontendHandlersBasic
+#else
+#define MAYBE_JsFrontendHandlersBasic JsFrontendHandlersBasic
+#endif
+TEST_F(ProfileSyncServiceTest, MAYBE_JsFrontendHandlersBasic) {
StartSyncService();
StrictMock<MockJsEventHandler> event_handler;
- browser_sync::SyncBackendHostForProfileSyncTest* test_backend =
+ SyncBackendHostForProfileSyncTest* test_backend =
service_->GetBackendForTest();
EXPECT_TRUE(service_->sync_initialized());
@@ -973,7 +163,7 @@ TEST_F(ProfileSyncServiceTest, JsFrontendHandlersBasic) {
ASSERT_TRUE(test_backend->GetJsBackend() != NULL);
EXPECT_EQ(NULL, test_backend->GetJsBackend()->GetParentJsEventRouter());
- browser_sync::JsFrontend* js_backend = service_->GetJsFrontend();
+ JsFrontend* js_backend = service_->GetJsFrontend();
js_backend->AddHandler(&event_handler);
ASSERT_TRUE(test_backend->GetJsBackend() != NULL);
EXPECT_TRUE(test_backend->GetJsBackend()->GetParentJsEventRouter() != NULL);
@@ -984,30 +174,34 @@ TEST_F(ProfileSyncServiceTest, JsFrontendHandlersBasic) {
TEST_F(ProfileSyncServiceTest,
JsFrontendHandlersDelayedBackendInitialization) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncServiceAndSetInitialSyncEnded(true, false);
+ StartSyncServiceAndSetInitialSyncEnded(true, false, false, true);
StrictMock<MockJsEventHandler> event_handler;
EXPECT_CALL(event_handler,
HandleJsEvent("onSyncServiceStateChanged",
- HasArgs(JsArgList()))).Times(3);
- // For some reason, these two events don't fire on Linux.
+ HasArgs(JsArgList()))).Times(AtLeast(3));
+ // For some reason, these events may or may not fire.
+ //
+ // TODO(akalin): Figure out exactly why there's non-determinism
+ // here, and if possible remove it.
EXPECT_CALL(event_handler, HandleJsEvent("onChangesApplied", _))
.Times(AtMost(1));
EXPECT_CALL(event_handler, HandleJsEvent("onChangesComplete", _))
.Times(AtMost(1));
+ EXPECT_CALL(event_handler, HandleJsEvent("onSyncNotificationStateChange", _))
+ .Times(AtMost(1));
EXPECT_EQ(NULL, service_->GetBackendForTest());
EXPECT_FALSE(service_->sync_initialized());
- browser_sync::JsFrontend* js_backend = service_->GetJsFrontend();
+ JsFrontend* js_backend = service_->GetJsFrontend();
js_backend->AddHandler(&event_handler);
// Since we're doing synchronous initialization, backend should be
// initialized by this call.
profile_->GetTokenService()->IssueAuthTokenForTest(
GaiaConstants::kSyncService, "token");
- browser_sync::SyncBackendHostForProfileSyncTest* test_backend =
+ SyncBackendHostForProfileSyncTest* test_backend =
service_->GetBackendForTest();
EXPECT_TRUE(service_->sync_initialized());
@@ -1020,10 +214,16 @@ TEST_F(ProfileSyncServiceTest,
}
TEST_F(ProfileSyncServiceTest, JsFrontendProcessMessageBasic) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
StartSyncService();
StrictMock<MockJsEventHandler> event_handler;
+ // For some reason, these events may or may not fire.
+ EXPECT_CALL(event_handler, HandleJsEvent("onChangesApplied", _))
+ .Times(AtMost(1));
+ EXPECT_CALL(event_handler, HandleJsEvent("onChangesComplete", _))
+ .Times(AtMost(1));
+ EXPECT_CALL(event_handler, HandleJsEvent("onSyncNotificationStateChange", _))
+ .Times(AtMost(1));
ListValue arg_list1;
arg_list1.Append(Value::CreateBooleanValue(true));
@@ -1043,7 +243,7 @@ TEST_F(ProfileSyncServiceTest, JsFrontendProcessMessageBasic) {
arg_list3.Append(arg_list2.DeepCopy());
JsArgList args3(arg_list3);
- browser_sync::JsFrontend* js_backend = service_->GetJsFrontend();
+ JsFrontend* js_backend = service_->GetJsFrontend();
// Never replied to.
js_backend->ProcessMessage("notRepliedTo", args3, &event_handler);
@@ -1072,15 +272,16 @@ TEST_F(ProfileSyncServiceTest, JsFrontendProcessMessageBasic) {
TEST_F(ProfileSyncServiceTest,
JsFrontendProcessMessageBasicDelayedBackendInitialization) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncServiceAndSetInitialSyncEnded(true, false);
+ StartSyncServiceAndSetInitialSyncEnded(true, false, false, true);
StrictMock<MockJsEventHandler> event_handler;
- // For some reason, these two events don't fire on Linux.
+ // For some reason, these events may or may not fire.
EXPECT_CALL(event_handler, HandleJsEvent("onChangesApplied", _))
.Times(AtMost(1));
EXPECT_CALL(event_handler, HandleJsEvent("onChangesComplete", _))
.Times(AtMost(1));
+ EXPECT_CALL(event_handler, HandleJsEvent("onSyncNotificationStateChange", _))
+ .Times(AtMost(1));
ListValue arg_list1;
arg_list1.Append(Value::CreateBooleanValue(true));
@@ -1104,9 +305,9 @@ TEST_F(ProfileSyncServiceTest,
const JsArgList kNoArgs;
EXPECT_CALL(event_handler, HandleJsEvent("onSyncServiceStateChanged",
- HasArgs(kNoArgs))).Times(3);
+ HasArgs(kNoArgs))).Times(AtLeast(3));
- browser_sync::JsFrontend* js_backend = service_->GetJsFrontend();
+ JsFrontend* js_backend = service_->GetJsFrontend();
// We expect a reply for this message, even though its sent before
// |event_handler| is added as a handler.
@@ -1133,472 +334,9 @@ TEST_F(ProfileSyncServiceTest,
js_backend->ProcessMessage("notRepliedTo", kNoArgs, &event_handler);
}
-struct TestData {
- const wchar_t* title;
- const char* url;
-};
-
-// TODO(ncarter): Integrate the existing TestNode/PopulateNodeFromString code
-// in the bookmark model unittest, to make it simpler to set up test data
-// here (and reduce the amount of duplication among tests), and to reduce the
-// duplication.
-class ProfileSyncServiceTestWithData : public ProfileSyncServiceTest {
- protected:
- // Populates or compares children of the given bookmark node from/with the
- // given test data array with the given size.
- void PopulateFromTestData(const BookmarkNode* node,
- const TestData* data,
- int size);
- void CompareWithTestData(const BookmarkNode* node,
- const TestData* data,
- int size);
-
- void ExpectBookmarkModelMatchesTestData();
- void WriteTestDataToBookmarkModel();
-};
-
-namespace {
-
-// Constants for bookmark model that looks like:
-// |-- Bookmark bar
-// | |-- u2, http://www.u2.com/
-// | |-- f1
-// | | |-- f1u4, http://www.f1u4.com/
-// | | |-- f1u2, http://www.f1u2.com/
-// | | |-- f1u3, http://www.f1u3.com/
-// | | +-- f1u1, http://www.f1u1.com/
-// | |-- u1, http://www.u1.com/
-// | +-- f2
-// | |-- f2u2, http://www.f2u2.com/
-// | |-- f2u4, http://www.f2u4.com/
-// | |-- f2u3, http://www.f2u3.com/
-// | +-- f2u1, http://www.f2u1.com/
-// +-- Other bookmarks
-// |-- f3
-// | |-- f3u4, http://www.f3u4.com/
-// | |-- f3u2, http://www.f3u2.com/
-// | |-- f3u3, http://www.f3u3.com/
-// | +-- f3u1, http://www.f3u1.com/
-// |-- u4, http://www.u4.com/
-// |-- u3, http://www.u3.com/
-// --- f4
-// | |-- f4u1, http://www.f4u1.com/
-// | |-- f4u2, http://www.f4u2.com/
-// | |-- f4u3, http://www.f4u3.com/
-// | +-- f4u4, http://www.f4u4.com/
-// |-- dup
-// | +-- dupu1, http://www.dupu1.com/
-// +-- dup
-// +-- dupu2, http://www.dupu1.com/
-//
-static TestData kBookmarkBarChildren[] = {
- { L"u2", "http://www.u2.com/" },
- { L"f1", NULL },
- { L"u1", "http://www.u1.com/" },
- { L"f2", NULL },
-};
-static TestData kF1Children[] = {
- { L"f1u4", "http://www.f1u4.com/" },
- { L"f1u2", "http://www.f1u2.com/" },
- { L"f1u3", "http://www.f1u3.com/" },
- { L"f1u1", "http://www.f1u1.com/" },
-};
-static TestData kF2Children[] = {
- { L"f2u2", "http://www.f2u2.com/" },
- { L"f2u4", "http://www.f2u4.com/" },
- { L"f2u3", "http://www.f2u3.com/" },
- { L"f2u1", "http://www.f2u1.com/" },
-};
-
-static TestData kOtherBookmarkChildren[] = {
- { L"f3", NULL },
- { L"u4", "http://www.u4.com/" },
- { L"u3", "http://www.u3.com/" },
- { L"f4", NULL },
- { L"dup", NULL },
- { L"dup", NULL },
-};
-static TestData kF3Children[] = {
- { L"f3u4", "http://www.f3u4.com/" },
- { L"f3u2", "http://www.f3u2.com/" },
- { L"f3u3", "http://www.f3u3.com/" },
- { L"f3u1", "http://www.f3u1.com/" },
-};
-static TestData kF4Children[] = {
- { L"f4u1", "http://www.f4u1.com/" },
- { L"f4u2", "http://www.f4u2.com/" },
- { L"f4u3", "http://www.f4u3.com/" },
- { L"f4u4", "http://www.f4u4.com/" },
-};
-static TestData kDup1Children[] = {
- { L"dupu1", "http://www.dupu1.com/" },
-};
-static TestData kDup2Children[] = {
- { L"dupu2", "http://www.dupu2.com/" },
-};
-
-} // anonymous namespace.
-
-void ProfileSyncServiceTestWithData::PopulateFromTestData(
- const BookmarkNode* node, const TestData* data, int size) {
- DCHECK(node);
- DCHECK(data);
- DCHECK(node->is_folder());
- for (int i = 0; i < size; ++i) {
- const TestData& item = data[i];
- if (item.url) {
- model_->AddURL(node, i, WideToUTF16Hack(item.title), GURL(item.url));
- } else {
- model_->AddGroup(node, i, WideToUTF16Hack(item.title));
- }
- }
-}
-
-void ProfileSyncServiceTestWithData::CompareWithTestData(
- const BookmarkNode* node, const TestData* data, int size) {
- DCHECK(node);
- DCHECK(data);
- DCHECK(node->is_folder());
- for (int i = 0; i < size; ++i) {
- const BookmarkNode* child_node = node->GetChild(i);
- const TestData& item = data[i];
- EXPECT_EQ(child_node->GetTitle(), WideToUTF16Hack(item.title));
- if (item.url) {
- EXPECT_FALSE(child_node->is_folder());
- EXPECT_TRUE(child_node->is_url());
- EXPECT_EQ(child_node->GetURL(), GURL(item.url));
- } else {
- EXPECT_TRUE(child_node->is_folder());
- EXPECT_FALSE(child_node->is_url());
- }
- }
-}
-
-// TODO(munjal): We should implement some way of generating random data and can
-// use the same seed to generate the same sequence.
-void ProfileSyncServiceTestWithData::WriteTestDataToBookmarkModel() {
- const BookmarkNode* bookmarks_bar_node = model_->GetBookmarkBarNode();
- PopulateFromTestData(bookmarks_bar_node,
- kBookmarkBarChildren,
- arraysize(kBookmarkBarChildren));
-
- ASSERT_GE(bookmarks_bar_node->GetChildCount(), 4);
- const BookmarkNode* f1_node = bookmarks_bar_node->GetChild(1);
- PopulateFromTestData(f1_node, kF1Children, arraysize(kF1Children));
- const BookmarkNode* f2_node = bookmarks_bar_node->GetChild(3);
- PopulateFromTestData(f2_node, kF2Children, arraysize(kF2Children));
-
- const BookmarkNode* other_bookmarks_node = model_->other_node();
- PopulateFromTestData(other_bookmarks_node,
- kOtherBookmarkChildren,
- arraysize(kOtherBookmarkChildren));
-
- ASSERT_GE(other_bookmarks_node->GetChildCount(), 6);
- const BookmarkNode* f3_node = other_bookmarks_node->GetChild(0);
- PopulateFromTestData(f3_node, kF3Children, arraysize(kF3Children));
- const BookmarkNode* f4_node = other_bookmarks_node->GetChild(3);
- PopulateFromTestData(f4_node, kF4Children, arraysize(kF4Children));
- const BookmarkNode* dup_node = other_bookmarks_node->GetChild(4);
- PopulateFromTestData(dup_node, kDup1Children, arraysize(kDup1Children));
- dup_node = other_bookmarks_node->GetChild(5);
- PopulateFromTestData(dup_node, kDup2Children, arraysize(kDup2Children));
-
- ExpectBookmarkModelMatchesTestData();
-}
-
-void ProfileSyncServiceTestWithData::ExpectBookmarkModelMatchesTestData() {
- const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
- CompareWithTestData(bookmark_bar_node,
- kBookmarkBarChildren,
- arraysize(kBookmarkBarChildren));
-
- ASSERT_GE(bookmark_bar_node->GetChildCount(), 4);
- const BookmarkNode* f1_node = bookmark_bar_node->GetChild(1);
- CompareWithTestData(f1_node, kF1Children, arraysize(kF1Children));
- const BookmarkNode* f2_node = bookmark_bar_node->GetChild(3);
- CompareWithTestData(f2_node, kF2Children, arraysize(kF2Children));
-
- const BookmarkNode* other_bookmarks_node = model_->other_node();
- CompareWithTestData(other_bookmarks_node,
- kOtherBookmarkChildren,
- arraysize(kOtherBookmarkChildren));
-
- ASSERT_GE(other_bookmarks_node->GetChildCount(), 6);
- const BookmarkNode* f3_node = other_bookmarks_node->GetChild(0);
- CompareWithTestData(f3_node, kF3Children, arraysize(kF3Children));
- const BookmarkNode* f4_node = other_bookmarks_node->GetChild(3);
- CompareWithTestData(f4_node, kF4Children, arraysize(kF4Children));
- const BookmarkNode* dup_node = other_bookmarks_node->GetChild(4);
- CompareWithTestData(dup_node, kDup1Children, arraysize(kDup1Children));
- dup_node = other_bookmarks_node->GetChild(5);
- CompareWithTestData(dup_node, kDup2Children, arraysize(kDup2Children));
-}
-
-// Tests persistence of the profile sync service by destroying the
-// profile sync service and then reloading it from disk.
-TEST_F(ProfileSyncServiceTestWithData, Persistence) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- WriteTestDataToBookmarkModel();
-
- ExpectModelMatch();
-
- // Force both models to discard their data and reload from disk. This
- // simulates what would happen if the browser were to shutdown normally,
- // and then relaunch.
- StopSyncService(SAVE_TO_STORAGE);
- LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- ExpectBookmarkModelMatchesTestData();
-
- // With the BookmarkModel contents verified, ExpectModelMatch will
- // verify the contents of the sync model.
- ExpectModelMatch();
-}
-
-// Tests the merge case when the BookmarkModel is non-empty but the
-// sync model is empty. This corresponds to uploading browser
-// bookmarks to an initially empty, new account.
-TEST_F(ProfileSyncServiceTestWithData, MergeWithEmptySyncModel) {
- // Don't start the sync service until we've populated the bookmark model.
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
-
- WriteTestDataToBookmarkModel();
-
- // Restart the profile sync service. This should trigger a merge step
- // during initialization -- we expect the browser bookmarks to be written
- // to the sync service during this call.
- StartSyncService();
-
- // Verify that the bookmark model hasn't changed, and that the sync model
- // matches it exactly.
- ExpectBookmarkModelMatchesTestData();
- ExpectModelMatch();
-}
-
-// Tests the merge case when the BookmarkModel is empty but the sync model is
-// non-empty. This corresponds (somewhat) to a clean install of the browser,
-// with no bookmarks, connecting to a sync account that has some bookmarks.
-TEST_F(ProfileSyncServiceTestWithData, MergeWithEmptyBookmarkModel) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- WriteTestDataToBookmarkModel();
-
- ExpectModelMatch();
-
- // Force the sync service to shut down and write itself to disk.
- StopSyncService(SAVE_TO_STORAGE);
-
- // Blow away the bookmark model -- it should be empty afterwards.
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- EXPECT_EQ(model_->GetBookmarkBarNode()->GetChildCount(), 0);
- EXPECT_EQ(model_->other_node()->GetChildCount(), 0);
-
- // Now restart the sync service. Starting it should populate the bookmark
- // model -- test for consistency.
- StartSyncService();
- ExpectBookmarkModelMatchesTestData();
- ExpectModelMatch();
-}
-
-// Tests the merge cases when both the models are expected to be identical
-// after the merge.
-TEST_F(ProfileSyncServiceTestWithData, MergeExpectedIdenticalModels) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
- WriteTestDataToBookmarkModel();
- ExpectModelMatch();
- StopSyncService(SAVE_TO_STORAGE);
-
- // At this point both the bookmark model and the server should have the
- // exact same data and it should match the test data.
- LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
- ExpectBookmarkModelMatchesTestData();
- ExpectModelMatch();
- StopSyncService(SAVE_TO_STORAGE);
-
- // Now reorder some bookmarks in the bookmark model and then merge. Make
- // sure we get the order of the server after merge.
- LoadBookmarkModel(LOAD_FROM_STORAGE, DONT_SAVE_TO_STORAGE);
- ExpectBookmarkModelMatchesTestData();
- const BookmarkNode* bookmark_bar = model_->GetBookmarkBarNode();
- ASSERT_TRUE(bookmark_bar);
- ASSERT_GT(bookmark_bar->GetChildCount(), 1);
- model_->Move(bookmark_bar->GetChild(0), bookmark_bar, 1);
- StartSyncService();
- ExpectModelMatch();
- ExpectBookmarkModelMatchesTestData();
-}
-
-// Tests the merge cases when both the models are expected to be identical
-// after the merge.
-TEST_F(ProfileSyncServiceTestWithData, MergeModelsWithSomeExtras) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- WriteTestDataToBookmarkModel();
- ExpectBookmarkModelMatchesTestData();
-
- // Remove some nodes and reorder some nodes.
- const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
- int remove_index = 2;
- ASSERT_GT(bookmark_bar_node->GetChildCount(), remove_index);
- const BookmarkNode* child_node = bookmark_bar_node->GetChild(remove_index);
- ASSERT_TRUE(child_node);
- ASSERT_TRUE(child_node->is_url());
- model_->Remove(bookmark_bar_node, remove_index);
- ASSERT_GT(bookmark_bar_node->GetChildCount(), remove_index);
- child_node = bookmark_bar_node->GetChild(remove_index);
- ASSERT_TRUE(child_node);
- ASSERT_TRUE(child_node->is_folder());
- model_->Remove(bookmark_bar_node, remove_index);
-
- const BookmarkNode* other_node = model_->other_node();
- ASSERT_GE(other_node->GetChildCount(), 1);
- const BookmarkNode* f3_node = other_node->GetChild(0);
- ASSERT_TRUE(f3_node);
- ASSERT_TRUE(f3_node->is_folder());
- remove_index = 2;
- ASSERT_GT(f3_node->GetChildCount(), remove_index);
- model_->Remove(f3_node, remove_index);
- ASSERT_GT(f3_node->GetChildCount(), remove_index);
- model_->Remove(f3_node, remove_index);
-
- StartSyncService();
- ExpectModelMatch();
- StopSyncService(SAVE_TO_STORAGE);
-
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- WriteTestDataToBookmarkModel();
- ExpectBookmarkModelMatchesTestData();
-
- // Remove some nodes and reorder some nodes.
- bookmark_bar_node = model_->GetBookmarkBarNode();
- remove_index = 0;
- ASSERT_GT(bookmark_bar_node->GetChildCount(), remove_index);
- child_node = bookmark_bar_node->GetChild(remove_index);
- ASSERT_TRUE(child_node);
- ASSERT_TRUE(child_node->is_url());
- model_->Remove(bookmark_bar_node, remove_index);
- ASSERT_GT(bookmark_bar_node->GetChildCount(), remove_index);
- child_node = bookmark_bar_node->GetChild(remove_index);
- ASSERT_TRUE(child_node);
- ASSERT_TRUE(child_node->is_folder());
- model_->Remove(bookmark_bar_node, remove_index);
-
- ASSERT_GE(bookmark_bar_node->GetChildCount(), 2);
- model_->Move(bookmark_bar_node->GetChild(0), bookmark_bar_node, 1);
-
- other_node = model_->other_node();
- ASSERT_GE(other_node->GetChildCount(), 1);
- f3_node = other_node->GetChild(0);
- ASSERT_TRUE(f3_node);
- ASSERT_TRUE(f3_node->is_folder());
- remove_index = 0;
- ASSERT_GT(f3_node->GetChildCount(), remove_index);
- model_->Remove(f3_node, remove_index);
- ASSERT_GT(f3_node->GetChildCount(), remove_index);
- model_->Remove(f3_node, remove_index);
-
- ASSERT_GE(other_node->GetChildCount(), 4);
- model_->Move(other_node->GetChild(0), other_node, 1);
- model_->Move(other_node->GetChild(2), other_node, 3);
-
- StartSyncService();
- ExpectModelMatch();
-
- // After the merge, the model should match the test data.
- ExpectBookmarkModelMatchesTestData();
-}
-
-// Tests that when persisted model associations are used, things work fine.
-TEST_F(ProfileSyncServiceTestWithData, ModelAssociationPersistence) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- WriteTestDataToBookmarkModel();
- StartSyncService();
- ExpectModelMatch();
- // Force the sync service to shut down and write itself to disk.
- StopSyncService(SAVE_TO_STORAGE);
- // Now restart the sync service. This time it should use the persistent
- // associations.
- StartSyncService();
- ExpectModelMatch();
-}
-
-// Tests that when persisted model associations are used, things work fine.
-TEST_F(ProfileSyncServiceTestWithData, ModelAssociationInvalidPersistence) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- WriteTestDataToBookmarkModel();
- StartSyncService();
- ExpectModelMatch();
- // Force the sync service to shut down and write itself to disk.
- StopSyncService(SAVE_TO_STORAGE);
- // Change the bookmark model before restarting sync service to simulate
- // the situation where bookmark model is different from sync model and
- // make sure model associator correctly rebuilds associations.
- const BookmarkNode* bookmark_bar_node = model_->GetBookmarkBarNode();
- model_->AddURL(bookmark_bar_node, 0, ASCIIToUTF16("xtra"),
- GURL("http://www.xtra.com"));
- // Now restart the sync service. This time it will try to use the persistent
- // associations and realize that they are invalid and hence will rebuild
- // associations.
- StartSyncService();
- ExpectModelMatch();
-}
-
-TEST_F(ProfileSyncServiceTestWithData, SortChildren) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, DONT_SAVE_TO_STORAGE);
- StartSyncService();
-
- // Write test data to bookmark model and verify that the models match.
- WriteTestDataToBookmarkModel();
- const BookmarkNode* folder_added = model_->other_node()->GetChild(0);
- ASSERT_TRUE(folder_added);
- ASSERT_TRUE(folder_added->is_folder());
-
- ExpectModelMatch();
-
- // Sort the other-bookmarks children and expect that hte models match.
- model_->SortChildren(folder_added);
- ExpectModelMatch();
-}
-
-// See what happens if we enable sync but then delete the "Sync Data"
-// folder.
-TEST_F(ProfileSyncServiceTestWithData, RecoverAfterDeletingSyncDataDirectory) {
- LoadBookmarkModel(DELETE_EXISTING_STORAGE, SAVE_TO_STORAGE);
- StartSyncService();
-
- WriteTestDataToBookmarkModel();
-
- // While the service is running.
- FilePath sync_data_directory =
- service_->GetBackendForTest()->sync_data_folder_path();
-
- // Simulate a normal shutdown for the sync service (don't disable it for
- // the user, which would reset the preferences and delete the sync data
- // directory).
- StopSyncService(SAVE_TO_STORAGE);
-
- // Now pretend that the user has deleted this directory from the disk.
- file_util::Delete(sync_data_directory, true);
-
- // Restart the sync service. Don't fake out setting initial sync ended; lets
- // make sure the system does in fact nudge and wait for this to happen.
- StartSyncServiceAndSetInitialSyncEnded(false, true);
-
- // Make sure we're back in sync. In real life, the user would need
- // to reauthenticate before this happens, but in the test, authentication
- // is sidestepped.
- ExpectBookmarkModelMatchesTestData();
- ExpectModelMatch();
-}
-
// Make sure that things still work if sync is not enabled, but some old sync
// databases are lingering in the "Sync Data" folder.
-TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) {
+TEST_F(ProfileSyncServiceTest, TestStartupWithOldSyncData) {
const char* nonsense1 = "reginald";
const char* nonsense2 = "beartato";
const char* nonsense3 = "harrison";
@@ -1607,53 +345,24 @@ TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) {
temp_directory.AppendASCII("BookmarkSyncSettings.sqlite3");
FilePath sync_file2 = temp_directory.AppendASCII("SyncData.sqlite3");
FilePath sync_file3 = temp_directory.AppendASCII("nonsense_file");
- file_util::CreateDirectory(temp_directory);
- file_util::WriteFile(sync_file1, nonsense1, strlen(nonsense1));
- file_util::WriteFile(sync_file2, nonsense2, strlen(nonsense2));
- file_util::WriteFile(sync_file3, nonsense3, strlen(nonsense3));
-
- LoadBookmarkModel(LOAD_FROM_STORAGE, SAVE_TO_STORAGE);
- if (!service_.get()) {
- service_.reset(
- new TestProfileSyncService(&factory_, profile_.get(),
- "test", true, NULL));
- service_->dont_set_initial_sync_ended_on_init();
- service_->set_synchronous_sync_configuration();
- profile_->GetPrefs()->SetBoolean(prefs::kSyncHasSetupCompleted, false);
-
- model_associator_ = new TestBookmarkModelAssociator(service_.get(),
- service_.get());
- change_processor_ = new BookmarkChangeProcessor(model_associator_,
- service_.get());
- EXPECT_CALL(factory_, CreateBookmarkSyncComponents(_, _)).
- WillOnce(Return(ProfileSyncFactory::SyncComponents(
- model_associator_, change_processor_)));
- EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
- WillOnce(ReturnNewDataTypeManager());
-
- service_->RegisterDataTypeController(
- new browser_sync::BookmarkDataTypeController(&factory_,
- profile_.get(),
- service_.get()));
-
- service_->Initialize(); // will call disableForUser because sync setup
- // hasn't been completed.
- }
+ ASSERT_TRUE(file_util::CreateDirectory(temp_directory));
+ ASSERT_NE(-1,
+ file_util::WriteFile(sync_file1, nonsense1, strlen(nonsense1)));
+ ASSERT_NE(-1,
+ file_util::WriteFile(sync_file2, nonsense2, strlen(nonsense2)));
+ ASSERT_NE(-1,
+ file_util::WriteFile(sync_file3, nonsense3, strlen(nonsense3)));
- ASSERT_FALSE(service_->HasSyncSetupCompleted());
+ StartSyncServiceAndSetInitialSyncEnded(false, false, true, false);
+ EXPECT_FALSE(service_->HasSyncSetupCompleted());
- // Create some tokens in the token service; the service will startup when
- // it is notified that tokens are available.
+ // Since we're doing synchronous initialization, backend should be
+ // initialized by this call.
profile_->GetTokenService()->IssueAuthTokenForTest(
- GaiaConstants::kSyncService, "sync_token");
-
- syncable::ModelTypeSet set;
- set.insert(syncable::BOOKMARKS);
- service_->OnUserChoseDatatypes(false, set);
-
- MessageLoop::current()->RunAllPending();
+ GaiaConstants::kSyncService, "token");
- // Stop the service so we can read the new Sync Data files that were created.
+ // Stop the service so we can read the new Sync Data files that were
+ // created.
service_.reset();
// This file should have been deleted when the whole directory was nuked.
@@ -1663,6 +372,10 @@ TEST_F(ProfileSyncServiceTestWithData, TestStartupWithOldSyncData) {
// This will still exist, but the text should have changed.
ASSERT_TRUE(file_util::PathExists(sync_file2));
std::string file2text;
- file_util::ReadFileToString(sync_file2, &file2text);
- ASSERT_FALSE(file2text.compare(nonsense2) == 0);
+ ASSERT_TRUE(file_util::ReadFileToString(sync_file2, &file2text));
+ ASSERT_NE(file2text.compare(nonsense2), 0);
}
+
+} // namespace
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/profile_sync_test_util.h b/chrome/browser/sync/profile_sync_test_util.h
index 5efa015..9adfe3c 100644
--- a/chrome/browser/sync/profile_sync_test_util.h
+++ b/chrome/browser/sync/profile_sync_test_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,15 +8,15 @@
#include <string>
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "chrome/browser/sync/profile_sync_service_observer.h"
-#include "chrome/common/notification_service.h"
-#include "chrome/common/notification_source.h"
-#include "chrome/common/notification_type.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
+#include "content/common/notification_source.h"
+#include "content/common/notification_type.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace base {
diff --git a/chrome/browser/sync/protocol/autofill_specifics.proto b/chrome/browser/sync/protocol/autofill_specifics.proto
index 520f25f..ae35da2 100644
--- a/chrome/browser/sync/protocol/autofill_specifics.proto
+++ b/chrome/browser/sync/protocol/autofill_specifics.proto
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -81,7 +81,7 @@ message AutofillSpecifics {
repeated int64 usage_timestamp = 3;
// An autofill++ profile object. If present, indicates this entity
- // represents an AutoFillProfile exclusively, and no other fields (such as
+ // represents an AutofillProfile exclusively, and no other fields (such as
// name/value or credit_card) should be present.
optional AutofillProfileSpecifics profile = 4;
diff --git a/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc b/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc
index 20e73e4..fef155b 100644
--- a/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc
+++ b/chrome/browser/sync/protocol/proto_value_conversions_unittest.cc
@@ -6,7 +6,7 @@
#include "chrome/browser/sync/protocol/proto_value_conversions.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/browser/sync/protocol/app_specifics.pb.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
diff --git a/chrome/browser/sync/protocol/sync.proto b/chrome/browser/sync/protocol/sync.proto
index a87b0b3..48cb214 100644
--- a/chrome/browser/sync/protocol/sync.proto
+++ b/chrome/browser/sync/protocol/sync.proto
@@ -105,17 +105,20 @@ message SyncEntity {
// Present in both GetUpdatesResponse and CommitMessage.
optional int64 ctime = 6;
- // A unique-in-the parent name for this item.
+ // The name of this item.
+ // Historical note:
+ // Since November 2010, this value is no different from non_unique_name.
+ // Before then, server implementations would maintain a unique-within-parent
+ // value separate from its base, "non-unique" value. Clients had not
+ // depended on the uniqueness of the property since November 2009; it was
+ // removed from Chromium by http://codereview.chromium.org/371029 .
// Present in both GetUpdatesResponse and CommitMessage.
required string name = 7;
- // non_unique_name holds the base name stored serverside, which is different
- // from |name| when |name| has been suffixed in a way to make it unique
- // among its siblings. In a GetUpdatesResponse, |non_unique_name| will
- // be supplied in addition to |name|, and the client may choose which
- // field to use depending on its needs. In a CommitMessage,
- // |non_unique_name| takes precedence over the |name| value if both are
- // supplied.
+ // The name of this item. Same as |name|.
+ // |non_unique_name| should take precedence over the |name| value if both
+ // are supplied. For efficiency, clients and servers should avoid setting
+ // this redundant value.
// Present in both GetUpdatesResponse and CommitMessage.
optional string non_unique_name = 8;
@@ -273,7 +276,7 @@ message GetUpdatesCallerInfo {
PERIODIC = 4; // The source of the update was periodic polling.
SYNC_CYCLE_CONTINUATION = 5; // The source of the update was a
// continuation of a previous update.
- CLEAR_PRIVATE_DATA = 6; // Source is a call to remove all private data
+ CLEAR_PRIVATE_DATA = 6; // Source is a call to remove all private data
}
required GetUpdatesSource source = 1;
@@ -363,6 +366,14 @@ message GetUpdatesMessage {
// Per-datatype progress marker. If present, the server will ignore
// the values of requested_types and from_timestamp, using this instead.
repeated DataTypeProgressMarker from_progress_marker = 6;
+
+ // Indicates whether the response should be sent in chunks. This may be
+ // needed for devices with limited memory resources. If true, the response
+ // will include one or more ClientToServerResponses, with the frist one
+ // containing GetUpdatesMetadataResponse, and the remaining ones, if any,
+ // containing GetUpdatesStreamingResponse. These ClientToServerResponses are
+ // delimited by a length prefix, which is encoded as a varint.
+ optional bool streaming = 7 [default = false];
};
message AuthenticateMessage {
@@ -487,6 +498,29 @@ message GetUpdatesResponse {
repeated DataTypeProgressMarker new_progress_marker = 5;
};
+// The metadata response for GetUpdatesMessage. This response is sent when
+// streaming is set to true in the request. It is prefixed with a length
+// delimiter, which is encoded in varint.
+message GetUpdatesMetadataResponse {
+ // Approximate count of changes remaining. Detailed comment is available in
+ // GetUpdatesResponse.
+ optional int64 changes_remaining = 1;
+
+ // Opaque, per-datatype timestamp-like tokens. Detailed comment is available
+ // in GetUpdatesResponse.
+ repeated DataTypeProgressMarker new_progress_marker = 2;
+};
+
+// The streaming response message for GetUpdatesMessage. This message is sent
+// when streaming is set to true in the request. There may be multiple
+// GetUpdatesStreamingResponse messages in a response. This type of messages
+// is preceded by GetUpdatesMetadataResponse. It is prefixed with a length
+// delimiter, which is encoded in varint.
+message GetUpdatesStreamingResponse {
+ // New sync entries that the client should apply.
+ repeated SyncEntity entries = 1;
+};
+
// A user-identifying struct. For a given Google account the email and display
// name can change, but obfuscated_id should be constant.
// The obfuscated id is optional because at least one planned use of the proto
@@ -526,6 +560,10 @@ message ClientToServerResponse {
optional GetUpdatesResponse get_updates = 2;
optional AuthenticateResponse authenticate = 3;
optional ClearUserDataResponse clear_user_data = 9;
+ optional GetUpdatesMetadataResponse stream_metadata = 10;
+ // If GetUpdatesStreamingResponse is contained in the ClientToServerResponse,
+ // none of the other fields (error_code and etc) will be set.
+ optional GetUpdatesStreamingResponse stream_data = 11;
enum ErrorType {
SUCCESS = 0;
@@ -544,6 +582,10 @@ message ClientToServerResponse {
// come back later.
TRANSIENT_ERROR = 8; // A transient error occured (eg. backend
// timeout). Client should try again later.
+ MIGRATION_DONE = 9; // Migration has finished for one or more data
+ // types. Client should clear the cache for
+ // these data types only and then re-sync with
+ // a server.
UNKNOWN = 100; // Unknown value. This should never be explicitly
// used; it is the default value when an
// out-of-date client parses a value it doesn't
@@ -567,5 +609,9 @@ message ClientToServerResponse {
optional ClientCommand client_command = 7;
optional ProfilingData profiling_data = 8;
+
+ // The data types whose storage has been migrated. Present when the value of
+ // error_code is MIGRATION_DONE.
+ repeated int32 migrated_data_type_id = 12;
};
diff --git a/chrome/browser/sync/protocol/sync_proto.gyp b/chrome/browser/sync/protocol/sync_proto.gyp
index a32beff..f505f41 100644
--- a/chrome/browser/sync/protocol/sync_proto.gyp
+++ b/chrome/browser/sync/protocol/sync_proto.gyp
@@ -5,6 +5,7 @@
{
'variables': {
'chromium_code': 1,
+ 'protoc_out_dir': '<(SHARED_INTERMEDIATE_DIR)/protoc_out',
},
'targets': [
{
@@ -36,14 +37,14 @@
],
'outputs': [
'<(PRODUCT_DIR)/pyproto/sync_pb/<(RULE_INPUT_ROOT)_pb2.py',
- '<(SHARED_INTERMEDIATE_DIR)/protoc_out/chrome/browser/sync/protocol/<(RULE_INPUT_ROOT).pb.h',
- '<(SHARED_INTERMEDIATE_DIR)/protoc_out/chrome/browser/sync/protocol/<(RULE_INPUT_ROOT).pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/<(RULE_INPUT_ROOT).pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/<(RULE_INPUT_ROOT).pb.cc',
],
'action': [
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)protoc<(EXECUTABLE_SUFFIX)',
'--proto_path=.',
'./<(RULE_INPUT_ROOT)<(RULE_INPUT_EXT)',
- '--cpp_out=<(SHARED_INTERMEDIATE_DIR)/protoc_out/chrome/browser/sync/protocol',
+ '--cpp_out=<(protoc_out_dir)/chrome/browser/sync/protocol',
'--python_out=<(PRODUCT_DIR)/pyproto/sync_pb',
],
'message': 'Generating C++ and Python code from <(RULE_INPUT_PATH)',
@@ -55,7 +56,33 @@
},
{
'target_name': 'sync_proto_cpp',
- 'type': 'none',
+ 'type': '<(library)',
+ 'sources': [
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/sync.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/sync.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/encryption.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/encryption.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/app_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/app_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/autofill_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/autofill_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/bookmark_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/bookmark_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/extension_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/extension_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/nigori_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/nigori_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/password_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/password_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/preference_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/preference_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/session_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/session_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/theme_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/theme_specifics.pb.h',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/typed_url_specifics.pb.cc',
+ '<(protoc_out_dir)/chrome/browser/sync/protocol/typed_url_specifics.pb.h',
+ ],
'export_dependent_settings': [
'../../../../third_party/protobuf/protobuf.gyp:protobuf_lite',
'sync_proto',
@@ -66,9 +93,12 @@
],
'direct_dependent_settings': {
'include_dirs': [
- '<(SHARED_INTERMEDIATE_DIR)/protoc_out',
+ '<(protoc_out_dir)',
],
},
+ # This target exports a hard dependency because it includes generated
+ # header files.
+ 'hard_dependency': 1,
},
],
}
diff --git a/chrome/browser/sync/resources/configure.html b/chrome/browser/sync/resources/configure.html
index 12b4f30..2c2f790 100644
--- a/chrome/browser/sync/resources/configure.html
+++ b/chrome/browser/sync/resources/configure.html
@@ -3,17 +3,42 @@
<title></title>
<style type="text/css">
body {
- line-height: 1.33em;
- background: #FFFFFF;
- font-size: 11pt;
+
}
-html[os='mac'] body {
- line-height: 1.5em;
- background: #FFFFFF;
+ body {
+
}
+
+h4 {
+ margin: 10px 0;
+}
+
+hr {
+ background-color: #ddd;
+ border: 0;
+ height: 1px;
+ margin: 5px 0;
+ text-align: left;
+ width: 100%;
+}
+
form {
-webkit-user-select: none;
}
+
+.content-area {
+ padding: 10px 15px;
+}
+
+.action-area {
+ -webkit-box-align: center;
+ -webkit-box-orient: horizontal;
+ -webkit-box-pack: end;
+ border-top: 1px solid rgba(188, 193, 208, .5);
+ display: -webkit-box;
+ padding: 12px;
+}
+
.sync-header {
font-size: 1.2em;
font-weight: bold;
@@ -22,6 +47,35 @@ form {
.sync-select-customization {
margin-top: 10px;
}
+
+.action-area-link-container {
+ -webkit-box-flex: 1;
+}
+
+#sync-passphrase-container {
+ margin: 10px 0;
+}
+
+#sync-custom-passphrase {
+ margin: 0 25px;
+}
+
+#sync-passphrase-message {
+ color: gray;
+}
+
+.sync-custom-passphrase-input {
+ margin: 10px 0;
+}
+
+#sync-select-container {
+ margin-bottom: 10px;
+}
+
+#sync-instructions-container {
+ margin-bottom: 30px;
+}
+
#chooseDataTypesRadio {
vertical-align: top;
}
@@ -29,15 +83,10 @@ form {
display: inline-block;
}
#chooseDataTypesBody {
- width: 90%;
- -webkit-margin-start: 3ex;
+ margin: 10px 0;
}
#chooseDataTypesBody > div {
- margin-top: 0px;
- -webkit-column-count: 2;
- -webkit-column-gap: 10px;
- column-count: 2;
- column-gap: 10px;
+ -webkit-column-count: 3;
}
.sync-config-tab-contents-inactive {
@@ -153,10 +202,6 @@ form {
margin-bottom: 5px;
}
-#learn-more-link {
- float: right;
-}
-
#change-passphrase {
margin: 10px 0;
background: #EEE;
@@ -186,9 +231,8 @@ html[os='mac'] input[type='submit'] {
</style>
<script src="chrome://resources/js/cr.js"></script>
+<!-- TODO(jhawkins): util.js for $(). -->
<script>
- var currentTab;
-
// Called once, when this html/js is loaded.
function initializeConfigureDialog(args) {
// Allow platform specific rules
@@ -198,13 +242,52 @@ html[os='mac'] input[type='submit'] {
document.documentElement.setAttribute('os', 'linux');
}
+ var datatypeSelect = document.getElementById('sync-select-datatypes');
+ datatypeSelect.onchange = function() {
+ var syncAll = this.selectedIndex == 0;
+ setCheckboxesToKeepEverythingSynced(syncAll);
+
+ document.getElementById('chooseDataTypesBody').hidden = syncAll;
+ };
+
if (args) {
- currentTab = args['initialTab'];
- switchToTab(currentTab);
setCheckboxesAndErrors(args);
+
+ // Whether to display the 'Sync everything' confirmation screen or the
+ // customize data types screen.
+ // TODO(jhawkins): Rename |keepEverythingSynced| to |syncAllDataTypes|.
+ var syncEverything = args['syncEverything'];
+ var syncAllDataTypes = args['keepEverythingSynced'];
+ var usePassphrase = args['usePassphrase'];
+ if (syncEverything == false || syncAllDataTypes == false ||
+ usePassphrase) {
+ showCustomizePage();
+ }
}
}
+ function showCustomizePage() {
+ document.getElementById('confirm-sync-preferences').hidden = true;
+ document.getElementById('customize-sync-preferences').hidden = false;
+
+ // If the user is shown the 'Customize' page, it's likely he intends to
+ // change the data types. Select the 'Choose data types' option in this
+ // case.
+ document.getElementById('sync-select-datatypes').selectedIndex = 1;
+ document.getElementById('chooseDataTypesBody').hidden = false;
+ setDataTypeCheckboxesEnabled(true);
+ }
+
+ function showSyncEverythingPage() {
+ document.getElementById('confirm-sync-preferences').hidden = false;
+ document.getElementById('customize-sync-preferences').hidden = true;
+
+ // The default state is to sync everything and to not use a custom
+ // passphrase.
+ setCheckboxesToKeepEverythingSynced(true);
+ document.getElementById("google-option").checked = true;
+ }
+
function setCheckboxesAndErrors(args) {
setChooseDataTypesCheckboxes(args);
setEncryptionCheckboxes(args);
@@ -236,10 +319,10 @@ html[os='mac'] input[type='submit'] {
document.getElementById("okButton").focus();
}
- document.getElementById("keepEverythingSyncedRadio").checked =
+ var datatypeSelect = document.getElementById('sync-select-datatypes');
+ datatypeSelect.selectedIndex = args.keepEverythingSynced ? 0 : 1;
+ document.getElementById('chooseDataTypesBody').hidden =
args.keepEverythingSynced;
- document.getElementById("chooseDataTypesRadio").checked =
- !args.keepEverythingSynced;
document.getElementById("bookmarksCheckbox").checked = args.syncBookmarks;
document.getElementById("preferencesCheckbox").checked =
@@ -295,10 +378,9 @@ html[os='mac'] input[type='submit'] {
// The passphrase, once set, cannot be unset, but we show a reset link.
document.getElementById("explicit-option").disabled = true;
document.getElementById("google-option").disabled = true;
- document.getElementById("change-passphrase").style.display = "block";
+ document.getElementById("sync-custom-passphrase").hidden = true;
} else {
document.getElementById("google-option").checked = true;
- document.getElementById("change-passphrase").style.display = "none";
}
switchToMode("");
}
@@ -306,7 +388,8 @@ html[os='mac'] input[type='submit'] {
function setErrorState(args) {
if (!args.was_aborted)
return;
- document.getElementById("aborted_text").className = "sync-error-show";
+
+ document.getElementById("aborted-text").className = "sync-error-show";
document.getElementById("okButton").disabled = true;
document.getElementById("keepEverythingSyncedRadio").disabled = true;
document.getElementById("chooseDataTypesRadio").disabled = true;
@@ -325,8 +408,6 @@ html[os='mac'] input[type='submit'] {
}
}
-
-
// Returns true if at least one data type is enabled and no data types are
// checked. (If all data type checkboxes are disabled, it's because "keep
// everything synced" is checked.)
@@ -348,11 +429,11 @@ html[os='mac'] input[type='submit'] {
function sendConfiguration() {
// Trying to submit, so hide previous errors.
- document.getElementById("aborted_text").className = "sync-error-hide";
- document.getElementById("error_text").className = "sync-error-hide";
+ document.getElementById("aborted-text").className = "sync-error-hide";
+ document.getElementById("error-text").className = "sync-error-hide";
if (noDataTypesChecked()) {
- document.getElementById("error_text").className = "sync-error-show";
+ document.getElementById("error-text").className = "sync-error-show";
return;
}
@@ -361,7 +442,9 @@ html[os='mac'] input[type='submit'] {
return false;
}
- var syncAll = f.keepEverythingSyncedRadio.checked;
+ var syncAll =
+ document.getElementById('sync-select-datatypes').selectedIndex == 0;
+
// These values need to be kept in sync with where they are read in
// SyncSetupFlow::GetDataTypeChoiceData().
var result = JSON.stringify({
@@ -381,35 +464,11 @@ html[os='mac'] input[type='submit'] {
chrome.send("Configure", [result]);
}
- function switchToTab(newTab) {
- if (currentTab) {
- document.getElementById(currentTab + "-tab").className =
- "sync-config-tab-inactive";
- document.getElementById(currentTab + "-tab-contents").className =
- "sync-config-tab-contents-inactive";
- }
-
- // Default to the 'Data Types' tab.
- if (!newTab)
- newTab = "data-type";
-
- document.getElementById(newTab + "-tab").className =
- "sync-config-tab-active";
- document.getElementById(newTab + "-tab-contents").className =
- "sync-config-tab-contents-active";
-
- currentTab = newTab;
- }
-
function switchToMode(mode) {
- document.getElementById("section-explicit").style.display = "none";
- document.getElementById("section-google").style.display = "none";
-
- if (mode == "google") {
- document.getElementById("section-google").style.display = "block";
- } else if (mode =="explicit") {
- document.getElementById("section-explicit").style.display = "block";
- }
+ if (mode == "google")
+ document.getElementById("sync-custom-passphrase").hidden = true;
+ else if (mode =="explicit")
+ document.getElementById("sync-custom-passphrase").hidden = false;
}
function getRadioCheckedValue() {
@@ -440,7 +499,10 @@ html[os='mac'] input[type='submit'] {
emptyError.style.display = "block";
return false;
}
- if (f.confirmpassphrase.value != f.passphrase.value) {
+
+ var confirmPassphrase = document.getElementById("confirm-passphrase");
+ var passphrase = document.getElementById("passphrase");
+ if (confirmPassphrase.value != passphrase.value) {
mismatchError.style.display = "block";
return false;
}
@@ -456,38 +518,37 @@ html[os='mac'] input[type='submit'] {
</head>
<body i18n-values=".style.fontFamily:fontfamily"
onload="initializeConfigureDialog(JSON.parse(chrome.dialogArguments));">
-<form id="chooseDataTypesForm" onSubmit="sendConfiguration(); return false;">
-
- <div class="sync-config-tabstrip">
- <div id="data-type-tab" class="sync-config-tab-inactive">
- <a href="#" onclick="switchToTab('data-type'); return false;"
- i18n-content="dataTypes"></a>
- </div>
- <div id="encryption-tab" class="sync-config-tab-inactive">
- <a href="#" onclick="switchToTab('encryption'); return false;"
- i18n-content="encryption"></a>
+<div id="confirm-sync-preferences">
+ <div class="sync-header" i18n-content="confirmSyncPreferences"></div>
+ <div id="sync-instructions-container" class="content-area">
+ <span i18n-content="choosedatatypesinstructions"></span>
+ <a i18n-values="href:encryptionhelpurl" target="_blank"
+ i18n-content="learnmore"></a>
+ </div>
+ <div>
+ <div class="action-area">
+ <div class="action-area-link-container">
+ <a id="customize-link" href="#" i18n-content="customizelinklabel"
+ onclick="showCustomizePage();"></a>
+ </div>
+ <input id="okButton" type="button" i18n-values="value:syncEverything"
+ onclick="sendConfiguration();">
+ <input id="cancelButton" type="button" i18n-values="value:cancel"
+ onclick="chrome.send('DialogClose', [''])">
</div>
</div>
- <div id="data-type-tab-contents" class="sync-config-tab-contents-inactive">
-
- <div class="sync-header"
- i18n-content="choosedatatypesheader"></div>
- <div class="sync-choose_data_types_instructions"
- i18n-content="choosedatatypesinstructions"></div>
- <div class="sync-select-customization">
- <div class="sync-choice_radio">
- <input id="keepEverythingSyncedRadio" type="radio"
- name="syncChooseDataTypes"
- onclick="setCheckboxesToKeepEverythingSynced(true);">
- <label for="keepEverythingSyncedRadio"
- i18n-content="keepeverythingsynced">
- </label>
- </div>
- <div id="chooseDataTypes" class="sync-choice_radio">
- <input id="chooseDataTypesRadio" type="radio" name="syncChooseDataTypes"
- onclick="setCheckboxesToKeepEverythingSynced(false)">
- <label for="chooseDataTypesRadio" i18n-content="choosedatatypes" ></label>
- <div id="chooseDataTypesBody">
+</div>
+<div id="customize-sync-preferences" hidden>
+ <form id="chooseDataTypesForm" onSubmit="sendConfiguration(); return false;">
+ <div class="sync-header">Customize Sync Preferences</div>
+ <hr>
+ <div id="sync-configure-content" class="content-area">
+ <div id="sync-select-container">
+ <select id="sync-select-datatypes">
+ <option i18n-content="keepeverythingsynced"></option>
+ <option i18n-content="choosedatatypes" selected></option>
+ </select>
+ <div id="chooseDataTypesBody" hidden>
<div>
<!-- Apps -->
<div class="sync-item-show" id="appsItem">
@@ -555,70 +616,58 @@ html[os='mac'] input[type='submit'] {
</div>
</div>
<div class="sync-errors">
- <span id="error_text" i18n-content="synczerodatatypeserror"
+ <span id="error-text" i18n-content="synczerodatatypeserror"
class="sync-error-hide"></span>
- <span id="aborted_text" i18n-content="abortederror"
+ <span id="aborted-text" i18n-content="abortederror"
class="sync-error-hide"></span>
</div>
- </div>
-
- </div>
- <div id="encryption-tab-contents" class="sync-config-tab-contents-inactive">
- <div id="sync-encryption-instructions"
- i18n-content="encryptionInstructions"></div>
-
- <div>
- <input id="google-option" name="option" type="radio"
- value="google" onchange="onRadioChange();">
- <label for="google-option" i18n-content="googleOption"></label>
- </input>
- </div>
- <div>
- <input id="explicit-option" name="option" type="radio" value="explicit"
- onchange="onRadioChange();">
- <div id="learn-more-link">
- <a i18n-values="href:encryptionhelpurl" target="_blank"
- i18n-content="learnmore"></a>
- </div>
- <label for="explicit-option" i18n-content="explicitOption"></label>
- </input>
- </div>
-
- <div class="sync-section" id="section-google">
- <div i18n-content="sectionGoogleMessage"></div>
- </div>
- <div class="sync-section" id="section-explicit">
- <div i18n-content="sectionExplicitMessage" id="explicit-message"></div>
- <div>
- <div i18n-content="passphraseLabel" id="passphraseLabel"></div>
- <input id="passphrase" name="passphrase" label="passphraseLabel"
- type="password" />
- </div>
- <div>
- <div i18n-content="confirmLabel" id="confirmPassphraseLabel">
+ <hr>
+ <h4 i18n-content="passphraseSectionTitle"></h4>
+ <div id="sync-passphrase-container">
+ <div>
+ <input id="google-option" name="option" type="radio" value="google"
+ onchange="onRadioChange();">
+ <label for="google-option" i18n-content="googleOption"></label>
+ </div>
+ <div>
+ <input id="explicit-option" name="option" type="radio"
+ value="explicit" onchange="onRadioChange();">
+ <label for="explicit-option" i18n-content="explicitOption"></label>
+ <a i18n-values="href:encryptionhelpurl" target="_blank"
+ i18n-content="learnmore"></a>
+ </div>
+ <div id="sync-custom-passphrase" hidden>
+ <div id="sync-passphrase-message">
+ <span i18n-content="sectionExplicitMessagePrefix"></span>
+ <a href="http://google.com/dashboard" target="_blank"
+ i18n-content="sectionExplicitMessagePostfix"></a>
+ <span>.</span>
+ </div>
+ <div class="sync-custom-passphrase-input">
+ <input id="passphrase" type="password"
+ i18n-values="placeholder:passphraseLabel">
+ </div>
+ <div class="sync-custom-passphrase-input">
+ <input id="confirm-passphrase" type="password"
+ i18n-values="placeholder:confirmLabel">
+ </div>
+ <div class="error" style="display:none"
+ id="emptyerror" i18n-content="emptyErrorMessage"></div>
+ <div class="error" style="display:none"
+ id="mismatcherror" i18n-content="mismatchErrorMessage"></div>
</div>
- <input id="confirmpassphrase" name="confirmpassphrase" type="password"
- label="confirmPassphraseLabel" />
</div>
- <div class="error" style="display:none"
- id="emptyerror" i18n-content="emptyErrorMessage"></div>
- <div class="error" style="display:none"
- id="mismatcherror" i18n-content="mismatchErrorMessage"></div>
- </div>
-
- <div id="change-passphrase">
- <div id="sync-passphrase-warning" i18n-content="passphraseWarning">
+ <div class="action-area">
+ <div class="action-area-link-container">
+ <a id="use-default-link" href="#" i18n-content="useDefaultSettings"
+ onclick="showSyncEverythingPage();"></a>
+ </div>
+ <input id="okButton" type="submit" i18n-values="value:ok" />
+ <input id="cancelButton" type="button" i18n-values="value:cancel"
+ onclick='chrome.send("DialogClose", [""])' />
</div>
- <a id="clear-data-link" i18n-content="cleardatalink" href="#"
- onclick='goToDashboard(); return false;'></a>
</div>
- </div>
-
- <div class="sync-footer">
- <input id="okButton" type="submit" i18n-values="value:ok" />
- <input id="cancelButton" type="button" i18n-values="value:cancel"
- onclick='chrome.send("DialogClose", [""])' />
- </div>
-</form>
+ </form>
+</div>
</body>
</html>
diff --git a/chrome/browser/sync/resources/firstpassphrase.html b/chrome/browser/sync/resources/firstpassphrase.html
index 15156ee..adb8b42 100644
--- a/chrome/browser/sync/resources/firstpassphrase.html
+++ b/chrome/browser/sync/resources/firstpassphrase.html
@@ -43,7 +43,9 @@ form {
#learn-more-link {
float: right;
}
-
+html[dir='rtl'] #learn-more-link {
+ float: left;
+}
html[dir='rtl'] .sync-footer {
text-align: left;
left: 0px;
@@ -144,51 +146,6 @@ html[os='mac'] input[type='submit'] {
</script>
</head>
<body i18n-values=".style.fontFamily:fontfamily" onload="setupDialog();">
-<form id="form" onSubmit="sendValuesAndClose(); return false;">
- <div class="sync-header" id="title" i18n-content="title"></div>
- <div class="sync-instructions" id="instructions"
- i18n-content="instructions"></div>
- <div>
- <input name="option" type="radio" value="google"
- id="google-option" onchange="onRadioChange();">
- <label for="google-option" i18n-content="googleOption"></label>
- </input>
- </div>
- <div>
- <input name="option" type="radio" value="explicit"
- id="explicit-option" onchange="onRadioChange();">
- <div id="learn-more-link">
- <a i18n-values="href:encryptionhelpurl" target="_blank"
- i18n-content="learnmore"></a>
- </div>
- <label for="explicit-option" i18n-content="explicitOption"></label>
- </input>
- </div>
-
- <div class="sync-section" id="section-explicit">
- <div i18n-content="sectionExplicitMessage"></div>
- <div>
- <div i18n-content="passphraseLabel" id="passphraseLabel"></div>
- <input id="passphrase" name="passphrase" label="passphraseLabel"
- type="password"/>
- </div>
- <div>
- <div i18n-content="confirmLabel" id="confirmPassphraseLabel">
- </div>
- <input id="confirmpassphrase" name="confirmpassphrase" type="password"
- label="confirmPassphraseLabel" />
- </div>
- <div class="error" style="display:none"
- id="emptyerror" i18n-content="emptyErrorMessage"></div>
- <div class="error" style="display:none"
- id="mismatcherror" i18n-content="mismatchErrorMessage"></div>
- </div>
- <div class="sync-footer">
- <input id="okButton" type="submit" i18n-values="value:syncpasswords" />
- <input id="noThanksButton" type="submit" i18n-values="value:nothanks"
- onclick="optOutOfPasswordsAndClose(); return false;"/>
- </div>
-</form>
</body>
</html>
diff --git a/chrome/browser/sync/resources/gaia_login.css b/chrome/browser/sync/resources/gaia_login.css
index a442f55..2237ca6 100644
--- a/chrome/browser/sync/resources/gaia_login.css
+++ b/chrome/browser/sync/resources/gaia_login.css
@@ -38,6 +38,8 @@ input[type='submit'] {
#gaia-account-text {
font-weight: bold;
+ position: relative;
+ top: -7px;
}
#email-readonly {
diff --git a/chrome/browser/sync/resources/gaia_login.html b/chrome/browser/sync/resources/gaia_login.html
index fb3910d..5f57c83 100644
--- a/chrome/browser/sync/resources/gaia_login.html
+++ b/chrome/browser/sync/resources/gaia_login.html
@@ -7,9 +7,6 @@
<body i18n-values=".style.fontFamily:fontfamily;.style.fontSize:fontsize">
<div>
<div>
- <div id="top-blurb">
- <span i18n-content="introduction"></span>
- </div>
<div id="top-blurb-error">
<span id="error-signing-in" i18n-content="errorsigningin"></span>
<span id="error-custom"></span>
@@ -26,17 +23,19 @@
cellpadding="1" cellspacing="0">
<tr>
<td colspan="2" align="center">
- <table>
- <tr>
- <td id="gaia-logo" valign="top">
+ <div>
+ <div>
+ <span class="gaia-le-lbl" i18n-content="signinprefix">
+ </span>
+ </div>
+ <div>
+ <span id="gaia-logo">
<img src="google_transparent.png" alt="Google">
- </td>
- <td valign="middle">
- <span id="gaia-account-text"
- i18n-content="signinsuffix"></span>
- </td>
- </tr>
- </table>
+ </span>
+ <span id="gaia-account-text"
+ i18n-content="signinsuffix"></span>
+ </div>
+ </div>
</td>
</tr>
<tr>
diff --git a/chrome/browser/sync/resources/gaia_login.js b/chrome/browser/sync/resources/gaia_login.js
index 5b765b3..f19dd29 100644
--- a/chrome/browser/sync/resources/gaia_login.js
+++ b/chrome/browser/sync/resources/gaia_login.js
@@ -141,8 +141,8 @@ function load() {
if (googleIsAtEndOfSentence == ltr) {
// We're in ltr and in the translation the word 'Google' is AFTER the
// word 'Account' OR we're in rtl and 'Google' is BEFORE 'Account'.
- var logo_td = document.getElementById('gaia-logo');
- logo_td.parentNode.appendChild(logo_td);
+ var logo_div = document.getElementById('gaia-logo');
+ logo_div.parentNode.appendChild(logo_div);
}
acct_text.textContent = translated_text.replace('Google','');
}
diff --git a/chrome/browser/sync/resources/passphrase.html b/chrome/browser/sync/resources/passphrase.html
index e92df66..954124a 100644
--- a/chrome/browser/sync/resources/passphrase.html
+++ b/chrome/browser/sync/resources/passphrase.html
@@ -19,10 +19,7 @@ form {
font-weight: bold;
margin-bottom: 10px;
}
-.sync-instructions-start-hidden {
- margin-top: 10px;
- display: none;
-}
+
.sync-footer {
position: fixed;
right: 0px;
@@ -76,6 +73,9 @@ html[os='mac'] input[type='submit'] {
float: right;
margin-left: 5px;
}
+#sync-passphrase-warning {
+ margin-bottom: 5px;
+}
</style>
<script src="chrome://resources/js/cr.js"></script>
<script>
@@ -160,7 +160,7 @@ html[os='mac'] input[type='submit'] {
<input id="cancel-no-button" type="submit" i18n-values="value:no"
onclick="hideCancelWarning(); return false;" />
<input id="cancel-yes-button" type="submit" i18n-values="value:yes"
- onclick="chrome.send('PassphraseCancel', ['']);" />
+ onclick="chrome.send('PassphraseCancel', ['']); return false;" />
</div>
<div class="sync-footer">
diff --git a/chrome/browser/sync/sessions/session_state.cc b/chrome/browser/sync/sessions/session_state.cc
index 27457fb..02b6dce 100644
--- a/chrome/browser/sync/sessions/session_state.cc
+++ b/chrome/browser/sync/sessions/session_state.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -20,65 +20,16 @@ using std::vector;
namespace browser_sync {
namespace sessions {
-TypePayloadMap MakeTypePayloadMapFromBitSet(
- const syncable::ModelTypeBitSet& types,
- const std::string& payload) {
- TypePayloadMap types_with_payloads;
- for (size_t i = syncable::FIRST_REAL_MODEL_TYPE;
- i < types.size(); ++i) {
- if (types[i]) {
- types_with_payloads[syncable::ModelTypeFromInt(i)] = payload;
- }
- }
- return types_with_payloads;
-}
-
-TypePayloadMap MakeTypePayloadMapFromRoutingInfo(
- const ModelSafeRoutingInfo& routes,
- const std::string& payload) {
- TypePayloadMap types_with_payloads;
- for (ModelSafeRoutingInfo::const_iterator i = routes.begin();
- i != routes.end(); ++i) {
- types_with_payloads[i->first] = payload;
- }
- return types_with_payloads;
-}
-
-DictionaryValue* TypePayloadMapToValue(const TypePayloadMap& type_payloads) {
- DictionaryValue* value = new DictionaryValue();
- for (TypePayloadMap::const_iterator it = type_payloads.begin();
- it != type_payloads.end(); ++it) {
- value->SetString(syncable::ModelTypeToString(it->first), it->second);
- }
- return value;
-}
-
-void CoalescePayloads(TypePayloadMap* original,
- const TypePayloadMap& update) {
- for (TypePayloadMap::const_iterator i = update.begin();
- i != update.end(); ++i) {
- if (original->count(i->first) == 0) {
- // If this datatype isn't already in our map, add it with whatever payload
- // it has.
- (*original)[i->first] = i->second;
- } else if (i->second.length() > 0) {
- // If this datatype is already in our map, we only overwrite the payload
- // if the new one is non-empty.
- (*original)[i->first] = i->second;
- }
- }
-}
-
SyncSourceInfo::SyncSourceInfo()
: updates_source(sync_pb::GetUpdatesCallerInfo::UNKNOWN) {}
SyncSourceInfo::SyncSourceInfo(
- const TypePayloadMap& t)
+ const syncable::ModelTypePayloadMap& t)
: updates_source(sync_pb::GetUpdatesCallerInfo::UNKNOWN), types(t) {}
SyncSourceInfo::SyncSourceInfo(
const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource& u,
- const TypePayloadMap& t)
+ const syncable::ModelTypePayloadMap& t)
: updates_source(u), types(t) {}
SyncSourceInfo::~SyncSourceInfo() {}
@@ -87,7 +38,7 @@ DictionaryValue* SyncSourceInfo::ToValue() const {
DictionaryValue* value = new DictionaryValue();
value->SetString("updatesSource",
GetUpdatesSourceString(updates_source));
- value->Set("types", TypePayloadMapToValue(types));
+ value->Set("types", syncable::ModelTypePayloadMapToValue(types));
return value;
}
@@ -101,6 +52,9 @@ SyncerStatus::SyncerStatus()
num_tombstone_updates_downloaded_total(0) {
}
+SyncerStatus::~SyncerStatus() {
+}
+
DictionaryValue* SyncerStatus::ToValue() const {
DictionaryValue* value = new DictionaryValue();
value->SetBoolean("invalidStore", invalid_store);
diff --git a/chrome/browser/sync/sessions/session_state.h b/chrome/browser/sync/sessions/session_state.h
index 479cc7f..4256cd5 100644
--- a/chrome/browser/sync/sessions/session_state.h
+++ b/chrome/browser/sync/sessions/session_state.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -24,6 +24,7 @@
#include "chrome/browser/sync/engine/syncproto.h"
#include "chrome/browser/sync/sessions/ordered_commit_set.h"
#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
#include "chrome/browser/sync/syncable/syncable.h"
class DictionaryValue;
@@ -37,51 +38,28 @@ namespace sessions {
class UpdateProgress;
-// A container that contains a set of datatypes with possible string payloads.
-typedef std::map<syncable::ModelType, std::string> TypePayloadMap;
-
-// Helper utils for building TypePayloadMaps.
-// Make a TypePayloadMap from all the types in a ModelTypeBitSet using a
-// default payload.
-TypePayloadMap MakeTypePayloadMapFromBitSet(
- const syncable::ModelTypeBitSet& types,
- const std::string& payload);
-
-// Make a TypePayloadMap for all the enabled types in a ModelSafeRoutingInfo
-// using a default payload.
-TypePayloadMap MakeTypePayloadMapFromRoutingInfo(
- const ModelSafeRoutingInfo& routes,
- const std::string& payload);
-
-// Caller takes ownership of the returned dictionary.
-DictionaryValue* TypePayloadMapToValue(const TypePayloadMap& type_payloads);
-
-// Coalesce |update| into |original|, overwriting only when |update| has
-// a non-empty payload.
-void CoalescePayloads(TypePayloadMap* original, const TypePayloadMap& update);
-
// A container for the source of a sync session. This includes the update
// source, the datatypes triggering the sync session, and possible session
// specific payloads which should be sent to the server.
struct SyncSourceInfo {
SyncSourceInfo();
- SyncSourceInfo(
- const TypePayloadMap& t);
+ explicit SyncSourceInfo(const syncable::ModelTypePayloadMap& t);
SyncSourceInfo(
const sync_pb::GetUpdatesCallerInfo::GetUpdatesSource& u,
- const TypePayloadMap& t);
+ const syncable::ModelTypePayloadMap& t);
~SyncSourceInfo();
// Caller takes ownership of the returned dictionary.
DictionaryValue* ToValue() const;
sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source;
- TypePayloadMap types;
+ syncable::ModelTypePayloadMap types;
};
// Data pertaining to the status of an active Syncer object.
struct SyncerStatus {
SyncerStatus();
+ ~SyncerStatus();
// Caller takes ownership of the returned dictionary.
DictionaryValue* ToValue() const;
@@ -98,6 +76,10 @@ struct SyncerStatus {
// Download event counters.
int num_updates_downloaded_total;
int num_tombstone_updates_downloaded_total;
+
+ // If the syncer encountered a MIGRATION_DONE code, these are the types that
+ // the client must now "migrate", by purging and re-downloading all updates.
+ syncable::ModelTypeSet types_needing_local_migration;
};
// Counters for various errors that can occur repeatedly during a sync session.
diff --git a/chrome/browser/sync/sessions/session_state_unittest.cc b/chrome/browser/sync/sessions/session_state_unittest.cc
index 3ac832f..649d520 100644
--- a/chrome/browser/sync/sessions/session_state_unittest.cc
+++ b/chrome/browser/sync/sessions/session_state_unittest.cc
@@ -7,7 +7,7 @@
#include <string>
#include "base/base64.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "chrome/test/values_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -24,26 +24,14 @@ using test::ExpectStringValue;
class SessionStateTest : public testing::Test {};
-TEST_F(SessionStateTest, TypePayloadMapToValue) {
- TypePayloadMap payloads;
- payloads[syncable::BOOKMARKS] = "bookmarkpayload";
- payloads[syncable::APPS] = "";
-
- scoped_ptr<DictionaryValue> value(TypePayloadMapToValue(payloads));
- EXPECT_EQ(2u, value->size());
- ExpectStringValue("bookmarkpayload", *value, "Bookmarks");
- ExpectStringValue("", *value, "Apps");
- EXPECT_FALSE(value->HasKey("Preferences"));
-}
-
TEST_F(SessionStateTest, SyncSourceInfoToValue) {
sync_pb::GetUpdatesCallerInfo::GetUpdatesSource updates_source =
sync_pb::GetUpdatesCallerInfo::PERIODIC;
- TypePayloadMap types;
+ syncable::ModelTypePayloadMap types;
types[syncable::PREFERENCES] = "preferencespayload";
types[syncable::EXTENSIONS] = "";
scoped_ptr<DictionaryValue> expected_types_value(
- TypePayloadMapToValue(types));
+ syncable::ModelTypePayloadMapToValue(types));
SyncSourceInfo source_info(updates_source, types);
diff --git a/chrome/browser/sync/sessions/status_controller.cc b/chrome/browser/sync/sessions/status_controller.cc
index c8d6866..8266f0f 100644
--- a/chrome/browser/sync/sessions/status_controller.cc
+++ b/chrome/browser/sync/sessions/status_controller.cc
@@ -51,6 +51,11 @@ void StatusController::increment_num_updates_downloaded_by(int value) {
shared_.syncer_status.mutate()->num_updates_downloaded_total += value;
}
+void StatusController::set_types_needing_local_migration(
+ const syncable::ModelTypeSet& types) {
+ shared_.syncer_status.mutate()->types_needing_local_migration = types;
+}
+
void StatusController::increment_num_tombstone_updates_downloaded_by(
int value) {
shared_.syncer_status.mutate()->num_tombstone_updates_downloaded_total +=
diff --git a/chrome/browser/sync/sessions/status_controller.h b/chrome/browser/sync/sessions/status_controller.h
index 805ff28..d4c912a 100644
--- a/chrome/browser/sync/sessions/status_controller.h
+++ b/chrome/browser/sync/sessions/status_controller.h
@@ -212,6 +212,7 @@ class StatusController {
void increment_num_successful_bookmark_commits();
void increment_num_updates_downloaded_by(int value);
void increment_num_tombstone_updates_downloaded_by(int value);
+ void set_types_needing_local_migration(const syncable::ModelTypeSet& types);
void set_unsynced_handles(const std::vector<int64>& unsynced_handles);
void set_commit_set(const OrderedCommitSet& commit_set);
diff --git a/chrome/browser/sync/sessions/sync_session.cc b/chrome/browser/sync/sessions/sync_session.cc
index 52a44fa..a38e31a 100644
--- a/chrome/browser/sync/sessions/sync_session.cc
+++ b/chrome/browser/sync/sessions/sync_session.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,7 +10,7 @@ namespace browser_sync {
namespace sessions {
SyncSession::SyncSession(SyncSessionContext* context, Delegate* delegate,
- SyncSourceInfo source,
+ const SyncSourceInfo& source,
const ModelSafeRoutingInfo& routing_info,
const std::vector<ModelSafeWorker*>& workers)
: context_(context),
@@ -41,11 +41,14 @@ void SyncSession::Coalesce(const SyncSession& session) {
std::back_inserter(temp));
workers_.swap(temp);
- ModelSafeRoutingInfo temp_r;
- std::set_union(routing_info_.begin(), routing_info_.end(),
- session.routing_info_.begin(), session.routing_info_.end(),
- std::insert_iterator<ModelSafeRoutingInfo>(temp_r, temp_r.begin()));
- routing_info_.swap(temp_r);
+ // We have to update the model safe routing info to the union. In case the
+ // same key is present in both pick the one from session.
+ for (ModelSafeRoutingInfo::const_iterator it =
+ session.routing_info_.begin();
+ it != session.routing_info_.end();
+ ++it) {
+ routing_info_[it->first] = it->second;
+ }
}
void SyncSession::ResetTransientState() {
diff --git a/chrome/browser/sync/sessions/sync_session.h b/chrome/browser/sync/sessions/sync_session.h
index 693d652..6a34c91 100644
--- a/chrome/browser/sync/sessions/sync_session.h
+++ b/chrome/browser/sync/sessions/sync_session.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -21,7 +21,7 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/time.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/sessions/ordered_commit_set.h"
@@ -82,7 +82,7 @@ class SyncSession {
SyncSession(SyncSessionContext* context,
Delegate* delegate,
- SyncSourceInfo source,
+ const SyncSourceInfo& source,
const ModelSafeRoutingInfo& routing_info,
const std::vector<ModelSafeWorker*>& workers);
~SyncSession();
diff --git a/chrome/browser/sync/sessions/sync_session_context.cc b/chrome/browser/sync/sessions/sync_session_context.cc
index b5f072b..f0cbce4 100644
--- a/chrome/browser/sync/sessions/sync_session_context.cc
+++ b/chrome/browser/sync/sessions/sync_session_context.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -36,10 +36,5 @@ SyncSessionContext::~SyncSessionContext() {
}
}
-void SyncSessionContext::set_last_snapshot(
- const SyncSessionSnapshot& snapshot) {
- previous_session_snapshot_.reset(new SyncSessionSnapshot(snapshot));
-}
-
} // namespace sessions
} // namespace browser_sync
diff --git a/chrome/browser/sync/sessions/sync_session_context.h b/chrome/browser/sync/sessions/sync_session_context.h
index 3f402ae..a3a69ec 100644
--- a/chrome/browser/sync/sessions/sync_session_context.h
+++ b/chrome/browser/sync/sessions/sync_session_context.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -21,7 +21,7 @@
#include <string>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
#include "chrome/browser/sync/engine/syncer_types.h"
@@ -92,13 +92,6 @@ class SyncSessionContext {
previous_session_routing_info_ = info;
}
- // TODO(tim): Deprecated. Bug 26339.
- sessions::SyncSessionSnapshot* previous_session_snapshot() {
- return previous_session_snapshot_.get();
- }
-
- void set_last_snapshot(const SyncSessionSnapshot& snapshot);
-
void NotifyListeners(const SyncEngineEvent& event) {
FOR_EACH_OBSERVER(SyncEngineEventListener, listeners_,
OnSyncEngineEvent(event));
diff --git a/chrome/browser/sync/sessions/sync_session_unittest.cc b/chrome/browser/sync/sessions/sync_session_unittest.cc
index 0f433de..bbd2574 100644
--- a/chrome/browser/sync/sessions/sync_session_unittest.cc
+++ b/chrome/browser/sync/sessions/sync_session_unittest.cc
@@ -1,10 +1,10 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include "chrome/browser/sync/sessions/sync_session.h"
-#include "base/ref_counted.h"
+#include "base/memory/ref_counted.h"
#include "chrome/browser/sync/engine/conflict_resolver.h"
#include "chrome/browser/sync/engine/mock_model_safe_workers.h"
#include "chrome/browser/sync/engine/syncer_types.h"
@@ -266,12 +266,14 @@ TEST_F(SyncSessionTest, ResetTransientState) {
TEST_F(SyncSessionTest, Coalesce) {
std::vector<ModelSafeWorker*> workers_one, workers_two;
ModelSafeRoutingInfo routes_one, routes_two;
- TypePayloadMap one_type = sessions::MakeTypePayloadMapFromBitSet(
- ParamsMeaningJustOneEnabledType(),
- std::string());;
- TypePayloadMap all_types = sessions::MakeTypePayloadMapFromBitSet(
- ParamsMeaningAllEnabledTypes(),
- std::string());;
+ syncable::ModelTypePayloadMap one_type =
+ syncable::ModelTypePayloadMapFromBitSet(
+ ParamsMeaningJustOneEnabledType(),
+ std::string());
+ syncable::ModelTypePayloadMap all_types =
+ syncable::ModelTypePayloadMapFromBitSet(
+ ParamsMeaningAllEnabledTypes(),
+ std::string());
SyncSourceInfo source_one(sync_pb::GetUpdatesCallerInfo::PERIODIC, one_type);
SyncSourceInfo source_two(sync_pb::GetUpdatesCallerInfo::LOCAL, all_types);
@@ -302,15 +304,16 @@ TEST_F(SyncSessionTest, Coalesce) {
TEST_F(SyncSessionTest, MakeTypePayloadMapFromBitSet) {
syncable::ModelTypeBitSet types;
std::string payload = "test";
- TypePayloadMap types_with_payloads = MakeTypePayloadMapFromBitSet(types,
- payload);
+ syncable::ModelTypePayloadMap types_with_payloads =
+ syncable::ModelTypePayloadMapFromBitSet(types,
+ payload);
EXPECT_TRUE(types_with_payloads.empty());
types[syncable::BOOKMARKS] = true;
types[syncable::PASSWORDS] = true;
types[syncable::AUTOFILL] = true;
payload = "test2";
- types_with_payloads = MakeTypePayloadMapFromBitSet(types, payload);
+ types_with_payloads = syncable::ModelTypePayloadMapFromBitSet(types, payload);
ASSERT_EQ(3U, types_with_payloads.size());
EXPECT_EQ(types_with_payloads[syncable::BOOKMARKS], payload);
@@ -320,8 +323,8 @@ TEST_F(SyncSessionTest, MakeTypePayloadMapFromBitSet) {
TEST_F(SyncSessionTest, MakeTypePayloadMapFromRoutingInfo) {
std::string payload = "test";
- TypePayloadMap types_with_payloads
- = MakeTypePayloadMapFromRoutingInfo(routes_, payload);
+ syncable::ModelTypePayloadMap types_with_payloads
+ = syncable::ModelTypePayloadMapFromRoutingInfo(routes_, payload);
ASSERT_EQ(routes_.size(), types_with_payloads.size());
for (ModelSafeRoutingInfo::iterator iter = routes_.begin();
iter != routes_.end();
@@ -331,7 +334,7 @@ TEST_F(SyncSessionTest, MakeTypePayloadMapFromRoutingInfo) {
}
TEST_F(SyncSessionTest, CoalescePayloads) {
- TypePayloadMap original;
+ syncable::ModelTypePayloadMap original;
std::string empty_payload;
std::string payload1 = "payload1";
std::string payload2 = "payload2";
@@ -341,7 +344,7 @@ TEST_F(SyncSessionTest, CoalescePayloads) {
original[syncable::AUTOFILL] = payload2;
original[syncable::THEMES] = payload3;
- TypePayloadMap update;
+ syncable::ModelTypePayloadMap update;
update[syncable::BOOKMARKS] = empty_payload; // Same.
update[syncable::PASSWORDS] = empty_payload; // Overwrite with empty.
update[syncable::AUTOFILL] = payload1; // Overwrite with non-empty.
diff --git a/chrome/browser/sync/signin_manager.cc b/chrome/browser/sync/signin_manager.cc
index 43a6e44..b052175 100644
--- a/chrome/browser/sync/signin_manager.cc
+++ b/chrome/browser/sync/signin_manager.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,8 +9,8 @@
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/net/gaia/gaia_constants.h"
-#include "chrome/common/notification_service.h"
#include "chrome/common/pref_names.h"
+#include "content/common/notification_service.h"
const char kGetInfoEmailKey[] = "email";
diff --git a/chrome/browser/sync/signin_manager.h b/chrome/browser/sync/signin_manager.h
index 24eb553..603c323 100644
--- a/chrome/browser/sync/signin_manager.h
+++ b/chrome/browser/sync/signin_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -13,7 +13,7 @@
#include <string>
#include "base/logging.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/common/net/gaia/gaia_auth_consumer.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
diff --git a/chrome/browser/sync/sync_constants.cc b/chrome/browser/sync/sync_constants.cc
deleted file mode 100644
index 923d2b3..0000000
--- a/chrome/browser/sync/sync_constants.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2010 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.
-
-#include "chrome/browser/sync/sync_constants.h"
-
-namespace browser_sync {
-
-const char kSyncLegacyServiceUrl[] = "google:notifier";
-const char kSyncServiceUrl[] = "http://www.google.com/chrome/sync";
-const char kSyncLegacyServiceId[] = "notification";
-const char kSyncServiceId[] = "sync-ping";
-const int kSyncPriority = 200;
-const char kSyncServiceSpecificData[] = "sync-ping-p2p";
-
-} // namespace browser_sync
-
diff --git a/chrome/browser/sync/sync_constants.h b/chrome/browser/sync/sync_constants.h
deleted file mode 100644
index d395d25..0000000
--- a/chrome/browser/sync/sync_constants.h
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2010 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.
-
-#ifndef CHROME_BROWSER_SYNC_SYNC_CONSTANTS_H_
-#define CHROME_BROWSER_SYNC_SYNC_CONSTANTS_H_
-#pragma once
-
-namespace browser_sync {
-
-extern const char kSyncLegacyServiceUrl[];
-extern const char kSyncServiceUrl[];
-extern const char kSyncLegacyServiceId[];
-extern const char kSyncServiceId[];
-extern const int kSyncPriority;
-extern const char kSyncServiceSpecificData[];
-
-} // namespace browser_sync
-
-#endif // CHROME_BROWSER_SYNC_SYNC_CONSTANTS_H_
-
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
index cf7cc6e..0ce1d20 100644
--- a/chrome/browser/sync/sync_setup_flow.cc
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -15,419 +15,171 @@
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
+#include "chrome/browser/sync/sync_setup_flow_handler.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_dialogs.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
#include "chrome/common/pref_names.h"
+#include "chrome/common/url_constants.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
-#include "content/browser/webui/web_ui_util.h"
+#include "grit/generated_resources.h"
#include "grit/locale_settings.h"
#include "ui/base/l10n/l10n_font_util.h"
#include "ui/gfx/font.h"
-// XPath expression for finding specific iframes.
-static const wchar_t* kLoginIFrameXPath = L"//iframe[@id='login']";
-static const wchar_t* kChooseDataTypesIFrameXPath =
- L"//iframe[@id='configure']";
-static const wchar_t* kPassphraseIFrameXPath =
- L"//iframe[@id='passphrase']";
-static const wchar_t* kDoneIframeXPath = L"//iframe[@id='done']";
+namespace {
-SyncConfiguration::SyncConfiguration()
- : sync_everything(false),
- use_secondary_passphrase(false) {
-}
-
-SyncConfiguration::~SyncConfiguration() {}
-
-void FlowHandler::RegisterMessages() {
- web_ui_->RegisterMessageCallback("SubmitAuth",
- NewCallback(this, &FlowHandler::HandleSubmitAuth));
- web_ui_->RegisterMessageCallback("Configure",
- NewCallback(this, &FlowHandler::HandleConfigure));
- web_ui_->RegisterMessageCallback("Passphrase",
- NewCallback(this, &FlowHandler::HandlePassphraseEntry));
- web_ui_->RegisterMessageCallback("PassphraseCancel",
- NewCallback(this, &FlowHandler::HandlePassphraseCancel));
- web_ui_->RegisterMessageCallback("FirstPassphrase",
- NewCallback(this, &FlowHandler::HandleFirstPassphrase));
- web_ui_->RegisterMessageCallback("GoToDashboard",
- NewCallback(this, &FlowHandler::HandleGoToDashboard));
-}
-
-static bool GetAuthData(const std::string& json,
- std::string* username,
- std::string* password,
- std::string* captcha,
- std::string* access_code) {
- scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
- if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
- return false;
-
- DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
- if (!result->GetString("user", username) ||
- !result->GetString("pass", password) ||
- !result->GetString("captcha", captcha) ||
- !result->GetString("access_code", access_code)) {
- return false;
- }
- return true;
-}
-
-bool GetPassphrase(const std::string& json, std::string* passphrase) {
- scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
- if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
- return false;
-
- DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
- return result->GetString("passphrase", passphrase);
-}
-
-bool GetFirstPassphrase(const std::string& json,
- std::string* option,
- std::string* passphrase) {
- scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
- if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
- return false;
-
- DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
- return result->GetString("option", option) &&
- result->GetString("passphrase", passphrase);
-}
-
-static bool GetConfiguration(const std::string& json,
- SyncConfiguration* config) {
- scoped_ptr<Value> parsed_value(base::JSONReader::Read(json, false));
- if (!parsed_value.get() || !parsed_value->IsType(Value::TYPE_DICTIONARY))
- return false;
-
- DictionaryValue* result = static_cast<DictionaryValue*>(parsed_value.get());
- if (!result->GetBoolean("keepEverythingSynced", &config->sync_everything))
- return false;
-
- // These values need to be kept in sync with where they are written in
- // choose_datatypes.html.
- bool sync_bookmarks;
- if (!result->GetBoolean("syncBookmarks", &sync_bookmarks))
- return false;
- if (sync_bookmarks)
- config->data_types.insert(syncable::BOOKMARKS);
-
- bool sync_preferences;
- if (!result->GetBoolean("syncPreferences", &sync_preferences))
- return false;
- if (sync_preferences)
- config->data_types.insert(syncable::PREFERENCES);
-
- bool sync_themes;
- if (!result->GetBoolean("syncThemes", &sync_themes))
- return false;
- if (sync_themes)
- config->data_types.insert(syncable::THEMES);
-
- bool sync_passwords;
- if (!result->GetBoolean("syncPasswords", &sync_passwords))
- return false;
- if (sync_passwords)
- config->data_types.insert(syncable::PASSWORDS);
-
- bool sync_autofill;
- if (!result->GetBoolean("syncAutofill", &sync_autofill))
- return false;
- if (sync_autofill)
- config->data_types.insert(syncable::AUTOFILL);
-
- bool sync_extensions;
- if (!result->GetBoolean("syncExtensions", &sync_extensions))
- return false;
- if (sync_extensions)
- config->data_types.insert(syncable::EXTENSIONS);
-
- bool sync_typed_urls;
- if (!result->GetBoolean("syncTypedUrls", &sync_typed_urls))
- return false;
- if (sync_typed_urls)
- config->data_types.insert(syncable::TYPED_URLS);
-
- bool sync_sessions;
- if (!result->GetBoolean("syncSessions", &sync_sessions))
- return false;
- if (sync_sessions)
- config->data_types.insert(syncable::SESSIONS);
-
- bool sync_apps;
- if (!result->GetBoolean("syncApps", &sync_apps))
- return false;
- if (sync_apps)
- config->data_types.insert(syncable::APPS);
-
- // Encyption settings.
- if (!result->GetBoolean("usePassphrase", &config->use_secondary_passphrase))
- return false;
- if (config->use_secondary_passphrase &&
- !result->GetString("passphrase", &config->secondary_passphrase))
- return false;
-
- return true;
-}
-
-static void DisablePasswordSync(ProfileSyncService* service) {
+// Helper function to disable password sync.
+void DisablePasswordSync(ProfileSyncService* service) {
syncable::ModelTypeSet types;
service->GetPreferredDataTypes(&types);
types.erase(syncable::PASSWORDS);
service->OnUserChoseDatatypes(false, types);
}
-void FlowHandler::HandleSubmitAuth(const ListValue* args) {
- std::string json;
- if (!args->GetString(0, &json)) {
- NOTREACHED() << "Could not read JSON argument";
- return;
- }
- if (json.empty())
- return;
-
- std::string username, password, captcha, access_code;
- if (!GetAuthData(json, &username, &password, &captcha, &access_code)) {
- // The page sent us something that we didn't understand.
- // This probably indicates a programming error.
- NOTREACHED();
- return;
- }
+} // namespace
- if (flow_)
- flow_->OnUserSubmittedAuth(username, password, captcha, access_code);
+SyncConfiguration::SyncConfiguration()
+ : sync_everything(false),
+ use_secondary_passphrase(false) {
}
-void FlowHandler::HandleConfigure(const ListValue* args) {
- std::string json;
- if (!args->GetString(0, &json)) {
- NOTREACHED() << "Could not read JSON argument";
- return;
- }
- if (json.empty())
- return;
-
- SyncConfiguration configuration;
- if (!GetConfiguration(json, &configuration)) {
- // The page sent us something that we didn't understand.
- // This probably indicates a programming error.
- NOTREACHED();
- return;
- }
-
- DCHECK(flow_);
- flow_->OnUserConfigured(configuration);
+SyncConfiguration::~SyncConfiguration() {}
- return;
+SyncSetupFlow::~SyncSetupFlow() {
+ flow_handler_->SetFlow(NULL);
}
-void FlowHandler::HandlePassphraseEntry(const ListValue* args) {
- std::string json;
- if (!args->GetString(0, &json)) {
- NOTREACHED() << "Could not read JSON argument";
- return;
- }
- if (json.empty())
- return;
+// static
+SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
+ SyncSetupFlowContainer* container,
+ SyncSetupWizard::State start,
+ SyncSetupWizard::State end) {
+ DictionaryValue args;
+ if (start == SyncSetupWizard::GAIA_LOGIN)
+ SyncSetupFlow::GetArgsForGaiaLogin(service, &args);
+ else if (start == SyncSetupWizard::CONFIGURE)
+ SyncSetupFlow::GetArgsForConfigure(service, &args);
+ else if (start == SyncSetupWizard::ENTER_PASSPHRASE)
+ SyncSetupFlow::GetArgsForEnterPassphrase(false, false, &args);
+ else if (start == SyncSetupWizard::PASSPHRASE_MIGRATION)
+ args.SetString("iframeToShow", "firstpassphrase");
- std::string passphrase;
- if (!GetPassphrase(json, &passphrase)) {
- // Couldn't understand what the page sent. Indicates a programming error.
- NOTREACHED();
- return;
- }
+ std::string json_args;
+ base::JSONWriter::Write(&args, false, &json_args);
- DCHECK(flow_);
- flow_->OnPassphraseEntry(passphrase);
-}
+ SyncSetupFlow* flow = new SyncSetupFlow(start, end, json_args,
+ container, service);
-void FlowHandler::HandlePassphraseCancel(const ListValue* args) {
- DCHECK(flow_);
- flow_->OnPassphraseCancel();
+ Browser* b = BrowserList::GetLastActive();
+ b->ShowOptionsTab(chrome::kSyncSetupSubPage);
+ return flow;
}
-void FlowHandler::HandleFirstPassphrase(const ListValue* args) {
- std::string json;
- if (!args->GetString(0, &json)) {
- NOTREACHED() << "Could not read JSON argument";
- return;
- }
- if (json.empty())
- return;
-
- std::string option;
- std::string passphrase;
- if (!GetFirstPassphrase(json, &option, &passphrase)) {
- // Page sent result which couldn't be parsed. Programming error.
- NOTREACHED();
- return;
+// static
+void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service,
+ DictionaryValue* args) {
+ args->SetString("iframeToShow", "login");
+ const GoogleServiceAuthError& error = service->GetAuthError();
+ if (!service->last_attempted_user_email().empty()) {
+ args->SetString("user", service->last_attempted_user_email());
+ args->SetInteger("error", error.state());
+ args->SetBoolean("editable_user", true);
+ } else {
+ string16 user;
+ if (!service->cros_user().empty())
+ user = UTF8ToUTF16(service->cros_user());
+ else
+ user = service->GetAuthenticatedUsername();
+ args->SetString("user", user);
+ args->SetInteger("error", 0);
+ args->SetBoolean("editable_user", user.empty());
}
- DCHECK(flow_);
- flow_->OnFirstPassphraseEntry(option, passphrase);
-}
-
-void FlowHandler::HandleGoToDashboard(const ListValue* args) {
- flow_->OnGoToDashboard();
-}
-
-// Called by SyncSetupFlow::Advance.
-void FlowHandler::ShowGaiaLogin(const DictionaryValue& args) {
- // Whenever you start a wizard, you pass in an arg so it starts on the right
- // iframe (see setup_flow.html's showTheRightIframe() method). But when you
- // transition from one flow to another, you have to explicitly call the JS
- // function to show the next iframe.
- // So if you ever made a wizard that involved a gaia login as not the first
- // frame, this call would be necessary to ensure that this method actually
- // shows the gaia login.
- if (web_ui_)
- web_ui_->CallJavascriptFunction(L"showGaiaLoginIframe");
-
- std::string json;
- base::JSONWriter::Write(&args, false, &json);
- std::wstring javascript = std::wstring(L"showGaiaLogin") +
- L"(" + UTF8ToWide(json) + L");";
- ExecuteJavascriptInIFrame(kLoginIFrameXPath, javascript);
-}
-
-void FlowHandler::ShowGaiaSuccessAndClose() {
- ExecuteJavascriptInIFrame(kLoginIFrameXPath, L"showGaiaSuccessAndClose();");
-}
-
-void FlowHandler::ShowGaiaSuccessAndSettingUp() {
- ExecuteJavascriptInIFrame(kLoginIFrameXPath,
- L"showGaiaSuccessAndSettingUp();");
-}
-
-// Called by SyncSetupFlow::Advance.
-void FlowHandler::ShowConfigure(const DictionaryValue& args) {
- // If you're starting the wizard at the configure screen (i.e. from
- // "Customize Sync"), this will be redundant. However, if you're coming from
- // another wizard state, this will make sure Choose Data Types is on top.
- if (web_ui_)
- web_ui_->CallJavascriptFunction(L"showConfigure");
-
- std::string json;
- base::JSONWriter::Write(&args, false, &json);
- std::wstring javascript = std::wstring(L"initializeConfigureDialog") +
- L"(" + UTF8ToWide(json) + L");";
- ExecuteJavascriptInIFrame(kChooseDataTypesIFrameXPath, javascript);
-}
-
-void FlowHandler::ShowPassphraseEntry(const DictionaryValue& args) {
- if (web_ui_)
- web_ui_->CallJavascriptFunction(L"showPassphrase");
-
- std::string json;
- base::JSONWriter::Write(&args, false, &json);
- std::wstring script = std::wstring(L"setupPassphraseDialog") +
- L"(" + UTF8ToWide(json) + L");";
- ExecuteJavascriptInIFrame(kPassphraseIFrameXPath, script);
-}
-
-void FlowHandler::ShowFirstPassphrase(const DictionaryValue& args) {
- if (web_ui_)
- web_ui_->CallJavascriptFunction(L"showFirstPassphrase");
-
- std::string json;
- base::JSONWriter::Write(&args, false, &json);
- std::wstring script = std::wstring(L"setupDialog") +
- L"(" + UTF8ToWide(json) + L");";
- ExecuteJavascriptInIFrame(kPassphraseIFrameXPath, script);
+ args->SetString("captchaUrl", error.captcha().image_url.spec());
}
-void FlowHandler::ShowSettingUp() {
- if (web_ui_)
- web_ui_->CallJavascriptFunction(L"showSettingUp");
-}
+// static
+void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
+ DictionaryValue* args) {
+ args->SetString("iframeToShow", "configure");
-void FlowHandler::ShowSetupDone(const std::wstring& user) {
- StringValue synced_to_string(l10n_util::GetStringFUTF8(
- IDS_SYNC_NTP_SYNCED_TO, WideToUTF16Hack(user)));
- std::string json;
- base::JSONWriter::Write(&synced_to_string, false, &json);
- std::wstring javascript = std::wstring(L"setSyncedToUser") +
- L"(" + UTF8ToWide(json) + L");";
- ExecuteJavascriptInIFrame(kDoneIframeXPath, javascript);
+ // The SYNC_EVERYTHING case will set this to true.
+ args->SetBoolean("syncEverything", false);
- if (web_ui_)
- web_ui_->CallJavascriptFunction(L"showSetupDone", synced_to_string);
+ args->SetBoolean("keepEverythingSynced",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced));
- ExecuteJavascriptInIFrame(kDoneIframeXPath,
- L"onPageShown();");
-}
+ // Bookmarks, Preferences, and Themes are launched for good, there's no
+ // going back now. Check if the other data types are registered though.
+ syncable::ModelTypeSet registered_types;
+ service->GetRegisteredDataTypes(&registered_types);
+ args->SetBoolean("passwordsRegistered",
+ registered_types.count(syncable::PASSWORDS) > 0);
+ args->SetBoolean("autofillRegistered",
+ registered_types.count(syncable::AUTOFILL) > 0);
+ args->SetBoolean("extensionsRegistered",
+ registered_types.count(syncable::EXTENSIONS) > 0);
+ args->SetBoolean("typedUrlsRegistered",
+ registered_types.count(syncable::TYPED_URLS) > 0);
+ args->SetBoolean("appsRegistered",
+ registered_types.count(syncable::APPS) > 0);
+ args->SetBoolean("sessionsRegistered",
+ registered_types.count(syncable::SESSIONS) > 0);
+ args->SetBoolean("syncBookmarks",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks));
+ args->SetBoolean("syncPreferences",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPreferences));
+ args->SetBoolean("syncThemes",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncThemes));
+ args->SetBoolean("syncPasswords",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPasswords));
+ args->SetBoolean("syncAutofill",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAutofill));
+ args->SetBoolean("syncExtensions",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncExtensions));
+ args->SetBoolean("syncSessions",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncSessions));
+ args->SetBoolean("syncTypedUrls",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls));
+ args->SetBoolean("syncApps",
+ service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps));
-void FlowHandler::ShowFirstTimeDone(const std::wstring& user) {
- ExecuteJavascriptInIFrame(kDoneIframeXPath,
- L"setShowFirstTimeSetupSummary();");
- ShowSetupDone(user);
+ // Load the parameters for the encryption tab.
+ args->SetBoolean("usePassphrase", service->IsUsingSecondaryPassphrase());
}
-void FlowHandler::ExecuteJavascriptInIFrame(const std::wstring& iframe_xpath,
- const std::wstring& js) {
- if (web_ui_) {
- RenderViewHost* rvh = web_ui_->tab_contents()->render_view_host();
- rvh->ExecuteJavascriptInWebFrame(WideToUTF16Hack(iframe_xpath),
- WideToUTF16Hack(js));
- }
+// static
+void SyncSetupFlow::GetArgsForEnterPassphrase(
+ bool tried_creating_explicit_passphrase,
+ bool tried_setting_explicit_passphrase,
+ DictionaryValue* args) {
+ args->SetString("iframeToShow", "passphrase");
+ args->SetBoolean("passphrase_creation_rejected",
+ tried_creating_explicit_passphrase);
+ args->SetBoolean("passphrase_setting_rejected",
+ tried_setting_explicit_passphrase);
}
-// Use static Run method to get an instance.
-SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state,
- SyncSetupWizard::State end_state,
- const std::string& args,
- SyncSetupFlowContainer* container,
- ProfileSyncService* service)
- : container_(container),
- dialog_start_args_(args),
- current_state_(start_state),
- end_state_(end_state),
- login_start_time_(base::TimeTicks::Now()),
- flow_handler_(new FlowHandler()),
- owns_flow_handler_(true),
- service_(service),
- html_dialog_window_(NULL) {
- flow_handler_->set_flow(this);
+void SyncSetupFlow::AttachSyncSetupHandler(SyncSetupFlowHandler* handler) {
+ flow_handler_ = handler;
+ ActivateState(current_state_);
}
-SyncSetupFlow::~SyncSetupFlow() {
- flow_handler_->set_flow(NULL);
- if (owns_flow_handler_) {
- delete flow_handler_;
+void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
+ if (!ShouldAdvance(advance_state)) {
+ LOG(WARNING) << "Invalid state change from "
+ << current_state_ << " to " << advance_state;
+ return;
}
-}
-void SyncSetupFlow::GetDialogSize(gfx::Size* size) const {
- PrefService* prefs = service_->profile()->GetPrefs();
- gfx::Font approximate_web_font = gfx::Font(
- UTF8ToUTF16(prefs->GetString(prefs::kWebKitSansSerifFontFamily)),
- prefs->GetInteger(prefs::kWebKitDefaultFontSize));
-
- *size = ui::GetLocalizedContentsSizeForFont(
- IDS_SYNC_SETUP_WIZARD_WIDTH_CHARS,
- IDS_SYNC_SETUP_WIZARD_HEIGHT_LINES,
- approximate_web_font);
-
-#if defined(OS_MACOSX)
- // NOTE(akalin): This is a hack to work around a problem with font height on
- // windows. Basically font metrics are incorrectly returned in logical units
- // instead of pixels on Windows. Logical units are very commonly 96 DPI
- // so our localized char/line counts are too small by a factor of 96/72.
- // So we compensate for this on non-windows platform.
- //
- // TODO(akalin): Remove this hack once we fix the windows font problem (or at
- // least work around it in some other place).
- float scale_hack = 96.f/72.f;
- size->set_width(size->width() * scale_hack);
- size->set_height(size->height() * scale_hack);
-#endif
+ ActivateState(advance_state);
}
-std::string SyncSetupFlow::GetDialogArgs() const {
- return dialog_start_args_;
+void SyncSetupFlow::Focus() {
+ // TODO(jhawkins): Implement this.
}
// A callback to notify the delegate that the dialog closed.
@@ -452,6 +204,8 @@ void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
case SyncSetupWizard::CONFIGURE:
case SyncSetupWizard::ENTER_PASSPHRASE:
case SyncSetupWizard::SETTING_UP:
+ // TODO(atwilson): Treat a close during ENTER_PASSPHRASE like a
+ // Cancel + Skip (i.e. call OnPassphraseCancel()). http://crbug.com/74645
ProfileSyncService::SyncEvent(
ProfileSyncService::CANCEL_DURING_CONFIGURE);
break;
@@ -470,108 +224,73 @@ void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
delete this;
}
-std::wstring SyncSetupFlow::GetDialogTitle() const {
- return UTF16ToWideHack(
- l10n_util::GetStringUTF16(IDS_SYNC_MY_BOOKMARKS_LABEL));
+void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username,
+ const std::string& password,
+ const std::string& captcha,
+ const std::string& access_code) {
+ service_->OnUserSubmittedAuth(username, password, captcha, access_code);
}
-bool SyncSetupFlow::IsDialogModal() const {
- return false;
-}
+void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
+ // Go to the "loading..." screen.
+ Advance(SyncSetupWizard::SETTING_UP);
-bool SyncSetupFlow::ShouldShowDialogTitle() const {
- return true;
-}
+ // If we are activating the passphrase, we need to have one supplied.
+ DCHECK(service_->IsUsingSecondaryPassphrase() ||
+ !configuration.use_secondary_passphrase ||
+ configuration.secondary_passphrase.length() > 0);
-// static
-void SyncSetupFlow::GetArgsForGaiaLogin(const ProfileSyncService* service,
- DictionaryValue* args) {
- args->SetString("iframeToShow", "login");
- const GoogleServiceAuthError& error = service->GetAuthError();
- if (!service->last_attempted_user_email().empty()) {
- args->SetString("user", service->last_attempted_user_email());
- args->SetInteger("error", error.state());
- args->SetBoolean("editable_user", true);
- } else {
- string16 user;
- if (!service->cros_user().empty())
- user = UTF8ToUTF16(service->cros_user());
- else
- user = service->GetAuthenticatedUsername();
- args->SetString("user", user);
- args->SetInteger("error", 0);
- args->SetBoolean("editable_user", user.empty());
+ if (configuration.use_secondary_passphrase &&
+ !service_->IsUsingSecondaryPassphrase()) {
+ service_->SetPassphrase(configuration.secondary_passphrase, true, true);
+ tried_creating_explicit_passphrase_ = true;
}
- args->SetString("captchaUrl", error.captcha().image_url.spec());
+ service_->OnUserChoseDatatypes(configuration.sync_everything,
+ configuration.data_types);
}
-// static
-void SyncSetupFlow::GetArgsForEnterPassphrase(
- const ProfileSyncService* service, DictionaryValue* args) {
- args->SetString("iframeToShow", "passphrase");
- args->SetBoolean("passphrase_creation_rejected",
- service->tried_creating_explicit_passphrase());
- args->SetBoolean("passphrase_setting_rejected",
- service->tried_setting_explicit_passphrase());
+void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
+ Advance(SyncSetupWizard::SETTING_UP);
+ service_->SetPassphrase(passphrase, true, false);
+ tried_setting_explicit_passphrase_ = true;
}
-// static
-void SyncSetupFlow::GetArgsForConfigure(ProfileSyncService* service,
- DictionaryValue* args) {
- args->SetString("iframeToShow", "configure");
-
- // By default start on the data types tab.
- args->SetString("initialTab", "data-type");
+void SyncSetupFlow::OnPassphraseCancel() {
+ // If the user cancels when being asked for the passphrase,
+ // just disable encrypted sync and continue setting up.
+ if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE)
+ DisablePasswordSync(service_);
- args->SetBoolean("keepEverythingSynced",
- service->profile()->GetPrefs()->GetBoolean(prefs::kKeepEverythingSynced));
+ Advance(SyncSetupWizard::SETTING_UP);
+}
- // Bookmarks, Preferences, and Themes are launched for good, there's no
- // going back now. Check if the other data types are registered though.
- syncable::ModelTypeSet registered_types;
- service->GetRegisteredDataTypes(&registered_types);
- args->SetBoolean("passwordsRegistered",
- registered_types.count(syncable::PASSWORDS) > 0);
- args->SetBoolean("autofillRegistered",
- registered_types.count(syncable::AUTOFILL) > 0);
- args->SetBoolean("extensionsRegistered",
- registered_types.count(syncable::EXTENSIONS) > 0);
- args->SetBoolean("typedUrlsRegistered",
- registered_types.count(syncable::TYPED_URLS) > 0);
- args->SetBoolean("appsRegistered",
- registered_types.count(syncable::APPS) > 0);
- args->SetBoolean("sessionsRegistered",
- registered_types.count(syncable::SESSIONS) > 0);
- args->SetBoolean("syncBookmarks",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncBookmarks));
- args->SetBoolean("syncPreferences",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPreferences));
- args->SetBoolean("syncThemes",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncThemes));
- args->SetBoolean("syncPasswords",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncPasswords));
- args->SetBoolean("syncAutofill",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncAutofill));
- args->SetBoolean("syncExtensions",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncExtensions));
- args->SetBoolean("syncSessions",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncSessions));
- args->SetBoolean("syncTypedUrls",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncTypedUrls));
- args->SetBoolean("syncApps",
- service->profile()->GetPrefs()->GetBoolean(prefs::kSyncApps));
+// TODO(jhawkins): Remove this method.
+void SyncSetupFlow::OnFirstPassphraseEntry(const std::string& option,
+ const std::string& passphrase) {
+ NOTREACHED();
+}
- // Load the parameters for the encryption tab.
- args->SetBoolean("usePassphrase", service->IsUsingSecondaryPassphrase());
+// TODO(jhawkins): Use this method instead of a direct link in the html.
+void SyncSetupFlow::OnGoToDashboard() {
+ BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
}
-void SyncSetupFlow::GetWebUIMessageHandlers(
- std::vector<WebUIMessageHandler*>* handlers) const {
- handlers->push_back(flow_handler_);
- // We don't own flow_handler_ anymore, but it sticks around until at least
- // right after OnDialogClosed() is called (and this object is destroyed).
- owns_flow_handler_ = false;
+// Use static Run method to get an instance.
+SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state,
+ SyncSetupWizard::State end_state,
+ const std::string& args,
+ SyncSetupFlowContainer* container,
+ ProfileSyncService* service)
+ : container_(container),
+ dialog_start_args_(args),
+ current_state_(start_state),
+ end_state_(end_state),
+ login_start_time_(base::TimeTicks::Now()),
+ flow_handler_(NULL),
+ service_(service),
+ tried_creating_explicit_passphrase_(false),
+ tried_setting_explicit_passphrase_(false) {
}
// Returns true if the flow should advance to |state| based on |current_state_|.
@@ -583,17 +302,22 @@ bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
current_state_ == SyncSetupWizard::SETTING_UP;
case SyncSetupWizard::GAIA_SUCCESS:
return current_state_ == SyncSetupWizard::GAIA_LOGIN;
+ case SyncSetupWizard::SYNC_EVERYTHING:
case SyncSetupWizard::CONFIGURE:
return current_state_ == SyncSetupWizard::GAIA_SUCCESS;
case SyncSetupWizard::ENTER_PASSPHRASE:
- return current_state_ == SyncSetupWizard::CONFIGURE ||
+ return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
+ current_state_ == SyncSetupWizard::CONFIGURE ||
current_state_ == SyncSetupWizard::SETTING_UP;
case SyncSetupWizard::PASSPHRASE_MIGRATION:
return current_state_ == SyncSetupWizard::GAIA_LOGIN;
case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR:
- return current_state_ == SyncSetupWizard::CONFIGURE;
+ DCHECK(current_state_ != SyncSetupWizard::GAIA_LOGIN &&
+ current_state_ != SyncSetupWizard::GAIA_SUCCESS);
+ return true;
case SyncSetupWizard::SETTING_UP:
- return current_state_ == SyncSetupWizard::CONFIGURE ||
+ return current_state_ == SyncSetupWizard::SYNC_EVERYTHING ||
+ current_state_ == SyncSetupWizard::CONFIGURE ||
current_state_ == SyncSetupWizard::ENTER_PASSPHRASE ||
current_state_ == SyncSetupWizard::PASSPHRASE_MIGRATION;
case SyncSetupWizard::FATAL_ERROR:
@@ -608,14 +332,8 @@ bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
}
}
-void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
- if (!ShouldAdvance(advance_state)) {
- LOG(WARNING) << "Invalid state change from "
- << current_state_ << " to " << advance_state;
- return;
- }
-
- switch (advance_state) {
+void SyncSetupFlow::ActivateState(SyncSetupWizard::State state) {
+ switch (state) {
case SyncSetupWizard::GAIA_LOGIN: {
DictionaryValue args;
SyncSetupFlow::GetArgsForGaiaLogin(service_, &args);
@@ -627,18 +345,27 @@ void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
flow_handler_->ShowGaiaSuccessAndClose();
break;
}
- advance_state = SyncSetupWizard::CONFIGURE;
+ state = SyncSetupWizard::SYNC_EVERYTHING;
// Fall through.
+ case SyncSetupWizard::SYNC_EVERYTHING: {
+ DictionaryValue args;
+ SyncSetupFlow::GetArgsForConfigure(service_, &args);
+ args.SetBoolean("syncEverything", true);
+ flow_handler_->ShowConfigure(args);
+ break;
+ }
case SyncSetupWizard::CONFIGURE: {
DictionaryValue args;
SyncSetupFlow::GetArgsForConfigure(service_, &args);
- args.SetString("initialTab", "data-type");
flow_handler_->ShowConfigure(args);
break;
}
case SyncSetupWizard::ENTER_PASSPHRASE: {
DictionaryValue args;
- SyncSetupFlow::GetArgsForEnterPassphrase(service_, &args);
+ SyncSetupFlow::GetArgsForEnterPassphrase(
+ tried_creating_explicit_passphrase_,
+ tried_setting_explicit_passphrase_,
+ &args);
flow_handler_->ShowPassphraseEntry(args);
break;
}
@@ -677,124 +404,7 @@ void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
UTF16ToWide(service_->GetAuthenticatedUsername()));
break;
default:
- NOTREACHED() << "Invalid advance state: " << advance_state;
- }
- current_state_ = advance_state;
-}
-
-void SyncSetupFlow::Focus() {
-#if defined(OS_MACOSX)
- if (html_dialog_window_) {
- platform_util::ActivateWindow(html_dialog_window_);
- }
-#else
- // TODO(csilv): We don't currently have a way to get the reference to the
- // dialog on windows/linux. This can be resolved by a cross platform
- // implementation of HTML dialogs as described by akalin below.
- NOTIMPLEMENTED();
-#endif // defined(OS_MACOSX)
-}
-
-GURL SyncSetupFlow::GetDialogContentURL() const {
- return GURL("chrome://syncresources/setup");
-}
-
-// static
-SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
- SyncSetupFlowContainer* container,
- SyncSetupWizard::State start,
- SyncSetupWizard::State end,
- gfx::NativeWindow parent_window) {
- DictionaryValue args;
- if (start == SyncSetupWizard::GAIA_LOGIN)
- SyncSetupFlow::GetArgsForGaiaLogin(service, &args);
- else if (start == SyncSetupWizard::CONFIGURE)
- SyncSetupFlow::GetArgsForConfigure(service, &args);
- else if (start == SyncSetupWizard::ENTER_PASSPHRASE)
- SyncSetupFlow::GetArgsForEnterPassphrase(service, &args);
- else if (start == SyncSetupWizard::PASSPHRASE_MIGRATION)
- args.SetString("iframeToShow", "firstpassphrase");
-
- std::string json_args;
- base::JSONWriter::Write(&args, false, &json_args);
-
- SyncSetupFlow* flow = new SyncSetupFlow(start, end, json_args,
- container, service);
-#if defined(OS_MACOSX)
- // TODO(akalin): Figure out a cleaner way to do this than to have this
- // gross per-OS behavior, i.e. have a cross-platform ShowHtmlDialog()
- // function that is not tied to a browser instance. Note that if we do
- // that, we'll have to fix sync_setup_wizard_unittest.cc as it relies on
- // being able to intercept ShowHtmlDialog() calls.
- flow->html_dialog_window_ = browser::ShowHtmlDialog(NULL, service->profile(),
- flow);
-#else
- Browser* b = BrowserList::GetLastActive();
- if (b) {
- b->BrowserShowHtmlDialog(flow, parent_window);
- } else {
- delete flow;
- return NULL;
- }
-#endif // defined(OS_MACOSX)
- return flow;
-}
-
-void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username,
- const std::string& password,
- const std::string& captcha,
- const std::string& access_code) {
- service_->OnUserSubmittedAuth(username, password, captcha, access_code);
-}
-
-void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
- // Go to the "loading..." screen."
- Advance(SyncSetupWizard::SETTING_UP);
-
- // If we are activating the passphrase, we need to have one supplied.
- DCHECK(service_->IsUsingSecondaryPassphrase() ||
- !configuration.use_secondary_passphrase ||
- configuration.secondary_passphrase.length() > 0);
-
- if (configuration.use_secondary_passphrase &&
- !service_->IsUsingSecondaryPassphrase()) {
- service_->SetPassphrase(configuration.secondary_passphrase, true, true);
+ NOTREACHED() << "Invalid advance state: " << state;
}
-
- service_->OnUserChoseDatatypes(configuration.sync_everything,
- configuration.data_types);
-}
-
-void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase) {
- Advance(SyncSetupWizard::SETTING_UP);
- service_->SetPassphrase(passphrase, true, false);
-}
-
-void SyncSetupFlow::OnPassphraseCancel() {
- // If the user cancels when being asked for the passphrase,
- // just disable encrypted sync and continue setting up.
- if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE)
- DisablePasswordSync(service_);
-
- Advance(SyncSetupWizard::SETTING_UP);
-}
-
-void SyncSetupFlow::OnFirstPassphraseEntry(const std::string& option,
- const std::string& passphrase) {
- Advance(SyncSetupWizard::SETTING_UP);
-
- if (option == "explicit") {
- service_->SetPassphrase(passphrase, true, true);
- } else if (option == "nothanks") {
- // User opted out of encrypted sync, need to turn off encrypted
- // data types.
- DisablePasswordSync(service_);
- } else if (option == "google") {
- // Implicit passphrase already set up.
- Advance(SyncSetupWizard::DONE);
- }
-}
-
-void SyncSetupFlow::OnGoToDashboard() {
- BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
+ current_state_ = state;
}
diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h
index 65e0024..377df17 100644
--- a/chrome/browser/sync/sync_setup_flow.h
+++ b/chrome/browser/sync/sync_setup_flow.h
@@ -15,11 +15,10 @@
#include "chrome/browser/sync/sync_setup_wizard.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/ui/webui/html_dialog_ui.h"
-#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/native_widget_types.h"
-class FlowHandler;
+class SyncSetupFlowHandler;
class SyncSetupFlowContainer;
// A structure which contains all the configuration information for sync.
@@ -37,8 +36,9 @@ struct SyncConfiguration {
// The state machine used by SyncSetupWizard, exposed in its own header
// to facilitate testing of SyncSetupWizard. This class is used to open and
-// run the html dialog and deletes itself when the dialog closes.
-class SyncSetupFlow : public HtmlDialogUIDelegate {
+// run the sync setup overlay in tabbed options and deletes itself when the
+// overlay closes.
+class SyncSetupFlow {
public:
virtual ~SyncSetupFlow();
@@ -48,8 +48,7 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
static SyncSetupFlow* Run(ProfileSyncService* service,
SyncSetupFlowContainer* container,
SyncSetupWizard::State start,
- SyncSetupWizard::State end,
- gfx::NativeWindow parent_window);
+ SyncSetupWizard::State end);
// Fills |args| with "user" and "error" arguments by querying |service|.
static void GetArgsForGaiaLogin(
@@ -63,9 +62,12 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
// Fills |args| for the enter passphrase screen.
static void GetArgsForEnterPassphrase(
- const ProfileSyncService* service,
+ bool tried_creating_explicit_passphrase,
+ bool tried_setting_explicit_passphrase,
DictionaryValue* args);
+ void AttachSyncSetupHandler(SyncSetupFlowHandler* handler);
+
// Triggers a state machine transition to advance_state.
void Advance(SyncSetupWizard::State advance_state);
@@ -73,36 +75,6 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
// obscured by a browser window.
void Focus();
- // HtmlDialogUIDelegate implementation.
- // Get the HTML file path for the content to load in the dialog.
- virtual GURL GetDialogContentURL() const;
-
- // HtmlDialogUIDelegate implementation.
- virtual void GetWebUIMessageHandlers(
- std::vector<WebUIMessageHandler*>* handlers) const;
-
- // HtmlDialogUIDelegate implementation.
- // Get the size of the dialog.
- virtual void GetDialogSize(gfx::Size* size) const;
-
- // HtmlDialogUIDelegate implementation.
- // Gets the JSON string input to use when opening the dialog.
- virtual std::string GetDialogArgs() const;
-
- // HtmlDialogUIDelegate implementation.
- // A callback to notify the delegate that the dialog closed.
- virtual void OnDialogClosed(const std::string& json_retval);
-
- // HtmlDialogUIDelegate implementation.
- virtual void OnCloseContents(TabContents* source, bool* out_close_dialog) {}
-
- // HtmlDialogUIDelegate implementation.
- virtual std::wstring GetDialogTitle() const;
-
- // HtmlDialogUIDelegate implementation.
- virtual bool IsDialogModal() const;
- virtual bool ShouldShowDialogTitle() const;
-
void OnUserSubmittedAuth(const std::string& username,
const std::string& password,
const std::string& captcha,
@@ -119,11 +91,14 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
// The 'first passphrase' screen is for users migrating from a build
// without passwords, who are prompted to make a passphrase choice.
+ // TODO(jhawkins): This is no longer used; remove this method.
void OnFirstPassphraseEntry(const std::string& option,
const std::string& passphrase);
void OnGoToDashboard();
+ void OnDialogClosed(const std::string& json_retval);
+
private:
FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, InitialStepLogin);
FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, ChooseDataTypesSetsPrefs);
@@ -141,7 +116,8 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
// Use static Run method to get an instance.
SyncSetupFlow(SyncSetupWizard::State start_state,
SyncSetupWizard::State end_state,
- const std::string& args, SyncSetupFlowContainer* container,
+ const std::string& args,
+ SyncSetupFlowContainer* container,
ProfileSyncService* service);
// Returns true if |this| should transition its state machine to |state|
@@ -149,6 +125,8 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
// a no-op.
bool ShouldAdvance(SyncSetupWizard::State state);
+ void ActivateState(SyncSetupWizard::State state);
+
SyncSetupFlowContainer* container_; // Our container. Don't own this.
std::string dialog_start_args_; // The args to pass to the initial page.
@@ -158,17 +136,16 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
// Time that the GAIA_LOGIN step was received.
base::TimeTicks login_start_time_;
- // The handler needed for the entire flow.
- FlowHandler* flow_handler_;
- mutable bool owns_flow_handler_;
+ // The handler needed for the entire flow. Weak reference.
+ SyncSetupFlowHandler* flow_handler_;
- // We need this to propagate back all user settings changes.
+ // We need this to propagate back all user settings changes. Weak reference.
ProfileSyncService* service_;
- // Currently used only on OS X
- // TODO(akalin): Add the necessary support to the other OSes and use
- // this for them.
- gfx::NativeWindow html_dialog_window_;
+ // Set to true if we've tried creating/setting an explicit passphrase, so we
+ // can appropriately reflect this in the UI.
+ bool tried_creating_explicit_passphrase_;
+ bool tried_setting_explicit_passphrase_;
DISALLOW_COPY_AND_ASSIGN(SyncSetupFlow);
};
@@ -191,45 +168,4 @@ class SyncSetupFlowContainer {
DISALLOW_COPY_AND_ASSIGN(SyncSetupFlowContainer);
};
-// The FlowHandler connects the state machine to the dialog backing HTML and
-// JS namespace by implementing WebUIMessageHandler and being invoked by the
-// SyncSetupFlow. Exposed here to facilitate testing.
-class FlowHandler : public WebUIMessageHandler {
- public:
- FlowHandler() {}
- virtual ~FlowHandler() {}
-
- // WebUIMessageHandler implementation.
- virtual void RegisterMessages();
-
- // Callbacks from the page.
- void HandleSubmitAuth(const ListValue* args);
- void HandleConfigure(const ListValue* args);
- void HandlePassphraseEntry(const ListValue* args);
- void HandlePassphraseCancel(const ListValue* args);
- void HandleFirstPassphrase(const ListValue* args);
- void HandleGoToDashboard(const ListValue* args);
-
- // These functions control which part of the HTML is visible.
- void ShowGaiaLogin(const DictionaryValue& args);
- void ShowGaiaSuccessAndClose();
- void ShowGaiaSuccessAndSettingUp();
- void ShowConfigure(const DictionaryValue& args);
- void ShowPassphraseEntry(const DictionaryValue& args);
- void ShowFirstPassphrase(const DictionaryValue& args);
- void ShowSettingUp();
- void ShowSetupDone(const std::wstring& user);
- void ShowFirstTimeDone(const std::wstring& user);
-
- void set_flow(SyncSetupFlow* flow) {
- flow_ = flow;
- }
-
- private:
- void ExecuteJavascriptInIFrame(const std::wstring& iframe_xpath,
- const std::wstring& js);
- SyncSetupFlow* flow_;
- DISALLOW_COPY_AND_ASSIGN(FlowHandler);
-};
-
#endif // CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_H_
diff --git a/chrome/browser/sync/sync_setup_flow_handler.h b/chrome/browser/sync/sync_setup_flow_handler.h
new file mode 100644
index 0000000..51455d0
--- /dev/null
+++ b/chrome/browser/sync/sync_setup_flow_handler.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_HANDLER_H_
+#define CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "content/browser/webui/web_ui.h"
+
+class DictionaryValue;
+class SyncSetupFlow;
+
+class SyncSetupFlowHandler {
+ public:
+ // These functions control which part of the HTML is visible.
+ virtual void ShowGaiaLogin(const DictionaryValue& args) = 0;
+ virtual void ShowGaiaSuccessAndClose() = 0;
+ virtual void ShowGaiaSuccessAndSettingUp() = 0;
+ virtual void ShowConfigure(const DictionaryValue& args) = 0;
+ virtual void ShowPassphraseEntry(const DictionaryValue& args) = 0;
+ virtual void ShowFirstPassphrase(const DictionaryValue& args) = 0;
+ virtual void ShowSettingUp() = 0;
+ virtual void ShowSetupDone(const std::wstring& user) = 0;
+ virtual void ShowFirstTimeDone(const std::wstring& user) = 0;
+
+ virtual void SetFlow(SyncSetupFlow* flow) = 0;
+
+ protected:
+ virtual ~SyncSetupFlowHandler() {}
+};
+
+#endif // CHROME_BROWSER_SYNC_SYNC_SETUP_FLOW_HANDLER_H_
diff --git a/chrome/browser/sync/sync_setup_wizard.cc b/chrome/browser/sync/sync_setup_wizard.cc
index 3201419..252aeff 100644
--- a/chrome/browser/sync/sync_setup_wizard.cc
+++ b/chrome/browser/sync/sync_setup_wizard.cc
@@ -4,276 +4,15 @@
#include "chrome/browser/sync/sync_setup_wizard.h"
-#include "base/message_loop.h"
-#include "base/singleton.h"
-#include "chrome/browser/google/google_util.h"
-#include "chrome/browser/prefs/pref_service.h"
-#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sync/profile_sync_service.h"
-#include "chrome/browser/sync/sync_setup_flow.h"
-#include "chrome/browser/ui/webui/chrome_url_data_manager.h"
-#include "chrome/common/jstemplate_builder.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/url_constants.h"
-#include "content/browser/browser_thread.h"
-#include "googleurl/src/gurl.h"
-#include "grit/app_resources.h"
-#include "grit/browser_resources.h"
-#include "grit/chromium_strings.h"
-#include "grit/locale_settings.h"
-#include "ui/base/resource/resource_bundle.h"
-
-namespace {
-
-// Utility method to keep dictionary population code streamlined.
-void AddString(DictionaryValue* dictionary,
- const std::string& key,
- int resource_id) {
- dictionary->SetString(key, l10n_util::GetStringUTF16(resource_id));
-}
-
-}
-
-class SyncResourcesSource : public ChromeURLDataManager::DataSource {
- public:
- SyncResourcesSource()
- : DataSource(chrome::kChromeUISyncResourcesHost, MessageLoop::current()) {
- }
-
- virtual void StartDataRequest(const std::string& path,
- bool is_off_the_record,
- int request_id);
-
- virtual std::string GetMimeType(const std::string& path) const {
- return "text/html";
- }
-
- static const char* kInvalidPasswordHelpUrl;
- static const char* kCanNotAccessAccountUrl;
- static const char* kCreateNewAccountUrl;
- static const char* kEncryptionHelpUrl;
-
- private:
- virtual ~SyncResourcesSource() {}
-
- // Takes a string containing an URL and returns an URL containing a CGI
- // parameter of the form "&hl=xy" where 'xy' is the language code of the
- // current locale.
- std::string GetLocalizedUrl(const std::string& url) const;
-
- DISALLOW_COPY_AND_ASSIGN(SyncResourcesSource);
-};
-
-const char* SyncResourcesSource::kInvalidPasswordHelpUrl =
- "http://www.google.com/support/accounts/bin/answer.py?ctx=ch&answer=27444";
-const char* SyncResourcesSource::kCanNotAccessAccountUrl =
- "http://www.google.com/support/accounts/bin/answer.py?answer=48598";
-#if defined(OS_CHROMEOS)
-const char* SyncResourcesSource::kEncryptionHelpUrl =
- "http://www.google.com/support/chromeos/bin/answer.py?answer=1181035";
-#else
-const char* SyncResourcesSource::kEncryptionHelpUrl =
- "http://www.google.com/support/chrome/bin/answer.py?answer=1181035";
-#endif
-const char* SyncResourcesSource::kCreateNewAccountUrl =
- "https://www.google.com/accounts/NewAccount?service=chromiumsync";
-
-void SyncResourcesSource::StartDataRequest(const std::string& path_raw,
- bool is_off_the_record, int request_id) {
- using l10n_util::GetStringUTF16;
- using l10n_util::GetStringFUTF16;
-
- const char kSyncSetupFlowPath[] = "setup";
- const char kSyncGaiaLoginPath[] = "gaialogin";
- const char kSyncConfigurePath[] = "configure";
- const char kSyncPassphrasePath[] = "passphrase";
- const char kSyncFirstPassphrasePath[] = "firstpassphrase";
- const char kSyncSettingUpPath[] = "settingup";
- const char kSyncSetupDonePath[] = "setupdone";
-
- std::string response;
- DictionaryValue strings;
- DictionaryValue* dict = &strings;
- int html_resource_id = 0;
- if (path_raw == kSyncGaiaLoginPath) {
- html_resource_id = IDR_GAIA_LOGIN_HTML;
-
- // Start by setting the per-locale URLs we show on the setup wizard.
- dict->SetString("invalidpasswordhelpurl",
- GetLocalizedUrl(kInvalidPasswordHelpUrl));
- dict->SetString("cannotaccessaccounturl",
- GetLocalizedUrl(kCanNotAccessAccountUrl));
- dict->SetString("createnewaccounturl",
- GetLocalizedUrl(kCreateNewAccountUrl));
- AddString(dict, "settingupsync", IDS_SYNC_LOGIN_SETTING_UP_SYNC);
- dict->SetString("introduction",
- GetStringFUTF16(IDS_SYNC_LOGIN_INTRODUCTION,
- GetStringUTF16(IDS_PRODUCT_NAME)));
- AddString(dict, "signinprefix", IDS_SYNC_LOGIN_SIGNIN_PREFIX);
- AddString(dict, "signinsuffix", IDS_SYNC_LOGIN_SIGNIN_SUFFIX);
- AddString(dict, "cannotbeblank", IDS_SYNC_CANNOT_BE_BLANK);
- AddString(dict, "emaillabel", IDS_SYNC_LOGIN_EMAIL);
- AddString(dict, "passwordlabel", IDS_SYNC_LOGIN_PASSWORD);
- AddString(dict, "invalidcredentials", IDS_SYNC_INVALID_USER_CREDENTIALS);
- AddString(dict, "signin", IDS_SYNC_SIGNIN);
- AddString(dict, "couldnotconnect", IDS_SYNC_LOGIN_COULD_NOT_CONNECT);
- AddString(dict, "cannotaccessaccount", IDS_SYNC_CANNOT_ACCESS_ACCOUNT);
- AddString(dict, "createaccount", IDS_SYNC_CREATE_ACCOUNT);
- AddString(dict, "cancel", IDS_CANCEL);
- AddString(dict, "settingup", IDS_SYNC_LOGIN_SETTING_UP);
- AddString(dict, "success", IDS_SYNC_SUCCESS);
- AddString(dict, "errorsigningin", IDS_SYNC_ERROR_SIGNING_IN);
- AddString(dict, "captchainstructions", IDS_SYNC_GAIA_CAPTCHA_INSTRUCTIONS);
- AddString(dict, "invalidaccesscode", IDS_SYNC_INVALID_ACCESS_CODE_LABEL);
- AddString(dict, "enteraccesscode", IDS_SYNC_ENTER_ACCESS_CODE_LABEL);
- AddString(dict, "getaccesscodehelp", IDS_SYNC_ACCESS_CODE_HELP_LABEL);
- AddString(dict, "getaccesscodeurl", IDS_SYNC_GET_ACCESS_CODE_URL);
- } else if (path_raw == kSyncConfigurePath) {
- html_resource_id = IDR_SYNC_CONFIGURE_HTML;
-
- AddString(dict, "dataTypes", IDS_SYNC_DATA_TYPES_TAB_NAME);
- AddString(dict, "encryption", IDS_SYNC_ENCRYPTION_TAB_NAME);
-
- // Stuff for the choose data types localized.
- AddString(dict, "choosedatatypesheader", IDS_SYNC_CHOOSE_DATATYPES_HEADER);
- dict->SetString("choosedatatypesinstructions",
- GetStringFUTF16(IDS_SYNC_CHOOSE_DATATYPES_INSTRUCTIONS,
- GetStringUTF16(IDS_PRODUCT_NAME)));
- AddString(dict, "keepeverythingsynced", IDS_SYNC_EVERYTHING);
- AddString(dict, "choosedatatypes", IDS_SYNC_CHOOSE_DATATYPES);
- AddString(dict, "bookmarks", IDS_SYNC_DATATYPE_BOOKMARKS);
- AddString(dict, "preferences", IDS_SYNC_DATATYPE_PREFERENCES);
- AddString(dict, "autofill", IDS_SYNC_DATATYPE_AUTOFILL);
- AddString(dict, "themes", IDS_SYNC_DATATYPE_THEMES);
- AddString(dict, "passwords", IDS_SYNC_DATATYPE_PASSWORDS);
- AddString(dict, "extensions", IDS_SYNC_DATATYPE_EXTENSIONS);
- AddString(dict, "typedurls", IDS_SYNC_DATATYPE_TYPED_URLS);
- AddString(dict, "apps", IDS_SYNC_DATATYPE_APPS);
- AddString(dict, "foreignsessions", IDS_SYNC_DATATYPE_SESSIONS);
- AddString(dict, "synczerodatatypeserror", IDS_SYNC_ZERO_DATA_TYPES_ERROR);
- AddString(dict, "abortederror", IDS_SYNC_SETUP_ABORTED_BY_PENDING_CLEAR);
+#include <stddef.h>
+#include <ostream>
- // Stuff for the encryption tab.
- dict->SetString("encryptionInstructions",
- GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS,
- GetStringUTF16(IDS_PRODUCT_NAME)));
- AddString(dict, "encryptAllLabel", IDS_SYNC_ENCRYPT_ALL_LABEL);
-
- AddString(dict, "googleOption", IDS_SYNC_PASSPHRASE_OPT_GOOGLE);
- AddString(dict, "explicitOption", IDS_SYNC_PASSPHRASE_OPT_EXPLICIT);
- AddString(dict, "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE);
- AddString(dict, "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT);
- AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL);
- AddString(dict, "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL);
- AddString(dict, "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR);
- AddString(dict, "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR);
-
- AddString(dict, "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING);
- AddString(dict, "cleardata", IDS_SYNC_CLEAR_DATA_FOR_PASSPHRASE);
- AddString(dict, "cleardatalink", IDS_SYNC_CLEAR_DATA_LINK);
-
- AddString(dict, "learnmore", IDS_LEARN_MORE);
- dict->SetString("encryptionhelpurl",
- GetLocalizedUrl(kEncryptionHelpUrl));
-
- // Stuff for the footer.
- AddString(dict, "ok", IDS_OK);
- AddString(dict, "cancel", IDS_CANCEL);
- } else if (path_raw == kSyncPassphrasePath) {
- html_resource_id = IDR_SYNC_PASSPHRASE_HTML;
- AddString(dict, "enterPassphraseTitle", IDS_SYNC_ENTER_PASSPHRASE_TITLE);
- AddString(dict, "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY);
- AddString(dict, "enterOtherPassphraseBody",
- IDS_SYNC_ENTER_OTHER_PASSPHRASE_BODY);
- AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL);
- AddString(dict, "incorrectPassphrase", IDS_SYNC_INCORRECT_PASSPHRASE);
- AddString(dict, "passphraseRecover", IDS_SYNC_PASSPHRASE_RECOVER);
- AddString(dict, "passphraseWarning", IDS_SYNC_PASSPHRASE_WARNING);
- AddString(dict, "cleardatalink", IDS_SYNC_CLEAR_DATA_LINK);
-
- AddString(dict, "cancelWarningHeader",
- IDS_SYNC_PASSPHRASE_CANCEL_WARNING_HEADER);
- AddString(dict, "cancelWarning", IDS_SYNC_PASSPHRASE_CANCEL_WARNING);
- AddString(dict, "yes", IDS_SYNC_PASSPHRASE_CANCEL_YES);
- AddString(dict, "no", IDS_SYNC_PASSPHRASE_CANCEL_NO);
-
- AddString(dict, "ok", IDS_OK);
- AddString(dict, "cancel", IDS_CANCEL);
- } else if (path_raw == kSyncFirstPassphrasePath) {
- html_resource_id = IDR_SYNC_FIRST_PASSPHRASE_HTML;
- AddString(dict, "title", IDS_SYNC_FIRST_PASSPHRASE_TITLE);
- dict->SetString("instructions",
- GetStringFUTF16(IDS_SYNC_FIRST_PASSPHRASE_MESSAGE,
- GetStringUTF16(IDS_PRODUCT_NAME)));
- AddString(dict, "googleOption", IDS_SYNC_PASSPHRASE_OPT_GOOGLE);
- AddString(dict, "explicitOption", IDS_SYNC_PASSPHRASE_OPT_EXPLICIT);
- AddString(dict, "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE);
- AddString(dict, "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT);
- AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL);
- AddString(dict, "confirmLabel", IDS_SYNC_CONFIRM_PASSPHRASE_LABEL);
- AddString(dict, "emptyErrorMessage", IDS_SYNC_EMPTY_PASSPHRASE_ERROR);
- AddString(dict, "mismatchErrorMessage", IDS_SYNC_PASSPHRASE_MISMATCH_ERROR);
-
- AddString(dict, "learnmore", IDS_LEARN_MORE);
- dict->SetString("encryptionhelpurl",
- GetLocalizedUrl(kEncryptionHelpUrl));
-
- AddString(dict, "syncpasswords", IDS_SYNC_FIRST_PASSPHRASE_OK);
- AddString(dict, "nothanks", IDS_SYNC_FIRST_PASSPHRASE_CANCEL);
- } else if (path_raw == kSyncSettingUpPath) {
- html_resource_id = IDR_SYNC_SETTING_UP_HTML;
-
- AddString(dict, "settingup", IDS_SYNC_LOGIN_SETTING_UP);
- AddString(dict, "cancel", IDS_CANCEL);
- } else if (path_raw == kSyncSetupDonePath) {
- html_resource_id = IDR_SYNC_SETUP_DONE_HTML;
-
- AddString(dict, "success", IDS_SYNC_SUCCESS);
- dict->SetString("setupsummary",
- GetStringFUTF16(IDS_SYNC_SETUP_ALL_DONE,
- GetStringUTF16(IDS_PRODUCT_NAME)));
- AddString(dict, "firsttimesummary", IDS_SYNC_SETUP_FIRST_TIME_ALL_DONE);
- AddString(dict, "okay", IDS_SYNC_SETUP_OK_BUTTON_LABEL);
- } else if (path_raw == kSyncSetupFlowPath) {
- html_resource_id = IDR_SYNC_SETUP_FLOW_HTML;
- }
-
- if (html_resource_id > 0) {
- const base::StringPiece html(
- ResourceBundle::GetSharedInstance().GetRawDataResource(
- html_resource_id));
- SetFontAndTextDirection(dict);
- response = jstemplate_builder::GetI18nTemplateHtml(html, dict);
- }
-
- // Send the response.
- scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
- html_bytes->data.resize(response.size());
- std::copy(response.begin(), response.end(), html_bytes->data.begin());
- SendResponse(request_id, html_bytes);
-}
-
-std::string SyncResourcesSource::GetLocalizedUrl(
- const std::string& url) const {
- GURL original_url(url);
- DCHECK(original_url.is_valid());
- GURL localized_url = google_util::AppendGoogleLocaleParam(original_url);
- return localized_url.spec();
-}
+#include "base/logging.h"
+#include "chrome/browser/sync/sync_setup_flow.h"
SyncSetupWizard::SyncSetupWizard(ProfileSyncService* service)
: service_(service),
- flow_container_(new SyncSetupFlowContainer()),
- parent_window_(NULL) {
- // If we're in a unit test, we may not have an IO thread or profile. Avoid
- // creating a SyncResourcesSource since we may leak it (since it's
- // DeleteOnUIThread).
- if (BrowserThread::IsMessageLoopValid(BrowserThread::IO) &&
- service_->profile()) {
- // Add our network layer data source for 'cloudy' URLs.
- SyncResourcesSource* sync_source = new SyncResourcesSource();
- service_->profile()->GetChromeURLDataManager()->AddDataSource(sync_source);
- }
+ flow_container_(new SyncSetupFlowContainer()) {
}
SyncSetupWizard::~SyncSetupWizard() {
@@ -285,23 +24,20 @@ void SyncSetupWizard::Step(State advance_state) {
if (flow) {
// A setup flow is in progress and dialog is currently showing.
flow->Advance(advance_state);
- } else if (!service_->profile()->GetPrefs()->GetBoolean(
- prefs::kSyncHasSetupCompleted)) {
+ } else if (!service_->HasSyncSetupCompleted()) {
if (IsTerminalState(advance_state))
return;
// No flow is in progress, and we have never escorted the user all the
// way through the wizard flow.
flow_container_->set_flow(
- SyncSetupFlow::Run(service_, flow_container_, advance_state, DONE,
- parent_window_));
+ SyncSetupFlow::Run(service_, flow_container_, advance_state, DONE));
} else {
- // No flow in in progress, but we've finished the wizard flow once before.
+ // No flow in progress, but we've finished the wizard flow once before.
// This is just a discrete run.
if (IsTerminalState(advance_state))
return; // Nothing to do.
flow_container_->set_flow(SyncSetupFlow::Run(service_, flow_container_,
- advance_state, GetEndStateForDiscreteRun(advance_state),
- parent_window_));
+ advance_state, GetEndStateForDiscreteRun(advance_state)));
}
}
@@ -325,8 +61,14 @@ void SyncSetupWizard::Focus() {
}
}
-void SyncSetupWizard::SetParent(gfx::NativeWindow parent_window) {
- parent_window_ = parent_window;
+SyncSetupFlow* SyncSetupWizard::AttachSyncSetupHandler(
+ SyncSetupFlowHandler* handler) {
+ SyncSetupFlow* flow = flow_container_->get_flow();
+ if (!flow)
+ return NULL;
+
+ flow->AttachSyncSetupHandler(handler);
+ return flow;
}
// static
@@ -336,6 +78,7 @@ SyncSetupWizard::State SyncSetupWizard::GetEndStateForDiscreteRun(
if (start_state == GAIA_LOGIN) {
result = GAIA_SUCCESS;
} else if (start_state == ENTER_PASSPHRASE ||
+ start_state == SYNC_EVERYTHING ||
start_state == CONFIGURE ||
start_state == PASSPHRASE_MIGRATION) {
result = DONE;
diff --git a/chrome/browser/sync/sync_setup_wizard.h b/chrome/browser/sync/sync_setup_wizard.h
index ce08266..7989761 100644
--- a/chrome/browser/sync/sync_setup_wizard.h
+++ b/chrome/browser/sync/sync_setup_wizard.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -7,9 +7,10 @@
#pragma once
#include "base/basictypes.h"
-#include "ui/gfx/native_widget_types.h"
+class SyncSetupFlow;
class SyncSetupFlowContainer;
+class SyncSetupFlowHandler;
class ProfileSyncService;
@@ -21,6 +22,8 @@ class SyncSetupWizard {
// A login attempt succeeded. This will wait for an explicit transition
// (via Step) to the next state.
GAIA_SUCCESS,
+ // Show the screen that confirms everything will be synced.
+ SYNC_EVERYTHING,
// Show the screen that lets you configure sync.
// There are two tabs:
// Data Types --
@@ -69,7 +72,7 @@ class SyncSetupWizard {
// not visible.
void Focus();
- void SetParent(gfx::NativeWindow parent_window);
+ SyncSetupFlow* AttachSyncSetupHandler(SyncSetupFlowHandler* handler);
private:
// If we just need to pop open an individual dialog, say to collect
@@ -85,7 +88,7 @@ class SyncSetupWizard {
SyncSetupFlowContainer* flow_container_;
- gfx::NativeWindow parent_window_;
+ SyncSetupFlowHandler* flow_handler_;
DISALLOW_COPY_AND_ASSIGN(SyncSetupWizard);
};
diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc
index d705706..72ef033 100644
--- a/chrome/browser/sync/sync_setup_wizard_unittest.cc
+++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc
@@ -2,16 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+// TODO(jhawkins): Rewrite these tests to handle the new inlined sync UI.
+
#include "chrome/browser/sync/sync_setup_wizard.h"
#include "base/json/json_writer.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/stl_util-inl.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/sync/profile_sync_factory_mock.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/sync_setup_flow.h"
+#include "chrome/browser/sync/sync_setup_flow_handler.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
@@ -126,27 +129,13 @@ class TestBrowserWindowForWizardTest : public TestBrowserWindow {
virtual ~TestBrowserWindowForWizardTest() {
if (flow_.get()) {
- // In real life, the handlers are destroyed by the WebUI infrastructure,
- // which calls GetWebUIMessageHandlers to take ownership. This does not
- // exist in our test, so we perform cleanup manually.
- std::vector<WebUIMessageHandler*> handlers;
- flow_->GetWebUIMessageHandlers(&handlers);
// The handler contract is that they are valid for the lifetime of the
- // HTMLDialogUIDelegate, but are cleaned up after the dialog is closed
+ // sync login overlay, but are cleaned up after the dialog is closed
// and/or deleted.
flow_.reset();
- STLDeleteElements(&handlers);
}
}
- // We intercept this call to hijack the flow created and then use it to
- // drive the wizard as if we were the HTML page.
- virtual void ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
- gfx::NativeWindow parent_window) {
- flow_.reset(static_cast<SyncSetupFlow*>(delegate));
- was_show_html_dialog_called_ = true;
- }
-
bool TestAndResetWasShowHTMLDialogCalled() {
bool ret = was_show_html_dialog_called_;
was_show_html_dialog_called_ = false;
@@ -157,11 +146,8 @@ class TestBrowserWindowForWizardTest : public TestBrowserWindow {
// Handles cleaning up the delegate and associated handlers.
void CloseDialog() {
if (flow_.get()) {
- std::vector<WebUIMessageHandler*> handlers;
- flow_->GetWebUIMessageHandlers(&handlers);
// The flow deletes itself here. Don't use reset().
flow_.release()->OnDialogClosed("");
- STLDeleteElements(&handlers);
}
}
@@ -178,8 +164,7 @@ class TestBrowserWindowForWizardTest : public TestBrowserWindow {
class SyncSetupWizardTest : public BrowserWithTestWindowTest {
public:
SyncSetupWizardTest()
- : file_thread_(BrowserThread::FILE, MessageLoop::current()),
- test_window_(NULL),
+ : test_window_(NULL),
wizard_(NULL) { }
virtual ~SyncSetupWizardTest() { }
virtual void SetUp() {
@@ -203,7 +188,6 @@ class SyncSetupWizardTest : public BrowserWithTestWindowTest {
wizard_.reset();
}
- BrowserThread file_thread_;
TestBrowserWindowForWizardTest* test_window_;
scoped_ptr<SyncSetupWizard> wizard_;
ProfileSyncServiceForWizardTest* service_;
@@ -220,7 +204,7 @@ class SyncSetupWizardTest : public BrowserWithTestWindowTest {
#define SKIP_TEST_ON_MACOSX() do {} while (0)
#endif
-TEST_F(SyncSetupWizardTest, InitialStepLogin) {
+TEST_F(SyncSetupWizardTest, DISABLED_InitialStepLogin) {
SKIP_TEST_ON_MACOSX();
DictionaryValue dialog_args;
SyncSetupFlow::GetArgsForGaiaLogin(service_, &dialog_args);
@@ -244,6 +228,7 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) {
EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->end_state_);
EXPECT_EQ(json_start_args, test_window_->flow()->dialog_start_args_);
+#if 0
// Simulate the user submitting credentials.
test_window_->flow()->flow_handler_->HandleSubmitAuth(&credentials);
EXPECT_TRUE(wizard_->IsVisible());
@@ -253,6 +238,7 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) {
EXPECT_EQ(kTestCaptcha, service_->captcha_);
EXPECT_FALSE(service_->user_cancelled_dialog_);
service_->ResetTestStats();
+#endif
// Simulate failed credentials.
AuthError invalid_gaia(AuthError::INVALID_GAIA_CREDENTIALS);
@@ -297,8 +283,8 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) {
EXPECT_TRUE(wizard_->IsVisible());
EXPECT_FALSE(test_window_->TestAndResetWasShowHTMLDialogCalled());
// In a non-discrete run, GAIA_SUCCESS immediately transitions you to
- // CONFIGURE.
- EXPECT_EQ(SyncSetupWizard::CONFIGURE,
+ // SYNC_EVERYTHING.
+ EXPECT_EQ(SyncSetupWizard::SYNC_EVERYTHING,
test_window_->flow()->current_state_);
// That's all we're testing here, just move on to DONE. We'll test the
@@ -310,7 +296,7 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) {
EXPECT_EQ(SyncSetupWizard::DONE, test_window_->flow()->current_state_);
}
-TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
+TEST_F(SyncSetupWizardTest, DISABLED_ChooseDataTypesSetsPrefs) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
@@ -325,6 +311,7 @@ TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
data_type_choices += "\"syncSessions\":false,\"usePassphrase\":false}";
data_type_choices_value.Append(new StringValue(data_type_choices));
+#if 0
// Simulate the user choosing data types; bookmarks, prefs, typed
// URLS, and apps are on, the rest are off.
test_window_->flow()->flow_handler_->HandleConfigure(
@@ -340,11 +327,11 @@ TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
EXPECT_EQ(service_->chosen_data_types_.count(syncable::EXTENSIONS), 0U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::TYPED_URLS), 1U);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::APPS), 1U);
-
+#endif
test_window_->CloseDialog();
}
-TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) {
+TEST_F(SyncSetupWizardTest, DISABLED_EnterPassphraseRequired) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
@@ -354,16 +341,19 @@ TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) {
wizard_->Step(SyncSetupWizard::ENTER_PASSPHRASE);
EXPECT_EQ(SyncSetupWizard::ENTER_PASSPHRASE,
test_window_->flow()->current_state_);
+#if 0
ListValue value;
value.Append(new StringValue("{\"passphrase\":\"myPassphrase\","
"\"mode\":\"gaia\"}"));
test_window_->flow()->flow_handler_->HandlePassphraseEntry(&value);
EXPECT_EQ("myPassphrase", service_->passphrase_);
+#endif
}
-TEST_F(SyncSetupWizardTest, PassphraseMigration) {
+TEST_F(SyncSetupWizardTest, DISABLED_PassphraseMigration) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::PASSPHRASE_MIGRATION);
+#if 0
ListValue value;
value.Append(new StringValue("{\"option\":\"explicit\","
"\"passphrase\":\"myPassphrase\"}"));
@@ -375,9 +365,10 @@ TEST_F(SyncSetupWizardTest, PassphraseMigration) {
"\"passphrase\":\"myPassphrase\"}"));
test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value2);
EXPECT_EQ(service_->chosen_data_types_.count(syncable::PASSWORDS), 0U);
+#endif
}
-TEST_F(SyncSetupWizardTest, DialogCancelled) {
+TEST_F(SyncSetupWizardTest, DISABLED_DialogCancelled) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
// Simulate the user closing the dialog.
@@ -400,7 +391,7 @@ TEST_F(SyncSetupWizardTest, DialogCancelled) {
EXPECT_EQ(std::string(), service_->password_);
}
-TEST_F(SyncSetupWizardTest, InvalidTransitions) {
+TEST_F(SyncSetupWizardTest, DISABLED_InvalidTransitions) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
EXPECT_FALSE(wizard_->IsVisible());
@@ -426,14 +417,14 @@ TEST_F(SyncSetupWizardTest, InvalidTransitions) {
test_window_->flow()->current_state_);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
- EXPECT_EQ(SyncSetupWizard::CONFIGURE,
+ EXPECT_EQ(SyncSetupWizard::SYNC_EVERYTHING,
test_window_->flow()->current_state_);
wizard_->Step(SyncSetupWizard::FATAL_ERROR);
EXPECT_EQ(SyncSetupWizard::FATAL_ERROR, test_window_->flow()->current_state_);
}
-TEST_F(SyncSetupWizardTest, FullSuccessfulRunSetsPref) {
+TEST_F(SyncSetupWizardTest, DISABLED_FullSuccessfulRunSetsPref) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
@@ -445,7 +436,7 @@ TEST_F(SyncSetupWizardTest, FullSuccessfulRunSetsPref) {
prefs::kSyncHasSetupCompleted));
}
-TEST_F(SyncSetupWizardTest, FirstFullSuccessfulRunSetsPref) {
+TEST_F(SyncSetupWizardTest, DISABLED_FirstFullSuccessfulRunSetsPref) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
@@ -457,7 +448,7 @@ TEST_F(SyncSetupWizardTest, FirstFullSuccessfulRunSetsPref) {
prefs::kSyncHasSetupCompleted));
}
-TEST_F(SyncSetupWizardTest, AbortedByPendingClear) {
+TEST_F(SyncSetupWizardTest, DISABLED_AbortedByPendingClear) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
@@ -468,7 +459,7 @@ TEST_F(SyncSetupWizardTest, AbortedByPendingClear) {
EXPECT_FALSE(wizard_->IsVisible());
}
-TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypes) {
+TEST_F(SyncSetupWizardTest, DISABLED_DiscreteRunChooseDataTypes) {
SKIP_TEST_ON_MACOSX();
// For a discrete run, we need to have ran through setup once.
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
@@ -486,7 +477,8 @@ TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypes) {
EXPECT_FALSE(wizard_->IsVisible());
}
-TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypesAbortedByPendingClear) {
+TEST_F(SyncSetupWizardTest,
+ DISABLED_DiscreteRunChooseDataTypesAbortedByPendingClear) {
SKIP_TEST_ON_MACOSX();
// For a discrete run, we need to have ran through setup once.
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
@@ -506,7 +498,7 @@ TEST_F(SyncSetupWizardTest, DiscreteRunChooseDataTypesAbortedByPendingClear) {
EXPECT_FALSE(wizard_->IsVisible());
}
-TEST_F(SyncSetupWizardTest, DiscreteRunGaiaLogin) {
+TEST_F(SyncSetupWizardTest, DISABLED_DiscreteRunGaiaLogin) {
SKIP_TEST_ON_MACOSX();
DictionaryValue dialog_args;
// For a discrete run, we need to have ran through setup once.
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index 8cfe948..1f1e453 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -12,6 +12,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/options/options_window.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
@@ -38,8 +39,7 @@ void GetStatusLabelsForAuthError(const AuthError& auth_error,
link_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_RELOGIN_LINK_LABEL));
if (auth_error.state() == AuthError::INVALID_GAIA_CREDENTIALS ||
auth_error.state() == AuthError::ACCOUNT_DELETED ||
- auth_error.state() == AuthError::ACCOUNT_DISABLED ||
- auth_error.state() == AuthError::SERVICE_UNAVAILABLE) {
+ auth_error.state() == AuthError::ACCOUNT_DISABLED) {
// If the user name is empty then the first login failed, otherwise the
// credentials are out-of-date.
if (service->GetAuthenticatedUsername().empty())
@@ -48,6 +48,10 @@ void GetStatusLabelsForAuthError(const AuthError& auth_error,
else
status_label->assign(
l10n_util::GetStringUTF16(IDS_SYNC_LOGIN_INFO_OUT_OF_DATE));
+ } else if (auth_error.state() == AuthError::SERVICE_UNAVAILABLE) {
+ DCHECK(service->GetAuthenticatedUsername().empty());
+ status_label->assign(
+ l10n_util::GetStringUTF16(IDS_SYNC_SERVICE_UNAVAILABLE));
} else if (auth_error.state() == AuthError::CONNECTION_FAILED) {
// Note that there is little the user can do if the server is not
// reachable. Since attempting to re-connect is done automatically by
@@ -71,9 +75,13 @@ string16 GetSyncedStateStatusLabel(ProfileSyncService* service) {
if (user_name.empty())
return label;
- return l10n_util::GetStringFUTF16(IDS_SYNC_ACCOUNT_SYNCED_TO_USER_WITH_TIME,
- user_name,
- service->GetLastSyncedTimeString());
+ const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
+ return l10n_util::GetStringFUTF16(
+ browser_command_line.HasSwitch(switches::kMultiProfiles) ?
+ IDS_PROFILES_SYNCED_TO_USER_WITH_TIME :
+ IDS_SYNC_ACCOUNT_SYNCED_TO_USER_WITH_TIME,
+ user_name,
+ service->GetLastSyncedTimeString());
}
// TODO(akalin): Write unit tests for these three functions below.
@@ -166,12 +174,6 @@ MessageType GetStatusInfo(ProfileSyncService* service,
if (status_label) {
status_label->assign(l10n_util::GetStringUTF16(IDS_SYNC_SETUP_ERROR));
}
- } else {
- if (status_label) {
- status_label->assign(
- l10n_util::GetStringFUTF16(IDS_SYNC_NOT_SET_UP_INFO,
- l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
- }
}
}
return result_type;
@@ -216,20 +218,6 @@ MessageType GetStatusInfoForNewTabPage(ProfileSyncService* service,
} // namespace
-// Returns an HTML chunk for a login prompt related to encryption.
-string16 GetLoginMessageForEncryption() {
- std::vector<std::string> subst;
- const base::StringPiece html(
- ResourceBundle::GetSharedInstance().GetRawDataResource(
- IDR_SYNC_ENCRYPTION_LOGIN_HTML));
- subst.push_back(l10n_util::GetStringUTF8(IDS_SYNC_PLEASE_SIGN_IN));
- subst.push_back(
- l10n_util::GetStringFUTF8(IDS_SYNC_LOGIN_FOR_ENCRYPTION,
- l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
-
- return UTF8ToUTF16(ReplaceStringPlaceholders(html, subst, NULL));
-}
-
MessageType GetStatusLabels(ProfileSyncService* service,
string16* status_label,
string16* link_label) {
@@ -252,8 +240,11 @@ MessageType GetStatus(ProfileSyncService* service) {
}
bool ShouldShowSyncErrorButton(ProfileSyncService* service) {
- return service && !service->IsManaged() && service->HasSyncSetupCompleted() &&
- (GetStatus(service) == sync_ui_util::SYNC_ERROR);
+ return service &&
+ ((!service->IsManaged() &&
+ service->HasSyncSetupCompleted()) &&
+ (GetStatus(service) == sync_ui_util::SYNC_ERROR ||
+ service->observed_passphrase_required()));
}
string16 GetSyncMenuLabel(ProfileSyncService* service) {
@@ -277,20 +268,13 @@ void OpenSyncMyBookmarksDialog(Profile* profile,
return;
}
- bool use_tabbed_options = !CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kDisableTabbedOptions);
-
if (service->HasSyncSetupCompleted()) {
- if (use_tabbed_options) {
- bool create_window = browser == NULL;
- if (create_window)
- browser = Browser::Create(profile);
- browser->ShowOptionsTab(chrome::kPersonalOptionsSubPage);
- if (create_window)
- browser->window()->Show();
- } else {
- ShowOptionsWindow(OPTIONS_PAGE_CONTENT, OPTIONS_GROUP_NONE, profile);
- }
+ bool create_window = browser == NULL;
+ if (create_window)
+ browser = Browser::Create(profile);
+ browser->ShowOptionsTab(chrome::kPersonalOptionsSubPage);
+ if (create_window)
+ browser->window()->Show();
} else {
service->ShowLoginDialog(NULL);
ProfileSyncService::SyncEvent(code); // UMA stats
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h
index 819916d..68738bc 100644
--- a/chrome/browser/sync/sync_ui_util.h
+++ b/chrome/browser/sync/sync_ui_util.h
@@ -32,8 +32,6 @@ enum MessageType {
// TODO(akalin): audit the use of ProfileSyncService* service below,
// and use const ProfileSyncService& service where possible.
-string16 GetLoginMessageForEncryption();
-
// Create status and link labels for the current status labels and link text
// by querying |service|.
MessageType GetStatusLabels(ProfileSyncService* service,
diff --git a/chrome/browser/sync/sync_ui_util_mac_unittest.mm b/chrome/browser/sync/sync_ui_util_mac_unittest.mm
index bebb06e..6206fd4 100644
--- a/chrome/browser/sync/sync_ui_util_mac_unittest.mm
+++ b/chrome/browser/sync/sync_ui_util_mac_unittest.mm
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,7 +6,7 @@
#import <Cocoa/Cocoa.h>
-#include "base/scoped_nsobject.h"
+#include "base/memory/scoped_nsobject.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/ui/cocoa/cocoa_test_helper.h"
#include "grit/generated_resources.h"
diff --git a/chrome/browser/sync/syncable/autofill_migration.h b/chrome/browser/sync/syncable/autofill_migration.h
index 81a16be..81b555d 100644
--- a/chrome/browser/sync/syncable/autofill_migration.h
+++ b/chrome/browser/sync/syncable/autofill_migration.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -33,11 +33,14 @@ enum AutofillMigrationState {
struct AutofillMigrationDebugInfo {
enum PropertyToSet {
MIGRATION_TIME,
- BOOKMARK_ADDED,
ENTRIES_ADDED,
PROFILES_ADDED
};
int64 autofill_migration_time;
+ // NOTE(akalin): We don't increment
+ // |bookmarks_added_during_migration| anymore, although it's not
+ // worth the effort to remove it from the code. Eventually, this
+ // will go away once we remove all the autofill migration code.
int bookmarks_added_during_migration;
int autofill_entries_added_during_migration;
int autofill_profile_added_during_migration;
diff --git a/chrome/browser/sync/syncable/directory_backing_store.cc b/chrome/browser/sync/syncable/directory_backing_store.cc
index fcadba1..7f5721e 100644
--- a/chrome/browser/sync/syncable/directory_backing_store.cc
+++ b/chrome/browser/sync/syncable/directory_backing_store.cc
@@ -437,7 +437,7 @@ DirOpenResult DirectoryBackingStore::InitializeTables() {
return FAILED_DISK_FULL;
}
int version_on_disk = GetVersion();
- int last_result = SQLITE_OK;
+ int last_result = SQLITE_DONE;
// Upgrade from version 67. Version 67 was widely distributed as the original
// Bookmark Sync release. Version 68 removed unique naming.
@@ -504,14 +504,10 @@ DirOpenResult DirectoryBackingStore::InitializeTables() {
// Fallback (re-sync everything) migration path.
VLOG(1) << "Old/null sync database, version " << version_on_disk;
// Delete the existing database (if any), and create a fresh one.
- if (SQLITE_OK == last_result) {
- DropAllTables();
- if (SQLITE_DONE == CreateTables()) {
- last_result = SQLITE_OK;
- }
- }
+ DropAllTables();
+ last_result = CreateTables();
}
- if (SQLITE_OK == last_result) {
+ if (SQLITE_DONE == last_result) {
{
SQLStatement statement;
statement.prepare(load_dbhandle_,
@@ -943,11 +939,11 @@ bool DirectoryBackingStore::MigrateVersion70To71() {
CreateShareInfoTableVersion71(kCreateAsTempShareInfo);
if (result != SQLITE_DONE)
return false;
- ExecQuery(load_dbhandle_,
- "INSERT INTO temp_share_info (id, name, store_birthday, "
- "db_create_version, db_create_time, next_id, cache_guid) "
- "SELECT id, name, store_birthday, db_create_version, "
- "db_create_time, next_id, cache_guid FROM share_info");
+ result = ExecQuery(load_dbhandle_,
+ "INSERT INTO temp_share_info (id, name, store_birthday, "
+ "db_create_version, db_create_time, next_id, cache_guid) "
+ "SELECT id, name, store_birthday, db_create_version, "
+ "db_create_time, next_id, cache_guid FROM share_info");
if (result != SQLITE_DONE)
return false;
SafeDropTable("share_info");
diff --git a/chrome/browser/sync/syncable/directory_backing_store_unittest.cc b/chrome/browser/sync/syncable/directory_backing_store_unittest.cc
index db40397..40ab3e3 100644
--- a/chrome/browser/sync/syncable/directory_backing_store_unittest.cc
+++ b/chrome/browser/sync/syncable/directory_backing_store_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,8 +10,8 @@
#include "app/sql/statement.h"
#include "base/file_path.h"
#include "base/file_util.h"
-#include "base/scoped_ptr.h"
-#include "base/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_temp_dir.h"
#include "base/stl_util-inl.h"
#include "base/string_number_conversions.h"
#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
@@ -20,7 +20,6 @@
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/syncable/syncable-inl.h"
#include "chrome/browser/sync/syncable/syncable.h"
-#include "chrome/common/sqlite_utils.h"
#include "testing/gtest/include/gtest/gtest-param-test.h"
namespace syncable {
diff --git a/chrome/browser/sync/syncable/directory_change_listener.h b/chrome/browser/sync/syncable/directory_change_listener.h
new file mode 100644
index 0000000..0867cb7
--- /dev/null
+++ b/chrome/browser/sync/syncable/directory_change_listener.h
@@ -0,0 +1,42 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_SYNCABLE_DIRECTORY_CHANGE_LISTENER_H_
+#define CHROME_BROWSER_SYNC_SYNCABLE_DIRECTORY_CHANGE_LISTENER_H_
+#pragma once
+
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/syncable.h"
+
+namespace syncable {
+
+// This is an interface for listening to directory change events, triggered by
+// the releasing of the syncable transaction. The listener performs work to
+// 1. Calculate changes, depending on the source of the transaction
+// (HandleCalculateChangesChangeEventFromSyncer/Syncapi).
+// 2. Perform final work while the transaction is held
+// (HandleTransactionEndingChangeEvent).
+// 3. Perform any work that should be done after the transaction is released.
+// (HandleTransactionCompleteChangeEvent).
+class DirectoryChangeListener {
+ public:
+ virtual void HandleCalculateChangesChangeEventFromSyncApi(
+ const OriginalEntries& originals,
+ const WriterTag& writer,
+ BaseTransaction* trans) = 0;
+ virtual void HandleCalculateChangesChangeEventFromSyncer(
+ const OriginalEntries& originals,
+ const WriterTag& writer,
+ BaseTransaction* trans) = 0;
+ virtual ModelTypeBitSet HandleTransactionEndingChangeEvent(
+ BaseTransaction* trans) = 0;
+ virtual void HandleTransactionCompleteChangeEvent(
+ const ModelTypeBitSet& models_with_changes) = 0;
+ protected:
+ virtual ~DirectoryChangeListener() {}
+};
+
+} // namespace syncable
+
+#endif // CHROME_BROWSER_SYNC_SYNCABLE_DIRECTORY_CHANGE_LISTENER_H_
diff --git a/chrome/browser/sync/syncable/directory_manager.cc b/chrome/browser/sync/syncable/directory_manager.cc
index bf6e5e0..73d9f8a 100644
--- a/chrome/browser/sync/syncable/directory_manager.cc
+++ b/chrome/browser/sync/syncable/directory_manager.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,8 +9,8 @@
#include <iterator>
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/port.h"
-#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/common/deprecated/event_sys-inl.h"
diff --git a/chrome/browser/sync/syncable/directory_manager.h b/chrome/browser/sync/syncable/directory_manager.h
index 4258f87..944e6d5 100644
--- a/chrome/browser/sync/syncable/directory_manager.h
+++ b/chrome/browser/sync/syncable/directory_manager.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
//
@@ -25,6 +25,7 @@
#include "chrome/common/deprecated/event_sys.h"
namespace sync_api { class BaseTransaction; }
+namespace syncable { class BaseTransaction; }
namespace syncable {
@@ -72,11 +73,20 @@ class DirectoryManager {
Channel* channel() const { return channel_; }
+ // Wrappers for cryptographer() that enforce holding a transaction.
+ // Note: the Cryptographer is NOT thread safe. It must only be accessed while
+ // the transaction is still active. The Cryptographer's pointer should not be
+ // stored separately.
+ browser_sync::Cryptographer* GetCryptographer(
+ const sync_api::BaseTransaction* trans) const { return cryptographer(); }
+ browser_sync::Cryptographer* GetCryptographer(
+ const syncable::BaseTransaction* trans) const { return cryptographer(); }
+
+ protected:
browser_sync::Cryptographer* cryptographer() const {
return cryptographer_.get();
}
- protected:
DirOpenResult OpenImpl(const std::string& name, const FilePath& path,
bool* was_open);
diff --git a/chrome/browser/sync/syncable/model_type.cc b/chrome/browser/sync/syncable/model_type.cc
index b9c4645..273eadd 100644
--- a/chrome/browser/sync/syncable/model_type.cc
+++ b/chrome/browser/sync/syncable/model_type.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -215,6 +215,18 @@ std::string ModelTypeToString(ModelType model_type) {
return "INVALID";
}
+StringValue* ModelTypeToValue(ModelType model_type) {
+ if (model_type >= syncable::FIRST_REAL_MODEL_TYPE) {
+ return Value::CreateStringValue(ModelTypeToString(model_type));
+ } else if (model_type == syncable::TOP_LEVEL_FOLDER) {
+ return Value::CreateStringValue("Top-level folder");
+ } else if (model_type == syncable::UNSPECIFIED) {
+ return Value::CreateStringValue("Unspecified");
+ }
+ NOTREACHED();
+ return Value::CreateStringValue("");
+}
+
std::string ModelTypeSetToString(const ModelTypeSet& model_types) {
std::string result;
for (ModelTypeSet::const_iterator iter = model_types.begin();
diff --git a/chrome/browser/sync/syncable/model_type.h b/chrome/browser/sync/syncable/model_type.h
index 58999d8..f0e96fb 100644
--- a/chrome/browser/sync/syncable/model_type.h
+++ b/chrome/browser/sync/syncable/model_type.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -18,6 +18,7 @@
#include "base/time.h"
class ListValue;
+class StringValue;
namespace sync_pb {
class EntitySpecifics;
@@ -106,6 +107,11 @@ int GetExtensionFieldNumberFromModelType(ModelType model_type);
// Returns a string that represents the name of |model_type|.
std::string ModelTypeToString(ModelType model_type);
+// Handles all model types, and not just real ones.
+//
+// Caller takes ownership of returned value.
+StringValue* ModelTypeToValue(ModelType model_type);
+
std::string ModelTypeSetToString(const ModelTypeSet& model_types);
// Returns the ModelType corresponding to the name |model_type_string|.
diff --git a/chrome/browser/sync/syncable/model_type_payload_map.cc b/chrome/browser/sync/syncable/model_type_payload_map.cc
new file mode 100644
index 0000000..af1bbca
--- /dev/null
+++ b/chrome/browser/sync/syncable/model_type_payload_map.cc
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+
+#include "base/values.h"
+
+namespace syncable {
+
+ModelTypePayloadMap ModelTypePayloadMapFromBitSet(
+ const syncable::ModelTypeBitSet& types,
+ const std::string& payload) {
+ ModelTypePayloadMap types_with_payloads;
+ for (size_t i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < types.size(); ++i) {
+ if (types[i]) {
+ types_with_payloads[syncable::ModelTypeFromInt(i)] = payload;
+ }
+ }
+ return types_with_payloads;
+}
+
+ModelTypePayloadMap ModelTypePayloadMapFromRoutingInfo(
+ const browser_sync::ModelSafeRoutingInfo& routes,
+ const std::string& payload) {
+ ModelTypePayloadMap types_with_payloads;
+ for (browser_sync::ModelSafeRoutingInfo::const_iterator i = routes.begin();
+ i != routes.end(); ++i) {
+ types_with_payloads[i->first] = payload;
+ }
+ return types_with_payloads;
+}
+
+DictionaryValue* ModelTypePayloadMapToValue(
+ const ModelTypePayloadMap& type_payloads) {
+ DictionaryValue* value = new DictionaryValue();
+ for (ModelTypePayloadMap::const_iterator it = type_payloads.begin();
+ it != type_payloads.end(); ++it) {
+ value->SetString(syncable::ModelTypeToString(it->first), it->second);
+ }
+ return value;
+}
+
+void CoalescePayloads(ModelTypePayloadMap* original,
+ const ModelTypePayloadMap& update) {
+ for (ModelTypePayloadMap::const_iterator i = update.begin();
+ i != update.end(); ++i) {
+ if (original->count(i->first) == 0) {
+ // If this datatype isn't already in our map, add it with
+ // whatever payload it has.
+ (*original)[i->first] = i->second;
+ } else if (i->second.length() > 0) {
+ // If this datatype is already in our map, we only overwrite the
+ // payload if the new one is non-empty.
+ (*original)[i->first] = i->second;
+ }
+ }
+}
+
+} // namespace syncable
diff --git a/chrome/browser/sync/syncable/model_type_payload_map.h b/chrome/browser/sync/syncable/model_type_payload_map.h
new file mode 100644
index 0000000..212220e
--- /dev/null
+++ b/chrome/browser/sync/syncable/model_type_payload_map.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2011 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.
+//
+// Definition of ModelTypePayloadMap and various utility functions.
+
+#ifndef CHROME_BROWSER_SYNC_SYNCABLE_MODEL_TYPE_PAYLOAD_MAP_H_
+#define CHROME_BROWSER_SYNC_SYNCABLE_MODEL_TYPE_PAYLOAD_MAP_H_
+#pragma once
+
+#include <map>
+#include <string>
+
+#include "chrome/browser/sync/engine/model_safe_worker.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+
+class DictionaryValue;
+
+namespace syncable {
+
+// A container that contains a set of datatypes with possible string
+// payloads.
+typedef std::map<ModelType, std::string> ModelTypePayloadMap;
+
+// Helper functions for building ModelTypePayloadMaps.
+
+// Make a TypePayloadMap from all the types in a ModelTypeBitSet using
+// a default payload.
+ModelTypePayloadMap ModelTypePayloadMapFromBitSet(
+ const ModelTypeBitSet& model_types,
+ const std::string& payload);
+
+// Make a TypePayloadMap for all the enabled types in a
+// ModelSafeRoutingInfo using a default payload.
+ModelTypePayloadMap ModelTypePayloadMapFromRoutingInfo(
+ const browser_sync::ModelSafeRoutingInfo& routes,
+ const std::string& payload);
+
+// Caller takes ownership of the returned dictionary.
+DictionaryValue* ModelTypePayloadMapToValue(
+ const ModelTypePayloadMap& model_type_payloads);
+
+// Coalesce |update| into |original|, overwriting only when |update| has
+// a non-empty payload.
+void CoalescePayloads(ModelTypePayloadMap* original,
+ const ModelTypePayloadMap& update);
+
+} // namespace syncable
+
+#endif // CHROME_BROWSER_SYNC_SYNCABLE_MODEL_TYPE_PAYLOAD_MAP_H_
diff --git a/chrome/browser/sync/syncable/model_type_payload_map_unittest.cc b/chrome/browser/sync/syncable/model_type_payload_map_unittest.cc
new file mode 100644
index 0000000..9350f41
--- /dev/null
+++ b/chrome/browser/sync/syncable/model_type_payload_map_unittest.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 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.
+
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
+#include "chrome/test/values_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncable {
+namespace {
+
+using test::ExpectBooleanValue;
+using test::ExpectDictionaryValue;
+using test::ExpectIntegerValue;
+using test::ExpectListValue;
+using test::ExpectStringValue;
+
+class ModelTypePayloadMapTest : public testing::Test {};
+
+TEST_F(ModelTypePayloadMapTest, TypePayloadMapToValue) {
+ ModelTypePayloadMap payloads;
+ payloads[syncable::BOOKMARKS] = "bookmarkpayload";
+ payloads[syncable::APPS] = "";
+
+ scoped_ptr<DictionaryValue> value(ModelTypePayloadMapToValue(payloads));
+ EXPECT_EQ(2u, value->size());
+ ExpectStringValue("bookmarkpayload", *value, "Bookmarks");
+ ExpectStringValue("", *value, "Apps");
+ EXPECT_FALSE(value->HasKey("Preferences"));
+}
+
+} // namespace
+} // namespace syncable
diff --git a/chrome/browser/sync/syncable/model_type_unittest.cc b/chrome/browser/sync/syncable/model_type_unittest.cc
index df8ca8d..cb846f6 100644
--- a/chrome/browser/sync/syncable/model_type_unittest.cc
+++ b/chrome/browser/sync/syncable/model_type_unittest.cc
@@ -6,7 +6,7 @@
#include <string>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -15,6 +15,30 @@ namespace {
class ModelTypeTest : public testing::Test {};
+// TODO(akalin): Move this to values_test_util.h.
+
+// Takes ownership of |actual|.
+void ExpectStringValue(const std::string& expected_str,
+ StringValue* actual) {
+ scoped_ptr<StringValue> scoped_actual(actual);
+ std::string actual_str;
+ EXPECT_TRUE(scoped_actual->GetAsString(&actual_str));
+ EXPECT_EQ(expected_str, actual_str);
+}
+
+TEST_F(ModelTypeTest, ModelTypeToValue) {
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ ModelType model_type = ModelTypeFromInt(i);
+ ExpectStringValue(ModelTypeToString(model_type),
+ ModelTypeToValue(model_type));
+ }
+ ExpectStringValue("Top-level folder",
+ ModelTypeToValue(TOP_LEVEL_FOLDER));
+ ExpectStringValue("Unspecified",
+ ModelTypeToValue(UNSPECIFIED));
+}
+
TEST_F(ModelTypeTest, ModelTypeBitSetToValue) {
ModelTypeBitSet model_types;
model_types.set(syncable::BOOKMARKS);
diff --git a/chrome/browser/sync/syncable/syncable.cc b/chrome/browser/sync/syncable/syncable.cc
index 73c1111..0674457 100644
--- a/chrome/browser/sync/syncable/syncable.cc
+++ b/chrome/browser/sync/syncable/syncable.cc
@@ -30,22 +30,25 @@
#include "base/hash_tables.h"
#include "base/file_util.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "base/perftimer.h"
-#include "base/scoped_ptr.h"
#include "base/string_number_conversions.h"
#include "base/string_util.h"
#include "base/stl_util-inl.h"
#include "base/time.h"
+#include "base/values.h"
#include "chrome/browser/sync/engine/syncer.h"
#include "chrome/browser/sync/engine/syncer_util.h"
+#include "chrome/browser/sync/protocol/proto_value_conversions.h"
#include "chrome/browser/sync/protocol/service_constants.h"
-#include "chrome/browser/sync/protocol/theme_specifics.pb.h"
-#include "chrome/browser/sync/protocol/typed_url_specifics.pb.h"
#include "chrome/browser/sync/syncable/directory_backing_store.h"
+#include "chrome/browser/sync/syncable/directory_change_listener.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
+#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/syncable/syncable-inl.h"
#include "chrome/browser/sync/syncable/syncable_changes_version.h"
#include "chrome/browser/sync/syncable/syncable_columns.h"
+#include "chrome/browser/sync/syncable/syncable_enum_conversions.h"
#include "chrome/browser/sync/util/crypto_helpers.h"
#include "chrome/common/deprecated/event_sys-inl.h"
#include "net/base/escape.h"
@@ -177,12 +180,96 @@ EntryKernel::EntryKernel() : dirty_(false) {
EntryKernel::~EntryKernel() {}
+namespace {
+
+// Utility function to loop through a set of enum values and add the
+// field keys/values in the kernel to the given dictionary.
+//
+// V should be convertible to Value.
+template <class T, class U, class V>
+void SetFieldValues(const EntryKernel& kernel,
+ DictionaryValue* dictionary_value,
+ const char* (*enum_key_fn)(T),
+ V* (*enum_value_fn)(U),
+ int field_key_min, int field_key_max) {
+ DCHECK_LE(field_key_min, field_key_max);
+ for (int i = field_key_min; i <= field_key_max; ++i) {
+ T field = static_cast<T>(i);
+ const std::string& key = enum_key_fn(field);
+ V* value = enum_value_fn(kernel.ref(field));
+ dictionary_value->Set(key, value);
+ }
+}
+
+// Helper functions for SetFieldValues().
+
+StringValue* Int64ToValue(int64 i) {
+ return Value::CreateStringValue(base::Int64ToString(i));
+}
+
+StringValue* IdToValue(const Id& id) {
+ return id.ToValue();
+}
+
+} // namespace
+
+DictionaryValue* EntryKernel::ToValue() const {
+ DictionaryValue* kernel_info = new DictionaryValue();
+ kernel_info->SetBoolean("isDirty", is_dirty());
+
+ // Int64 fields.
+ SetFieldValues(*this, kernel_info,
+ &GetMetahandleFieldString, &Int64ToValue,
+ INT64_FIELDS_BEGIN, META_HANDLE);
+ SetFieldValues(*this, kernel_info,
+ &GetBaseVersionString, &Int64ToValue,
+ META_HANDLE + 1, BASE_VERSION);
+ SetFieldValues(*this, kernel_info,
+ &GetInt64FieldString, &Int64ToValue,
+ BASE_VERSION + 1, INT64_FIELDS_END - 1);
+
+ // ID fields.
+ SetFieldValues(*this, kernel_info,
+ &GetIdFieldString, &IdToValue,
+ ID_FIELDS_BEGIN, ID_FIELDS_END - 1);
+
+ // Bit fields.
+ SetFieldValues(*this, kernel_info,
+ &GetIndexedBitFieldString, &Value::CreateBooleanValue,
+ BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1);
+ SetFieldValues(*this, kernel_info,
+ &GetIsDelFieldString, &Value::CreateBooleanValue,
+ INDEXED_BIT_FIELDS_END, IS_DEL);
+ SetFieldValues(*this, kernel_info,
+ &GetBitFieldString, &Value::CreateBooleanValue,
+ IS_DEL + 1, BIT_FIELDS_END - 1);
+
+ // String fields.
+ {
+ // Pick out the function overload we want.
+ StringValue* (*string_to_value)(const std::string&) =
+ &Value::CreateStringValue;
+ SetFieldValues(*this, kernel_info,
+ &GetStringFieldString, string_to_value,
+ STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1);
+ }
+
+ // Proto fields.
+ SetFieldValues(*this, kernel_info,
+ &GetProtoFieldString, &browser_sync::EntitySpecificsToValue,
+ PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
+
+ // Bit temps.
+ SetFieldValues(*this, kernel_info,
+ &GetBitTempString, &Value::CreateBooleanValue,
+ BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
+
+ return kernel_info;
+}
+
///////////////////////////////////////////////////////////////////////////
// Directory
-static const DirectoryChangeEvent kShutdownChangesEvent =
- { DirectoryChangeEvent::SHUTDOWN, 0, 0 };
-
void Directory::init_kernel(const std::string& name) {
DCHECK(kernel_ == NULL);
kernel_ = new Kernel(FilePath(), name, KernelLoadInfo());
@@ -229,6 +316,7 @@ Directory::Kernel::Kernel(const FilePath& db_path,
dirty_metahandles(new MetahandleSet),
metahandles_to_purge(new MetahandleSet),
channel(new Directory::Channel(syncable::DIRECTORY_DESTROYED)),
+ change_listener_(NULL),
info_status(Directory::KERNEL_SHARE_INFO_VALID),
persisted_info(info.kernel_info),
cache_guid(info.cache_guid),
@@ -247,7 +335,6 @@ void Directory::Kernel::Release() {
Directory::Kernel::~Kernel() {
CHECK_EQ(0, refcount);
delete channel;
- changes_channel.Notify(kShutdownChangesEvent);
delete unsynced_metahandles;
delete unapplied_update_metahandles;
delete dirty_metahandles;
@@ -687,6 +774,7 @@ void Directory::SetDownloadProgress(
const sync_pb::DataTypeProgressMarker& new_progress) {
ScopedKernelLock lock(this);
kernel_->persisted_info.download_progress[model_type].CopyFrom(new_progress);
+ kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
}
bool Directory::initial_sync_ended_for_type(ModelType type) const {
@@ -727,14 +815,6 @@ void Directory::set_autofill_migration_state_debug_info(
&info.autofill_migration_time);
break;
}
- case AutofillMigrationDebugInfo::BOOKMARK_ADDED: {
- AutofillMigrationDebugInfo& debug_info =
- kernel_->persisted_info.autofill_migration_debug_info;
- TestAndSet<int>(
- &debug_info.bookmarks_added_during_migration,
- &info.bookmarks_added_during_migration);
- break;
- }
case AutofillMigrationDebugInfo::ENTRIES_ADDED: {
AutofillMigrationDebugInfo& debug_info =
kernel_->persisted_info.autofill_migration_debug_info;
@@ -797,7 +877,7 @@ string Directory::store_birthday() const {
return kernel_->persisted_info.store_birthday;
}
-void Directory::set_store_birthday(string store_birthday) {
+void Directory::set_store_birthday(const string& store_birthday) {
ScopedKernelLock lock(this);
if (kernel_->persisted_info.store_birthday == store_birthday)
return;
@@ -921,9 +1001,7 @@ void Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
void Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
const MetahandleSet& handles,
const IdFilter& idfilter) {
- int64 max_ms = kInvariantCheckMaxMs;
- if (max_ms < 0)
- max_ms = std::numeric_limits<int64>::max();
+ const int64 max_ms = kInvariantCheckMaxMs;
PerfTimer check_timer;
MetahandleSet::const_iterator i;
int entries_done = 0;
@@ -1008,9 +1086,9 @@ void Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
}
}
-browser_sync::ChannelHookup<DirectoryChangeEvent>* Directory::AddChangeObserver(
- browser_sync::ChannelEventHandler<DirectoryChangeEvent>* observer) {
- return kernel_->changes_channel.AddObserver(observer);
+void Directory::SetChangeListener(DirectoryChangeListener* listener) {
+ DCHECK(!kernel_->change_listener_);
+ kernel_->change_listener_ = listener;
}
///////////////////////////////////////////////////////////////////////////////
@@ -1058,23 +1136,22 @@ BaseTransaction::BaseTransaction(Directory* directory)
BaseTransaction::~BaseTransaction() {}
-void BaseTransaction::UnlockAndLog(OriginalEntries* originals_arg) {
- // Triggers the CALCULATE_CHANGES and TRANSACTION_ENDING events while
- // holding dir_kernel_'s transaction_mutex and changes_channel mutex.
- // Releases all mutexes upon completion.
- if (!NotifyTransactionChangingAndEnding(originals_arg)) {
+void BaseTransaction::UnlockAndLog(OriginalEntries* entries) {
+ // Work while trasnaction mutex is held
+ ModelTypeBitSet models_with_changes;
+ if (!NotifyTransactionChangingAndEnding(entries, &models_with_changes))
return;
- }
- // Triggers the TRANSACTION_COMPLETE event (and does not hold any mutexes).
- NotifyTransactionComplete();
+ // Work after mutex is relased.
+ NotifyTransactionComplete(models_with_changes);
}
bool BaseTransaction::NotifyTransactionChangingAndEnding(
- OriginalEntries* originals_arg) {
+ OriginalEntries* entries,
+ ModelTypeBitSet* models_with_changes) {
dirkernel_->transaction_mutex.AssertAcquired();
- scoped_ptr<OriginalEntries> originals(originals_arg);
+ scoped_ptr<OriginalEntries> originals(entries);
const base::TimeDelta elapsed = base::TimeTicks::Now() - time_acquired_;
if (LOG_IS_ON(INFO) &&
(1 <= logging::GetVlogLevelHelper(
@@ -1085,43 +1162,40 @@ bool BaseTransaction::NotifyTransactionChangingAndEnding(
<< " seconds.";
}
- if (NULL == originals.get() || originals->empty()) {
+ if (NULL == originals.get() || originals->empty() ||
+ !dirkernel_->change_listener_) {
dirkernel_->transaction_mutex.Release();
return false;
}
-
- {
- // Scoped_lock is only active through the calculate_changes and
- // transaction_ending events.
- base::AutoLock scoped_lock(dirkernel_->changes_channel_mutex);
-
- // Tell listeners to calculate changes while we still have the mutex.
- DirectoryChangeEvent event = { DirectoryChangeEvent::CALCULATE_CHANGES,
- originals.get(), this, writer_ };
- dirkernel_->changes_channel.Notify(event);
-
- // Necessary for reads to be performed prior to transaction mutex release.
- // Allows the listener to use the current transaction to perform reads.
- DirectoryChangeEvent ending_event =
- { DirectoryChangeEvent::TRANSACTION_ENDING,
- NULL, this, INVALID };
- dirkernel_->changes_channel.Notify(ending_event);
-
- dirkernel_->transaction_mutex.Release();
+ if (writer_ == syncable::SYNCAPI) {
+ dirkernel_->change_listener_->HandleCalculateChangesChangeEventFromSyncApi(
+ *originals.get(),
+ writer_,
+ this);
+ } else {
+ dirkernel_->change_listener_->HandleCalculateChangesChangeEventFromSyncer(
+ *originals.get(),
+ writer_,
+ this);
}
+ *models_with_changes = dirkernel_->change_listener_->
+ HandleTransactionEndingChangeEvent(this);
+
+ // Release the transaction. Note, once the transaction is released this thread
+ // can be interrupted by another that was waiting for the transaction,
+ // resulting in this code possibly being interrupted with another thread
+ // performing following the same code path. From this point foward, only
+ // local state can be touched.
+ dirkernel_->transaction_mutex.Release();
return true;
}
-void BaseTransaction::NotifyTransactionComplete() {
- // Transaction is no longer holding any locks/mutexes, notify that we're
- // complete (and commit any outstanding changes that should not be performed
- // while holding mutexes).
- DirectoryChangeEvent complete_event =
- { DirectoryChangeEvent::TRANSACTION_COMPLETE,
- NULL, NULL, INVALID };
- dirkernel_->changes_channel.Notify(complete_event);
+void BaseTransaction::NotifyTransactionComplete(
+ ModelTypeBitSet models_with_changes) {
+ dirkernel_->change_listener_->HandleTransactionCompleteChangeEvent(
+ models_with_changes);
}
ReadTransaction::ReadTransaction(Directory* directory, const char* file,
@@ -1208,21 +1282,33 @@ Id Entry::ComputePrevIdFromServerPosition(const Id& parent_id) const {
return dir()->ComputePrevIdFromServerPosition(kernel_, parent_id);
}
+DictionaryValue* Entry::ToValue() const {
+ DictionaryValue* entry_info = new DictionaryValue();
+ entry_info->SetBoolean("good", good());
+ if (good()) {
+ entry_info->Set("kernel", kernel_->ToValue());
+ entry_info->Set("serverModelType",
+ ModelTypeToValue(GetServerModelTypeHelper()));
+ entry_info->Set("modelType",
+ ModelTypeToValue(GetModelType()));
+ entry_info->SetBoolean("shouldMaintainPosition",
+ ShouldMaintainPosition());
+ entry_info->SetBoolean("existsOnClientBecauseNameIsNonEmpty",
+ ExistsOnClientBecauseNameIsNonEmpty());
+ entry_info->SetBoolean("isRoot", IsRoot());
+ }
+ return entry_info;
+}
+
const string& Entry::Get(StringField field) const {
DCHECK(kernel_);
return kernel_->ref(field);
}
syncable::ModelType Entry::GetServerModelType() const {
- ModelType specifics_type = GetModelTypeFromSpecifics(Get(SERVER_SPECIFICS));
+ ModelType specifics_type = GetServerModelTypeHelper();
if (specifics_type != UNSPECIFIED)
return specifics_type;
- if (IsRoot())
- return TOP_LEVEL_FOLDER;
- // Loose check for server-created top-level folders that aren't
- // bound to a particular model type.
- if (!Get(UNIQUE_SERVER_TAG).empty() && Get(SERVER_IS_DIR))
- return TOP_LEVEL_FOLDER;
// Otherwise, we don't have a server type yet. That should only happen
// if the item is an uncommitted locally created item.
@@ -1236,6 +1322,20 @@ syncable::ModelType Entry::GetServerModelType() const {
return UNSPECIFIED;
}
+syncable::ModelType Entry::GetServerModelTypeHelper() const {
+ ModelType specifics_type = GetModelTypeFromSpecifics(Get(SERVER_SPECIFICS));
+ if (specifics_type != UNSPECIFIED)
+ return specifics_type;
+ if (IsRoot())
+ return TOP_LEVEL_FOLDER;
+ // Loose check for server-created top-level folders that aren't
+ // bound to a particular model type.
+ if (!Get(UNIQUE_SERVER_TAG).empty() && Get(SERVER_IS_DIR))
+ return TOP_LEVEL_FOLDER;
+
+ return UNSPECIFIED;
+}
+
syncable::ModelType Entry::GetModelType() const {
ModelType specifics_type = GetModelTypeFromSpecifics(Get(SPECIFICS));
if (specifics_type != UNSPECIFIED)
@@ -1413,6 +1513,15 @@ bool MutableEntry::Put(ProtoField field,
return true;
}
+bool MutableEntry::Put(BitField field, bool value) {
+ DCHECK(kernel_);
+ if (kernel_->ref(field) != value) {
+ kernel_->put(field, value);
+ kernel_->mark_dirty(GetDirtyIndexHelper());
+ }
+ return true;
+}
+
MetahandleSet* MutableEntry::GetDirtyIndexHelper() {
return dir()->kernel_->dirty_metahandles;
}
@@ -1565,6 +1674,11 @@ bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
return true;
}
+bool MutableEntry::Put(BitTemp field, bool value) {
+ DCHECK(kernel_);
+ kernel_->put(field, value);
+ return true;
+}
///////////////////////////////////////////////////////////////////////////
// High-level functions
diff --git a/chrome/browser/sync/syncable/syncable.h b/chrome/browser/sync/syncable/syncable.h
index 3a3c597..c97da92 100644
--- a/chrome/browser/sync/syncable/syncable.h
+++ b/chrome/browser/sync/syncable/syncable.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -27,10 +27,10 @@
#include "chrome/browser/sync/syncable/directory_event.h"
#include "chrome/browser/sync/syncable/syncable_id.h"
#include "chrome/browser/sync/syncable/model_type.h"
-#include "chrome/browser/sync/util/channel.h"
#include "chrome/browser/sync/util/dbgq.h"
#include "chrome/common/deprecated/event_sys.h"
+class DictionaryValue;
struct PurgeInfo;
namespace sync_api {
@@ -40,6 +40,7 @@ class ReadNode;
}
namespace syncable {
+class DirectoryChangeListener;
class Entry;
std::ostream& operator<<(std::ostream& s, const Entry& e);
@@ -48,6 +49,9 @@ class DirectoryBackingStore;
static const int64 kInvalidMetaHandle = 0;
+// Update syncable_enum_conversions{.h,.cc,_unittest.cc} if you change
+// any fields in this file.
+
enum {
BEGIN_FIELDS = 0,
INT64_FIELDS_BEGIN = BEGIN_FIELDS
@@ -330,6 +334,10 @@ struct EntryKernel {
return id_fields[field - ID_FIELDS_BEGIN];
}
+ // Dumps all kernel info into a DictionaryValue and returns it.
+ // Transfers ownership of the DictionaryValue to the caller.
+ DictionaryValue* ToValue() const;
+
private:
// Tracks whether this entry needs to be saved to the database.
bool dirty_;
@@ -422,6 +430,10 @@ class Entry {
// SERVER_POSITION_IN_PARENT ordering.
Id ComputePrevIdFromServerPosition(const Id& parent_id) const;
+ // Dumps all entry info into a DictionaryValue and returns it.
+ // Transfers ownership of the DictionaryValue to the caller.
+ DictionaryValue* ToValue() const;
+
protected: // Don't allow creation on heap, except by sync API wrappers.
friend class sync_api::ReadNode;
void* operator new(size_t size) { return (::operator new)(size); }
@@ -436,6 +448,10 @@ class Entry {
EntryKernel* kernel_;
+ private:
+ // Like GetServerModelType() but without the DCHECKs.
+ ModelType GetServerModelTypeHelper() const;
+
DISALLOW_COPY_AND_ASSIGN(Entry);
};
@@ -481,9 +497,7 @@ class MutableEntry : public Entry {
bool Put(BaseVersion field, int64 value);
bool Put(ProtoField field, const sync_pb::EntitySpecifics& value);
- inline bool Put(BitField field, bool value) {
- return PutField(field, value);
- }
+ bool Put(BitField field, bool value);
inline bool Put(IsDelField field, bool value) {
return PutIsDel(value);
}
@@ -495,30 +509,11 @@ class MutableEntry : public Entry {
// ID to put the node in first position.
bool PutPredecessor(const Id& predecessor_id);
- inline bool Put(BitTemp field, bool value) {
- return PutTemp(field, value);
- }
+ bool Put(BitTemp field, bool value);
protected:
syncable::MetahandleSet* GetDirtyIndexHelper();
- template <typename FieldType, typename ValueType>
- inline bool PutField(FieldType field, const ValueType& value) {
- DCHECK(kernel_);
- if (kernel_->ref(field) != value) {
- kernel_->put(field, value);
- kernel_->mark_dirty(GetDirtyIndexHelper());
- }
- return true;
- }
-
- template <typename TempType, typename ValueType>
- inline bool PutTemp(TempType field, const ValueType& value) {
- DCHECK(kernel_);
- kernel_->put(field, value);
- return true;
- }
-
bool PutIsDel(bool value);
private: // Don't allow creation on heap, except by sync API wrappers.
@@ -638,35 +633,6 @@ enum WriterTag {
SYNCAPI
};
-// A separate Event type and channel for very frequent changes, caused
-// by anything, not just the user.
-struct DirectoryChangeEvent {
- enum {
- // Means listener should go through list of original entries and
- // calculate what it needs to notify. It should *not* call any
- // callbacks or attempt to lock anything because a
- // WriteTransaction is being held until the listener returns.
- CALCULATE_CHANGES,
- // Means the WriteTransaction is ending, and this is the absolute
- // last chance to perform any read operations in the current transaction.
- // It is not recommended that the listener perform any writes.
- TRANSACTION_ENDING,
- // Means the WriteTransaction has been released and the listener
- // can now take action on the changes it calculated.
- TRANSACTION_COMPLETE,
- // Channel is closing.
- SHUTDOWN
- } todo;
- // These members are only valid for CALCULATE_CHANGES.
- const OriginalEntries* originals;
- BaseTransaction* trans; // This is valid also for TRANSACTION_ENDING
- WriterTag writer;
- typedef DirectoryChangeEvent EventType;
- static inline bool IsChannelShutdownEvent(const EventType& e) {
- return SHUTDOWN == e.todo;
- }
-};
-
// The name Directory in this case means the entire directory
// structure within a single user account.
//
@@ -815,11 +781,11 @@ class Directory {
const std::string& name() const { return kernel_->name; }
- // (Account) Store birthday is opaque to the client,
- // so we keep it in the format it is in the proto buffer
- // in case we switch to a binary birthday later.
+ // (Account) Store birthday is opaque to the client, so we keep it in the
+ // format it is in the proto buffer in case we switch to a binary birthday
+ // later.
std::string store_birthday() const;
- void set_store_birthday(std::string store_birthday);
+ void set_store_birthday(const std::string& store_birthday);
std::string GetAndClearNotificationState();
void SetNotificationState(const std::string& notification_state);
@@ -827,8 +793,7 @@ class Directory {
// Unique to each account / client pair.
std::string cache_guid() const;
- browser_sync::ChannelHookup<DirectoryChangeEvent>* AddChangeObserver(
- browser_sync::ChannelEventHandler<DirectoryChangeEvent>* observer);
+ void SetChangeListener(DirectoryChangeListener* listener);
protected: // for friends, mainly used by Entry constructors
virtual EntryKernel* GetEntryByHandle(int64 handle);
@@ -1050,12 +1015,10 @@ class Directory {
// TODO(ncarter): Figure out what the hell this is, and comment it.
Channel* const channel;
- // The changes channel mutex is explicit because it must be locked
- // while holding the transaction mutex and released after
- // releasing the transaction mutex.
- browser_sync::Channel<DirectoryChangeEvent> changes_channel;
+ // The listener for directory change events, triggered when the transaction
+ // is ending.
+ DirectoryChangeListener* change_listener_;
- base::Lock changes_channel_mutex;
KernelShareInfoStatus info_status;
// These 3 members are backed in the share_info table, and
@@ -1132,8 +1095,10 @@ class BaseTransaction {
explicit BaseTransaction(Directory* directory);
void UnlockAndLog(OriginalEntries* entries);
- bool NotifyTransactionChangingAndEnding(OriginalEntries* entries);
- virtual void NotifyTransactionComplete();
+ virtual bool NotifyTransactionChangingAndEnding(
+ OriginalEntries* entries,
+ ModelTypeBitSet* models_with_changes);
+ virtual void NotifyTransactionComplete(ModelTypeBitSet models_with_changes);
Directory* const directory_;
Directory::Kernel* const dirkernel_; // for brevity
diff --git a/chrome/browser/sync/syncable/syncable_enum_conversions.cc b/chrome/browser/sync/syncable/syncable_enum_conversions.cc
new file mode 100644
index 0000000..95afe49
--- /dev/null
+++ b/chrome/browser/sync/syncable/syncable_enum_conversions.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2011 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.
+
+// Keep this file in sync with syncable.h.
+
+#include "chrome/browser/sync/syncable/syncable_enum_conversions.h"
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace syncable {
+
+// We can't tokenize expected_min/expected_max since it can be a
+// general expression.
+#define ASSERT_ENUM_BOUNDS(enum_min, enum_max, expected_min, expected_max) \
+ COMPILE_ASSERT(static_cast<int>(enum_min) == \
+ static_cast<int>(expected_min), \
+ enum_min##_not_expected_min); \
+ COMPILE_ASSERT(static_cast<int>(enum_max) == \
+ static_cast<int>(expected_max), \
+ enum_max##_not_expected_max);
+
+#define ENUM_CASE(enum_value) case enum_value: return #enum_value
+
+const char* GetMetahandleFieldString(MetahandleField metahandle_field) {
+ ASSERT_ENUM_BOUNDS(META_HANDLE, META_HANDLE,
+ INT64_FIELDS_BEGIN, BASE_VERSION - 1);
+ switch (metahandle_field) {
+ ENUM_CASE(META_HANDLE);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetBaseVersionString(BaseVersion base_version) {
+ ASSERT_ENUM_BOUNDS(BASE_VERSION, BASE_VERSION,
+ META_HANDLE + 1, SERVER_VERSION - 1);
+ switch (base_version) {
+ ENUM_CASE(BASE_VERSION);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetInt64FieldString(Int64Field int64_field) {
+ ASSERT_ENUM_BOUNDS(SERVER_VERSION, LOCAL_EXTERNAL_ID,
+ BASE_VERSION + 1, INT64_FIELDS_END - 1);
+ switch (int64_field) {
+ ENUM_CASE(SERVER_VERSION);
+ ENUM_CASE(MTIME);
+ ENUM_CASE(SERVER_MTIME);
+ ENUM_CASE(CTIME);
+ ENUM_CASE(SERVER_CTIME);
+ ENUM_CASE(SERVER_POSITION_IN_PARENT);
+ ENUM_CASE(LOCAL_EXTERNAL_ID);
+ case INT64_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetIdFieldString(IdField id_field) {
+ ASSERT_ENUM_BOUNDS(ID, NEXT_ID,
+ ID_FIELDS_BEGIN, ID_FIELDS_END - 1);
+ switch (id_field) {
+ ENUM_CASE(ID);
+ ENUM_CASE(PARENT_ID);
+ ENUM_CASE(SERVER_PARENT_ID);
+ ENUM_CASE(PREV_ID);
+ ENUM_CASE(NEXT_ID);
+ case ID_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetIndexedBitFieldString(IndexedBitField indexed_bit_field) {
+ ASSERT_ENUM_BOUNDS(IS_UNSYNCED, IS_UNAPPLIED_UPDATE,
+ BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1);
+ switch (indexed_bit_field) {
+ ENUM_CASE(IS_UNSYNCED);
+ ENUM_CASE(IS_UNAPPLIED_UPDATE);
+ case INDEXED_BIT_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetIsDelFieldString(IsDelField is_del_field) {
+ ASSERT_ENUM_BOUNDS(IS_DEL, IS_DEL,
+ INDEXED_BIT_FIELDS_END, IS_DIR - 1);
+ switch (is_del_field) {
+ ENUM_CASE(IS_DEL);
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetBitFieldString(BitField bit_field) {
+ ASSERT_ENUM_BOUNDS(IS_DIR, SERVER_IS_DEL,
+ IS_DEL + 1, BIT_FIELDS_END - 1);
+ switch (bit_field) {
+ ENUM_CASE(IS_DIR);
+ ENUM_CASE(SERVER_IS_DIR);
+ ENUM_CASE(SERVER_IS_DEL);
+ case BIT_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetStringFieldString(StringField string_field) {
+ ASSERT_ENUM_BOUNDS(NON_UNIQUE_NAME, UNIQUE_CLIENT_TAG,
+ STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1);
+ switch (string_field) {
+ ENUM_CASE(NON_UNIQUE_NAME);
+ ENUM_CASE(SERVER_NON_UNIQUE_NAME);
+ ENUM_CASE(UNIQUE_SERVER_TAG);
+ ENUM_CASE(UNIQUE_CLIENT_TAG);
+ case STRING_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetProtoFieldString(ProtoField proto_field) {
+ ASSERT_ENUM_BOUNDS(SPECIFICS, SERVER_SPECIFICS,
+ PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
+ switch (proto_field) {
+ ENUM_CASE(SPECIFICS);
+ ENUM_CASE(SERVER_SPECIFICS);
+ case PROTO_FIELDS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+const char* GetBitTempString(BitTemp bit_temp) {
+ ASSERT_ENUM_BOUNDS(SYNCING, SYNCING,
+ BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
+ switch (bit_temp) {
+ ENUM_CASE(SYNCING);
+ case BIT_TEMPS_END: break;
+ }
+ NOTREACHED();
+ return "";
+}
+
+#undef ENUM_CASE
+#undef ASSERT_ENUM_BOUNDS
+
+} // namespace syncable
diff --git a/chrome/browser/sync/syncable/syncable_enum_conversions.h b/chrome/browser/sync/syncable/syncable_enum_conversions.h
new file mode 100644
index 0000000..eede89e
--- /dev/null
+++ b/chrome/browser/sync/syncable/syncable_enum_conversions.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2011 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.
+
+#ifndef CHROME_BROWSER_SYNC_SYNCABLE_SYNCABLE_ENUM_CONVERSIONS_H_
+#define CHROME_BROWSER_SYNC_SYNCABLE_SYNCABLE_ENUM_CONVERSIONS_H_
+#pragma once
+
+// Keep this file in sync with syncable.h.
+
+#include "chrome/browser/sync/syncable/syncable.h"
+
+// Utility functions to get the string equivalent for some syncable
+// enums.
+
+namespace syncable {
+
+// The returned strings (which don't have to be freed) are in ASCII.
+// The result of passing in an invalid enum value is undefined.
+
+const char* GetMetahandleFieldString(MetahandleField metahandle_field);
+
+const char* GetBaseVersionString(BaseVersion base_version);
+
+const char* GetInt64FieldString(Int64Field int64_field);
+
+const char* GetIdFieldString(IdField id_field);
+
+const char* GetIndexedBitFieldString(IndexedBitField indexed_bit_field);
+
+const char* GetIsDelFieldString(IsDelField is_del_field);
+
+const char* GetBitFieldString(BitField bit_field);
+
+const char* GetStringFieldString(StringField string_field);
+
+const char* GetProtoFieldString(ProtoField proto_field);
+
+const char* GetBitTempString(BitTemp bit_temp);
+
+} // namespace syncable
+
+#endif // CHROME_BROWSER_SYNC_SYNCABLE_SYNCABLE_ENUM_CONVERSIONS_H_
diff --git a/chrome/browser/sync/syncable/syncable_enum_conversions_unittest.cc b/chrome/browser/sync/syncable/syncable_enum_conversions_unittest.cc
new file mode 100644
index 0000000..3661765
--- /dev/null
+++ b/chrome/browser/sync/syncable/syncable_enum_conversions_unittest.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 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.
+
+// Keep this file in sync with syncable.h.
+
+#include "chrome/browser/sync/syncable/syncable_enum_conversions.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace syncable {
+namespace {
+
+class SyncableEnumConversionsTest : public testing::Test {
+};
+
+template <class T>
+void TestEnumStringFunction(const char* (*enum_string_fn)(T),
+ int enum_min, int enum_max) {
+ EXPECT_LE(enum_min, enum_max);
+ for (int i = enum_min; i <= enum_max; ++i) {
+ const std::string& str = enum_string_fn(static_cast<T>(i));
+ EXPECT_FALSE(str.empty());
+ }
+}
+
+TEST_F(SyncableEnumConversionsTest, GetMetahandleFieldString) {
+ TestEnumStringFunction(
+ GetMetahandleFieldString, INT64_FIELDS_BEGIN, META_HANDLE);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetBaseVersionString) {
+ TestEnumStringFunction(
+ GetBaseVersionString, META_HANDLE + 1, BASE_VERSION);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetInt64FieldString) {
+ TestEnumStringFunction(
+ GetInt64FieldString, BASE_VERSION + 1, INT64_FIELDS_END - 1);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetIdFieldString) {
+ TestEnumStringFunction(
+ GetIdFieldString, ID_FIELDS_BEGIN, ID_FIELDS_END - 1);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetIndexedBitFieldString) {
+ TestEnumStringFunction(
+ GetIndexedBitFieldString, BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetIsDelFieldString) {
+ TestEnumStringFunction(
+ GetIsDelFieldString, INDEXED_BIT_FIELDS_END, IS_DEL);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetBitFieldString) {
+ TestEnumStringFunction(
+ GetBitFieldString, IS_DEL + 1, BIT_FIELDS_END - 1);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetStringFieldString) {
+ TestEnumStringFunction(
+ GetStringFieldString, STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetProtoFieldString) {
+ TestEnumStringFunction(
+ GetProtoFieldString, PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
+}
+
+TEST_F(SyncableEnumConversionsTest, GetBitTempString) {
+ TestEnumStringFunction(
+ GetBitTempString, BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
+}
+
+} // namespace
+} // namespace syncable
diff --git a/chrome/browser/sync/syncable/syncable_id.cc b/chrome/browser/sync/syncable/syncable_id.cc
index ea8aebd..ea34404 100644
--- a/chrome/browser/sync/syncable/syncable_id.cc
+++ b/chrome/browser/sync/syncable/syncable_id.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -7,6 +7,7 @@
#include <iosfwd>
#include "base/string_util.h"
+#include "base/values.h"
using std::ostream;
using std::string;
@@ -19,6 +20,10 @@ ostream& operator<<(ostream& out, const Id& id) {
return out;
}
+StringValue* Id::ToValue() const {
+ return Value::CreateStringValue(s_);
+}
+
string Id::GetServerId() const {
// Currently root is the string "0". We need to decide on a true value.
// "" would be convenient here, as the IsRoot call would not be needed.
diff --git a/chrome/browser/sync/syncable/syncable_id.h b/chrome/browser/sync/syncable/syncable_id.h
index abc7a02..33ed3b8 100644
--- a/chrome/browser/sync/syncable/syncable_id.h
+++ b/chrome/browser/sync/syncable/syncable_id.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -13,15 +13,12 @@
#include "base/hash_tables.h"
-extern "C" {
-struct sqlite3;
-struct sqlite3_stmt;
-}
+class StringValue;
namespace syncable {
struct EntryKernel;
class Id;
-} // namespace syncable
+}
class MockConnectionManager;
class SQLStatement;
@@ -96,6 +93,10 @@ class Id {
// by operator<.
Id GetLexicographicSuccessor() const;
+ // Dumps the ID as a value and returns it. Transfers ownership of
+ // the StringValue to the caller.
+ StringValue* ToValue() const;
+
// Three functions are used to work with our proto buffers.
std::string GetServerId() const;
static Id CreateFromServerId(const std::string& server_id);
diff --git a/chrome/browser/sync/syncable/syncable_id_unittest.cc b/chrome/browser/sync/syncable/syncable_id_unittest.cc
index 77517c7..7fdbb71 100644
--- a/chrome/browser/sync/syncable/syncable_id_unittest.cc
+++ b/chrome/browser/sync/syncable/syncable_id_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,6 +6,8 @@
#include <vector>
+#include "base/memory/scoped_ptr.h"
+#include "base/values.h"
#include "chrome/test/sync/engine/test_id_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -81,4 +83,27 @@ TEST(SyncableIdTest, GetLeastIdForLexicographicComparison) {
}
}
+namespace {
+
+// TODO(akalin): Move this to values_test_util.h.
+
+// Takes ownership of |actual|.
+void ExpectStringValue(const std::string& expected_str,
+ StringValue* actual) {
+ scoped_ptr<StringValue> scoped_actual(actual);
+ std::string actual_str;
+ EXPECT_TRUE(scoped_actual->GetAsString(&actual_str));
+ EXPECT_EQ(expected_str, actual_str);
+}
+
+} // namespace
+
+TEST(SyncableIdTest, ToValue) {
+ ExpectStringValue("r", Id::CreateFromServerId("0").ToValue());
+ ExpectStringValue("svalue", Id::CreateFromServerId("value").ToValue());
+
+ ExpectStringValue("r", Id::CreateFromClientString("0").ToValue());
+ ExpectStringValue("cvalue", Id::CreateFromClientString("value").ToValue());
+}
+
} // namespace syncable
diff --git a/chrome/browser/sync/syncable/syncable_unittest.cc b/chrome/browser/sync/syncable/syncable_unittest.cc
index 952aada..d9e3319 100644
--- a/chrome/browser/sync/syncable/syncable_unittest.cc
+++ b/chrome/browser/sync/syncable/syncable_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -23,10 +23,11 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
-#include "base/scoped_ptr.h"
-#include "base/scoped_temp_dir.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_temp_dir.h"
#include "base/string_util.h"
#include "base/threading/platform_thread.h"
+#include "base/values.h"
#include "chrome/browser/sync/engine/syncproto.h"
#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h"
#include "chrome/browser/sync/syncable/directory_backing_store.h"
@@ -34,13 +35,32 @@
#include "chrome/common/deprecated/event_sys-inl.h"
#include "chrome/test/sync/engine/test_id_factory.h"
#include "chrome/test/sync/engine/test_syncable_utils.h"
+#include "chrome/test/values_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h"
using browser_sync::TestIdFactory;
+using test::ExpectBooleanValue;
+using test::ExpectStringValue;
namespace syncable {
+class SyncableKernelTest : public testing::Test {};
+
+TEST_F(SyncableKernelTest, ToValue) {
+ EntryKernel kernel;
+ scoped_ptr<DictionaryValue> value(kernel.ToValue());
+ if (value.get()) {
+ // Not much to check without repeating the ToValue() code.
+ EXPECT_TRUE(value->HasKey("isDirty"));
+ // The extra +1 is for "isDirty".
+ EXPECT_EQ(BIT_TEMPS_END - BEGIN_FIELDS + 1,
+ static_cast<int>(value->size()));
+ } else {
+ ADD_FAILURE();
+ }
+}
+
namespace {
void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
MutableEntry* e,
@@ -237,6 +257,42 @@ TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) {
}
}
+TEST_F(SyncableGeneralTest, ToValue) {
+ Directory dir;
+ dir.Open(db_path_, "SimpleTest");
+
+ const Id id = TestIdFactory::FromNumber(99);
+ {
+ ReadTransaction rtrans(&dir, __FILE__, __LINE__);
+ Entry e(&rtrans, GET_BY_ID, id);
+ EXPECT_FALSE(e.good()); // Hasn't been written yet.
+
+ scoped_ptr<DictionaryValue> value(e.ToValue());
+ ExpectBooleanValue(false, *value, "good");
+ EXPECT_EQ(1u, value->size());
+ }
+
+ // Test creating a new meta entry.
+ {
+ WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__);
+ MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "new");
+ ASSERT_TRUE(me.good());
+ me.Put(ID, id);
+ me.Put(BASE_VERSION, 1);
+
+ scoped_ptr<DictionaryValue> value(me.ToValue());
+ ExpectBooleanValue(true, *value, "good");
+ EXPECT_TRUE(value->HasKey("kernel"));
+ ExpectStringValue("Unspecified", *value, "serverModelType");
+ ExpectStringValue("Unspecified", *value, "modelType");
+ ExpectBooleanValue(false, *value, "shouldMaintainPosition");
+ ExpectBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty");
+ ExpectBooleanValue(false, *value, "isRoot");
+ }
+
+ dir.SaveChanges();
+}
+
// A Directory whose backing store always fails SaveChanges by returning false.
class TestUnsaveableDirectory : public Directory {
public:
diff --git a/chrome/browser/sync/test_profile_sync_service.cc b/chrome/browser/sync/test_profile_sync_service.cc
index ed4668e..7f6d82a 100644
--- a/chrome/browser/sync/test_profile_sync_service.cc
+++ b/chrome/browser/sync/test_profile_sync_service.cc
@@ -24,41 +24,20 @@ using syncable::ModelType;
using syncable::ScopedDirLookup;
using sync_api::UserShare;
-ACTION_P(CallOnPaused, core) {
- core->OnPaused();
-};
-
-ACTION_P(CallOnResumed, core) {
- core->OnResumed();
-}
-
namespace browser_sync {
+using ::testing::_;
SyncBackendHostForProfileSyncTest::SyncBackendHostForProfileSyncTest(
Profile* profile,
- int num_expected_resumes,
- int num_expected_pauses,
bool set_initial_sync_ended_on_init,
bool synchronous_init)
: browser_sync::SyncBackendHost(profile),
synchronous_init_(synchronous_init) {
- // By default, the RequestPause and RequestResume methods will
- // send the confirmation notification and return true.
- ON_CALL(*this, RequestPause()).
- WillByDefault(testing::DoAll(CallOnPaused(core_),
- testing::Return(true)));
- ON_CALL(*this, RequestResume()).
- WillByDefault(testing::DoAll(CallOnResumed(core_),
- testing::Return(true)));
- ON_CALL(*this, RequestNudge()).WillByDefault(
+ ON_CALL(*this, RequestNudge(_)).WillByDefault(
testing::Invoke(this,
&SyncBackendHostForProfileSyncTest::
SimulateSyncCycleCompletedInitialSyncEnded));
-
- EXPECT_CALL(*this, RequestPause()).Times(num_expected_pauses);
- EXPECT_CALL(*this, RequestResume()).Times(num_expected_resumes);
- EXPECT_CALL(*this,
- RequestNudge()).Times(set_initial_sync_ended_on_init ? 0 : 1);
+ EXPECT_CALL(*this, RequestNudge(_)).Times(testing::AnyNumber());
}
SyncBackendHostForProfileSyncTest::~SyncBackendHostForProfileSyncTest() {}
@@ -73,7 +52,8 @@ void SyncBackendHostForProfileSyncTest::ConfigureDataTypes(
}
void SyncBackendHostForProfileSyncTest::
- SimulateSyncCycleCompletedInitialSyncEnded() {
+ SimulateSyncCycleCompletedInitialSyncEnded(
+ const tracked_objects::Location& location) {
syncable::ModelTypeBitSet sync_ended;
ModelSafeRoutingInfo enabled_types;
GetModelSafeRoutingInfo(&enabled_types);
@@ -90,13 +70,13 @@ void SyncBackendHostForProfileSyncTest::
sync_api::HttpPostProviderFactory*
SyncBackendHostForProfileSyncTest::MakeHttpBridgeFactory(
- URLRequestContextGetter* getter) {
+ net::URLRequestContextGetter* getter) {
return new browser_sync::TestHttpBridgeFactory;
}
void SyncBackendHostForProfileSyncTest::InitCore(
const Core::DoInitializeOptions& options) {
- std::wstring user = L"testuser";
+ std::wstring user = L"testuser@gmail.com";
core_loop()->PostTask(
FROM_HERE,
NewRunnableMethod(core_.get(),
@@ -147,6 +127,10 @@ void SyncBackendHostForProfileSyncTest::ProcessMessage(
}
}
+void SyncBackendHostForProfileSyncTest::StartConfiguration(Callback0::Type*) {
+ SyncBackendHost::FinishConfigureDataTypesOnFrontendLoop();
+}
+
void SyncBackendHostForProfileSyncTest::
SetDefaultExpectationsForWorkerCreation(ProfileMock* profile) {
EXPECT_CALL(*profile, GetPasswordStore(testing::_)).
@@ -181,8 +165,6 @@ TestProfileSyncService::TestProfileSyncService(
synchronous_backend_initialization_(
synchronous_backend_initialization),
synchronous_sync_configuration_(false),
- num_expected_resumes_(1),
- num_expected_pauses_(1),
initial_condition_setup_task_(initial_condition_setup_task),
set_initial_sync_ended_on_init_(true) {
RegisterPreferences();
@@ -219,6 +201,7 @@ void TestProfileSyncService::OnBackendInitialized() {
// Pretend we downloaded initial updates and set initial sync ended bits
// if we were asked to.
+ bool send_passphrase_required = false;
if (set_initial_sync_ended_on_init_) {
UserShare* user_share = GetUserShare();
DirectoryManager* dir_manager = user_share->dir_manager.get();
@@ -231,12 +214,18 @@ void TestProfileSyncService::OnBackendInitialized() {
ProfileSyncServiceTestHelper::CreateRoot(
syncable::NIGORI, GetUserShare(),
id_factory());
+
+ // A side effect of adding the NIGORI mode (normally done by the syncer)
+ // is a decryption attempt, which will fail the first time.
+ send_passphrase_required = true;
}
SetInitialSyncEndedForEnabledTypes();
}
ProfileSyncService::OnBackendInitialized();
+ if (send_passphrase_required)
+ OnPassphraseRequired(true);
// TODO(akalin): Figure out a better way to do this.
if (synchronous_backend_initialization_) {
@@ -254,12 +243,6 @@ void TestProfileSyncService::Observe(NotificationType type,
}
}
-void TestProfileSyncService::set_num_expected_resumes(int times) {
- num_expected_resumes_ = times;
-}
-void TestProfileSyncService::set_num_expected_pauses(int num) {
- num_expected_pauses_ = num;
-}
void TestProfileSyncService::dont_set_initial_sync_ended_on_init() {
set_initial_sync_ended_on_init_ = false;
}
@@ -270,7 +253,6 @@ void TestProfileSyncService::set_synchronous_sync_configuration() {
void TestProfileSyncService::CreateBackend() {
backend_.reset(new browser_sync::SyncBackendHostForProfileSyncTest(
profile(),
- num_expected_resumes_, num_expected_pauses_,
set_initial_sync_ended_on_init_,
synchronous_backend_initialization_));
}
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h
index c027725..0aaf07b 100644
--- a/chrome/browser/sync/test_profile_sync_service.h
+++ b/chrome/browser/sync/test_profile_sync_service.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -36,15 +36,11 @@ class SyncBackendHostForProfileSyncTest
// completed setting itself up and called us back.
SyncBackendHostForProfileSyncTest(
Profile* profile,
- int num_expected_resumes,
- int num_expected_pauses,
bool set_initial_sync_ended_on_init,
bool synchronous_init);
virtual ~SyncBackendHostForProfileSyncTest();
- MOCK_METHOD0(RequestPause, bool());
- MOCK_METHOD0(RequestResume, bool());
- MOCK_METHOD0(RequestNudge, void());
+ MOCK_METHOD1(RequestNudge, void(const tracked_objects::Location&));
virtual void ConfigureDataTypes(
const DataTypeController::TypeMap& data_type_controllers,
@@ -52,10 +48,11 @@ class SyncBackendHostForProfileSyncTest
CancelableTask* ready_task);
// Called when a nudge comes in.
- void SimulateSyncCycleCompletedInitialSyncEnded();
+ void SimulateSyncCycleCompletedInitialSyncEnded(
+ const tracked_objects::Location&);
virtual sync_api::HttpPostProviderFactory* MakeHttpBridgeFactory(
- URLRequestContextGetter* getter);
+ net::URLRequestContextGetter* getter);
virtual void InitCore(const Core::DoInitializeOptions& options);
@@ -71,6 +68,8 @@ class SyncBackendHostForProfileSyncTest
virtual void ProcessMessage(const std::string& name, const JsArgList& args,
const JsEventHandler* sender);
+ virtual void StartConfiguration(Callback0::Type* callback);
+
static void SetDefaultExpectationsForWorkerCreation(ProfileMock* profile);
static void SetHistoryServiceExpectations(ProfileMock* profile);
@@ -101,9 +100,6 @@ class TestProfileSyncService : public ProfileSyncService {
const NotificationSource& source,
const NotificationDetails& details);
- void set_num_expected_resumes(int times);
- void set_num_expected_pauses(int num);
-
// If this is called, configuring data types will require a syncer
// nudge.
void dont_set_initial_sync_ended_on_init();
@@ -133,8 +129,6 @@ class TestProfileSyncService : public ProfileSyncService {
// step is performed synchronously.
bool synchronous_sync_configuration_;
bool set_expect_resume_expectations_;
- int num_expected_resumes_;
- int num_expected_pauses_;
Task* initial_condition_setup_task_;
bool set_initial_sync_ended_on_init_;
diff --git a/chrome/browser/sync/token_migrator.cc b/chrome/browser/sync/token_migrator.cc
deleted file mode 100644
index a2f4e62..0000000
--- a/chrome/browser/sync/token_migrator.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 2010 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.
-
-#include "chrome/browser/sync/token_migrator.h"
-
-#include "chrome/browser/sync/profile_sync_service.h"
-#include "chrome/browser/sync/util/user_settings.h"
-#include "chrome/common/net/gaia/gaia_constants.h"
-
-const FilePath::CharType kSyncDataFolderName[] =
- FILE_PATH_LITERAL("Sync Data");
-
-const FilePath::CharType kBookmarkSyncUserSettingsDatabase[] =
- FILE_PATH_LITERAL("BookmarkSyncSettings.sqlite3");
-
-using browser_sync::UserSettings;
-
-TokenMigrator::TokenMigrator(ProfileSyncService* service,
- const FilePath& profile_path)
- : service_(service),
- database_location_(profile_path.Append(kSyncDataFolderName)) {
-}
-
-TokenMigrator::~TokenMigrator() {
-}
-
-void TokenMigrator::TryMigration() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- BrowserThread::PostTask(BrowserThread::DB, FROM_HERE,
- NewRunnableMethod(this, &TokenMigrator::LoadTokens));
-}
-
-void TokenMigrator::LoadTokens() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- scoped_ptr<UserSettings> user_settings(new UserSettings());
- FilePath settings_db_file =
- database_location_.Append(FilePath(kBookmarkSyncUserSettingsDatabase));
- if (!user_settings->Init(settings_db_file))
- return;
-
- if (!user_settings->GetLastUserAndServiceToken(GaiaConstants::kSyncService,
- &username_, &token_))
- return;
-
- BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
- NewRunnableMethod(this, &TokenMigrator::PostTokensBack));
-}
-
-void TokenMigrator::PostTokensBack() {
- DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
- service_->LoadMigratedCredentials(username_, token_);
-}
diff --git a/chrome/browser/sync/token_migrator.h b/chrome/browser/sync/token_migrator.h
deleted file mode 100644
index 81d6f6d..0000000
--- a/chrome/browser/sync/token_migrator.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright (c) 2010 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.
-
-#ifndef CHROME_BROWSER_SYNC_TOKEN_MIGRATOR_H_
-#define CHROME_BROWSER_SYNC_TOKEN_MIGRATOR_H_
-#pragma once
-
-#include <string>
-
-#include "base/file_path.h"
-#include "base/task.h"
-
-class ProfileSyncService;
-
-// The TokenMigrator provides a bridge between the IO thread and
-// the UI thread to load the old-style credentials from the user
-// settings DB, and post them back to the UI thread to store the
-// token in the token service.
-class TokenMigrator {
- public:
- TokenMigrator(ProfileSyncService* service,
- const FilePath& profile_path);
- ~TokenMigrator();
-
- // This is called on the UI thread, it only posts a task.
- // If migration succeeds, the tokens will become available in
- // the token service.
- void TryMigration();
-
- private:
- // This runs as a deferred task on the DB thread.
- void LoadTokens();
-
- // This runs as a deferred task on the UI thread (only after the DB thread is
- // finished with the data.
- void PostTokensBack();
-
- // The username and token retrieved from the user settings DB.
- std::string username_;
- std::string token_;
-
- // The ProfileSyncService owns this object, so this pointer is valid when
- // PostTokensBack is called.
- ProfileSyncService* service_;
-
- // The directory to search for the user settings database.
- FilePath database_location_;
-
- DISALLOW_COPY_AND_ASSIGN(TokenMigrator);
-};
-
-// We ensure this object will outlive its tasks, so don't need refcounting.
-DISABLE_RUNNABLE_METHOD_REFCOUNT(TokenMigrator);
-
-#endif // CHROME_BROWSER_SYNC_TOKEN_MIGRATOR_H_
diff --git a/chrome/browser/sync/tools/sync_listen_notifications.cc b/chrome/browser/sync/tools/sync_listen_notifications.cc
index 0eb9459..1a4382e 100644
--- a/chrome/browser/sync/tools/sync_listen_notifications.cc
+++ b/chrome/browser/sync/tools/sync_listen_notifications.cc
@@ -1,229 +1,70 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
+#include <cstdio>
#include <string>
-#include <vector>
#include "base/at_exit.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/observer_list.h"
-#include "base/string_util.h"
-#include "base/task.h"
-#include "base/weak_ptr.h"
-#include "chrome/browser/sync/notifier/cache_invalidation_packet_handler.h"
-#include "chrome/browser/sync/notifier/chrome_invalidation_client.h"
-#include "chrome/browser/sync/notifier/chrome_system_resources.h"
-#include "chrome/browser/sync/sync_constants.h"
-#include "jingle/notifier/base/xmpp_connection.h"
-#include "jingle/notifier/listener/listen_task.h"
-#include "jingle/notifier/listener/notification_constants.h"
-#include "jingle/notifier/listener/notification_defines.h"
-#include "jingle/notifier/listener/send_update_task.h"
-#include "jingle/notifier/listener/subscribe_task.h"
-#include "jingle/notifier/listener/xml_element_util.h"
-#include "net/base/cert_verifier.h"
-#include "net/base/ssl_config_service.h"
-#include "net/socket/client_socket_factory.h"
-#include "talk/base/cryptstring.h"
-#include "talk/base/logging.h"
-#include "talk/base/sigslot.h"
-#include "talk/base/task.h"
-#include "talk/xmpp/jid.h"
-#include "talk/xmpp/xmppclientsettings.h"
-#include "talk/xmpp/constants.h"
-#include "talk/xmpp/xmppengine.h"
+#include "chrome/browser/sync/notifier/sync_notifier.h"
+#include "chrome/browser/sync/notifier/sync_notifier_factory.h"
+#include "chrome/browser/sync/notifier/sync_notifier_observer.h"
+#include "chrome/browser/sync/syncable/model_type.h"
+#include "chrome/browser/sync/syncable/model_type_payload_map.h"
+#include "chrome/test/test_url_request_context_getter.h"
+#include "content/browser/browser_thread.h"
-// This is a simple utility that logs into an XMPP server, subscribes
-// to Sync notifications, and prints out any such notifications that
-// are received.
+// This is a simple utility that initializes a sync notifier and
+// listens to any received notifications.
namespace {
-// Main class that listens for and handles messages from the XMPP
-// client.
-class XmppNotificationClient : public notifier::XmppConnection::Delegate {
+// Class to print received notifications events.
+class NotificationPrinter : public sync_notifier::SyncNotifierObserver {
public:
- // An observer is notified when we are logged into XMPP or when an
- // error occurs.
- class Observer {
- public:
- virtual ~Observer() {}
-
- virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) = 0;
-
- virtual void OnError() = 0;
- };
-
- template <class T> XmppNotificationClient(T begin, T end) {
- for (T it = begin; it != end; ++it) {
- observer_list_.AddObserver(*it);
- }
- }
-
- virtual ~XmppNotificationClient() {}
-
- // Connect with the given XMPP settings and run until disconnected.
- void Run(const buzz::XmppClientSettings& xmpp_client_settings,
- net::CertVerifier* cert_verifier) {
- DCHECK(!xmpp_connection_.get());
- xmpp_connection_.reset(
- new notifier::XmppConnection(xmpp_client_settings, cert_verifier,
- this, NULL));
- MessageLoop::current()->Run();
- DCHECK(!xmpp_connection_.get());
- }
-
- virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) {
- FOR_EACH_OBSERVER(Observer, observer_list_, OnConnect(base_task));
- }
-
- virtual void OnError(buzz::XmppEngine::Error error, int subcode,
- const buzz::XmlElement* stream_error) {
- LOG(INFO) << "Error: " << error << ", subcode: " << subcode;
- if (stream_error) {
- LOG(INFO) << "Stream error: "
- << notifier::XmlElementToString(*stream_error);
- }
- FOR_EACH_OBSERVER(Observer, observer_list_, OnError());
- // This has to go before the message loop quits.
- xmpp_connection_.reset();
- MessageLoop::current()->Quit();
- }
-
- private:
- ObserverList<Observer> observer_list_;
- scoped_ptr<notifier::XmppConnection> xmpp_connection_;
-
- DISALLOW_COPY_AND_ASSIGN(XmppNotificationClient);
-};
-
-// Delegate for legacy notifications.
-class LegacyNotifierDelegate
- : public XmppNotificationClient::Observer,
- public sigslot::has_slots<> {
- public:
- explicit LegacyNotifierDelegate(bool send_initial_update)
- : send_initial_update_(send_initial_update) {}
-
- virtual ~LegacyNotifierDelegate() {}
-
- virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) {
- LOG(INFO) << "Logged in";
- std::vector<std::string> subscribed_services_list;
- subscribed_services_list.push_back(browser_sync::kSyncServiceUrl);
- // Owned by base_task.
- notifier::SubscribeTask* subscribe_task =
- new notifier::SubscribeTask(base_task, subscribed_services_list);
- subscribe_task->Start();
- // Owned by xmpp_client.
- notifier::ListenTask* listen_task =
- new notifier::ListenTask(base_task);
- listen_task->SignalUpdateAvailable.connect(
- this, &LegacyNotifierDelegate::OnUpdateAvailable);
- listen_task->Start();
- if (send_initial_update_) {
- // Owned by xmpp_client.
- notifier::SendUpdateTask* send_update_task =
- new notifier::SendUpdateTask(base_task,
- OutgoingNotificationData());
- send_update_task->Start();
- }
- }
-
- virtual void OnError() {}
-
- void OnUpdateAvailable(
- const IncomingNotificationData& notification_data) {
- LOG(INFO) << "Notification received: "
- << notification_data.service_url << " "
- << notification_data.service_specific_data;
- }
-
- private:
- bool send_initial_update_;
-};
-
-// The actual listener for sync notifications.
-class ChromeInvalidationListener
- : public sync_notifier::ChromeInvalidationClient::Listener {
- public:
- ChromeInvalidationListener() {}
-
- virtual void OnInvalidate(syncable::ModelType model_type,
- const std::string& payload) {
- LOG(INFO) << "OnInvalidate: "
- << syncable::ModelTypeToString(model_type) << " - " << payload;
- // A real implementation would respond to the invalidation.
- }
-
- virtual void OnInvalidateAll() {
- LOG(INFO) << "InvalidateAll";
- // A real implementation would loop over the current registered
- // data types and send notifications for those.
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(ChromeInvalidationListener);
-};
-
-// Delegate for server-issued notifications.
-class ServerNotifierDelegate
- : public XmppNotificationClient::Observer,
- public sync_notifier::StateWriter {
- public:
- explicit ServerNotifierDelegate(
- const std::string& server_notifier_state)
- : server_notifier_state_(server_notifier_state) {}
-
- virtual ~ServerNotifierDelegate() {}
-
- virtual void OnConnect(base::WeakPtr<talk_base::Task> base_task) {
- LOG(INFO) << "Logged in";
-
- // TODO(akalin): app_name should be per-client unique.
- const std::string kAppName = "cc_sync_listen_notifications";
- const std::string kAppInfo = kAppName;
- chrome_invalidation_client_.Start(kAppName, kAppInfo,
- server_notifier_state_,
- &chrome_invalidation_listener_,
- this, base_task);
- syncable::ModelTypeSet all_types;
- for (int i = syncable::FIRST_REAL_MODEL_TYPE;
- i < syncable::MODEL_TYPE_COUNT; ++i) {
- syncable::ModelType model_type = syncable::ModelTypeFromInt(i);
- all_types.insert(model_type);
+ NotificationPrinter() {}
+ virtual ~NotificationPrinter() {}
+
+ virtual void OnIncomingNotification(
+ const syncable::ModelTypePayloadMap& type_payloads) {
+ for (syncable::ModelTypePayloadMap::const_iterator it =
+ type_payloads.begin(); it != type_payloads.end(); ++it) {
+ LOG(INFO) << "Notification: type = "
+ << syncable::ModelTypeToString(it->first)
+ << ", payload = " << it->second;
}
-
- chrome_invalidation_client_.RegisterTypes(all_types);
}
- virtual void OnError() {
- chrome_invalidation_client_.Stop();
+ virtual void OnNotificationStateChange(bool notifications_enabled) {
+ LOG(INFO) << "Notifications enabled: " << notifications_enabled;
}
- virtual void WriteState(const std::string& state) {
+ virtual void StoreState(const std::string& state) {
std::string base64_state;
CHECK(base::Base64Encode(state, &base64_state));
- LOG(INFO) << "New state: " << base64_state;
+ LOG(INFO) << "Got state to store: " << base64_state;
}
private:
- ChromeInvalidationListener chrome_invalidation_listener_;
- // Opaque blob capturing the notifications state of a previous run
- // (i.e., the base64-decoded value of a string output by
- // WriteState()).
- std::string server_notifier_state_;
- sync_notifier::ChromeInvalidationClient chrome_invalidation_client_;
+ DISALLOW_COPY_AND_ASSIGN(NotificationPrinter);
};
} // namespace
int main(int argc, char* argv[]) {
base::AtExitManager exit_manager;
+ scoped_refptr<TestURLRequestContextGetter> request_context_getter(
+ new TestURLRequestContextGetter);
+ BrowserThread io_thread(BrowserThread::IO);
+ base::Thread::Options options;
+ options.message_loop_type = MessageLoop::TYPE_IO;
+ io_thread.StartWithOptions(options);
CommandLine::Init(argc, argv);
logging::InitLogging(
NULL,
@@ -231,96 +72,49 @@ int main(int argc, char* argv[]) {
logging::LOCK_LOG_FILE,
logging::DELETE_OLD_LOG_FILE,
logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS);
- // TODO(akalin): Make sure that all log messages are printed to the
- // console, even on Windows (SetMinLogLevel isn't enough).
- talk_base::LogMessage::LogToDebug(talk_base::LS_VERBOSE);
// Parse command line.
const CommandLine& command_line = *CommandLine::ForCurrentProcess();
std::string email = command_line.GetSwitchValueASCII("email");
- if (email.empty()) {
- printf("Usage: %s --email=foo@bar.com [--password=mypassword] "
- "[--server=talk.google.com] [--port=5222] [--allow-plain] "
- "[--disable-tls] [--use-ssl-tcp] [--server-notifier-state] "
- "[--use-legacy-notifier] "
- "[--legacy-notifier-send-initial-update]\n",
- argv[0]);
+ std::string token = command_line.GetSwitchValueASCII("token");
+ // TODO(akalin): Write a wrapper script that gets a token for an
+ // email and password and passes that in to this utility.
+ if (email.empty() || token.empty()) {
+ std::printf("Usage: %s --email=foo@bar.com --token=token\n\n"
+ "See sync_notifier_factory.cc for more switches.\n\n"
+ "Run chrome and set a breakpoint on "
+ "sync_api::SyncManager::SyncInternal::UpdateCredentials() "
+ "after logging into sync to get the token to pass into this "
+ "utility.\n",
+ argv[0]);
return -1;
}
- std::string password = command_line.GetSwitchValueASCII("password");
- std::string server = command_line.GetSwitchValueASCII("server");
- if (server.empty()) {
- server = "talk.google.com";
- }
- std::string port_str = command_line.GetSwitchValueASCII("port");
- int port = 5222;
- if (!port_str.empty()) {
- int port_from_port_str = std::strtol(port_str.c_str(), NULL, 10);
- if (port_from_port_str == 0) {
- LOG(WARNING) << "Invalid port " << port_str << "; using default";
- } else {
- port = port_from_port_str;
- }
- }
- bool allow_plain = command_line.HasSwitch("allow-plain");
- bool disable_tls = command_line.HasSwitch("disable-tls");
- bool use_ssl_tcp = command_line.HasSwitch("use-ssl-tcp");
- if (use_ssl_tcp && (port != 443)) {
- LOG(WARNING) << "--use-ssl-tcp is set but port is " << port
- << " instead of 443";
- }
- std::string server_notifier_state;
- std::string server_notifier_state_encoded =
- command_line.GetSwitchValueASCII("server-notifier-state");
- if (!server_notifier_state_encoded.empty() &&
- !base::Base64Decode(server_notifier_state_encoded,
- &server_notifier_state)) {
- LOG(ERROR) << "Could not decode state: "
- << server_notifier_state_encoded;
- }
- bool use_legacy_notifier = command_line.HasSwitch("use-legacy-notifier");
- bool legacy_notifier_send_initial_update =
- command_line.HasSwitch("legacy-notifier-send-initial-update");
- // Build XMPP client settings.
- buzz::XmppClientSettings xmpp_client_settings;
- buzz::Jid jid(email);
- xmpp_client_settings.set_user(jid.node());
- xmpp_client_settings.set_resource("cc_sync_listen_notifications");
- xmpp_client_settings.set_host(jid.domain());
- xmpp_client_settings.set_allow_plain(allow_plain);
- xmpp_client_settings.set_use_tls(!disable_tls);
- if (use_ssl_tcp) {
- xmpp_client_settings.set_protocol(cricket::PROTO_SSLTCP);
- }
- talk_base::InsecureCryptStringImpl insecure_crypt_string;
- insecure_crypt_string.password() = password;
- xmpp_client_settings.set_pass(
- talk_base::CryptString(insecure_crypt_string));
- talk_base::SocketAddress addr(server, port);
- if (!addr.ResolveIP()) {
- LOG(ERROR) << "Could not resolve " << addr.ToString();
- return -1;
- }
- xmpp_client_settings.set_server(addr);
+ // Needed by the SyncNotifier implementations.
+ MessageLoop main_loop;
- net::CertVerifier cert_verifier;
- MessageLoopForIO message_loop;
+ const char kClientInfo[] = "sync_listen_notifications";
+ sync_notifier::SyncNotifierFactory sync_notifier_factory(kClientInfo);
+ scoped_ptr<sync_notifier::SyncNotifier> sync_notifier(
+ sync_notifier_factory.CreateSyncNotifier(command_line,
+ request_context_getter.get()));
+ NotificationPrinter notification_printer;
+ sync_notifier->AddObserver(&notification_printer);
- // Connect and listen.
- ServerNotifierDelegate server_notifier_delegate(
- server_notifier_state);
- LegacyNotifierDelegate legacy_notifier_delegate(
- legacy_notifier_send_initial_update);
- std::vector<XmppNotificationClient::Observer*> observers;
- if (use_legacy_notifier) {
- observers.push_back(&legacy_notifier_delegate);
- } else {
- observers.push_back(&server_notifier_delegate);
+ sync_notifier->UpdateCredentials(email, token);
+ {
+ // Listen for notifications for all known types.
+ syncable::ModelTypeSet types;
+ for (int i = syncable::FIRST_REAL_MODEL_TYPE;
+ i < syncable::MODEL_TYPE_COUNT; ++i) {
+ types.insert(syncable::ModelTypeFromInt(i));
+ }
+ sync_notifier->UpdateEnabledTypes(types);
}
- XmppNotificationClient xmpp_notification_client(
- observers.begin(), observers.end());
- xmpp_notification_client.Run(xmpp_client_settings, &cert_verifier);
+ main_loop.Run();
+
+ sync_notifier->RemoveObserver(&notification_printer);
+ io_thread.Stop();
return 0;
}
diff --git a/chrome/browser/sync/tools/sync_tools.gyp b/chrome/browser/sync/tools/sync_tools.gyp
index 18d163c..4ebe6ea 100644
--- a/chrome/browser/sync/tools/sync_tools.gyp
+++ b/chrome/browser/sync/tools/sync_tools.gyp
@@ -9,18 +9,12 @@
'type': 'executable',
'sources': [
'sync_listen_notifications.cc',
- # We are directly including the sync_constants.cc and h files to avoid
- # pulling in browser.lib.
- # TODO(akalin): Fix this.
- '<(DEPTH)/chrome/browser/sync/sync_constants.cc',
- '<(DEPTH)/chrome/browser/sync/sync_constants.h',
],
'dependencies': [
'<(DEPTH)/base/base.gyp:base',
- '<(DEPTH)/chrome/chrome.gyp:common_constants',
'<(DEPTH)/chrome/chrome.gyp:sync_notifier',
- '<(DEPTH)/jingle/jingle.gyp:notifier',
- '<(DEPTH)/third_party/libjingle/libjingle.gyp:libjingle',
+ '<(DEPTH)/chrome/chrome.gyp:test_support_common',
+ '<(DEPTH)/content/content.gyp:content_browser',
],
},
],
diff --git a/chrome/browser/sync/util/channel.h b/chrome/browser/sync/util/channel.h
deleted file mode 100644
index 839d653..0000000
--- a/chrome/browser/sync/util/channel.h
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 2010 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.
-
-#ifndef CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_
-#define CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_
-#pragma once
-
-///////////////////////////////////////////////////////////////////////////////
-//
-// OVERVIEW:
-//
-// A threadsafe container for a list of observers. Observers are able to
-// remove themselves during iteration, and can be added on any thread. This
-// allows observers to safely remove themselves during notifications. It
-// also provides a handler when an observer is added that will remove the
-// observer on destruction.
-//
-// It is expected that all observers are removed before destruction.
-// The channel owner should notify all observers to disconnect on shutdown if
-// needed to ensure this.
-//
-// TYPICAL USAGE:
-//
-// class MyWidget {
-// public:
-// ...
-//
-// class Observer : public ChannelEventHandler<FooEvent> {
-// public:
-// virtual void HandleChannelEvent(const FooEvent& w) = 0;
-// };
-//
-// ChannelHookup<MyEvent>* AddObserver(Observer* obs) {
-// return channel_.AddObserver(obs);
-// }
-//
-// void RemoveObserver(Observer* obs) {
-// channel_.RemoveObserver(obs);
-// }
-//
-// void NotifyFoo(FooEvent& event) {
-// channel_.Notify(event);
-// }
-//
-// private:
-// Channel<FooEvent> channel_;
-// };
-//
-//
-///////////////////////////////////////////////////////////////////////////////
-
-#include "base/observer_list.h"
-#include "base/synchronization/lock.h"
-#include "base/threading/platform_thread.h"
-
-namespace browser_sync {
-
-template <typename EventType>
-class Channel;
-
-class EventHandler {
-};
-
-template <typename EventType>
-class ChannelEventHandler : public EventHandler {
- public:
- virtual void HandleChannelEvent(const EventType& event) = 0;
-
- protected:
- virtual ~ChannelEventHandler() {}
-};
-
-// This class manages a connection to a channel. When it is destroyed, it
-// will remove the listener from the channel observer list.
-template <typename EventType>
-class ChannelHookup {
- public:
- ChannelHookup(Channel<EventType>* channel,
- browser_sync::ChannelEventHandler<EventType>* handler)
- : channel_(channel),
- handler_(handler) {}
- ~ChannelHookup() {
- channel_->RemoveObserver(handler_);
- }
-
- private:
- Channel<EventType>* channel_;
- browser_sync::ChannelEventHandler<EventType>* handler_;
-};
-
-template <typename EventType>
-class Channel {
- public:
- typedef ObserverListBase<EventHandler> ChannelObserverList;
-
- Channel() : locking_thread_(0) {}
-
- ChannelHookup<EventType>* AddObserver(
- ChannelEventHandler<EventType>* observer) {
- base::AutoLock scoped_lock(event_handlers_mutex_);
- event_handlers_.AddObserver(observer);
- return new ChannelHookup<EventType>(this, observer);
- }
-
- void RemoveObserver(ChannelEventHandler<EventType>* observer) {
- // This can be called in response to a notification, so we may already have
- // locked this channel on this thread.
- bool need_lock = (locking_thread_ != base::PlatformThread::CurrentId());
- if (need_lock)
- event_handlers_mutex_.Acquire();
-
- event_handlers_mutex_.AssertAcquired();
- event_handlers_.RemoveObserver(observer);
- if (need_lock)
- event_handlers_mutex_.Release();
- }
-
- void Notify(const EventType& event) {
- base::AutoLock scoped_lock(event_handlers_mutex_);
-
- // This may result in an observer trying to remove itself, so keep track
- // of the thread we're locked on.
- locking_thread_ = base::PlatformThread::CurrentId();
-
- ChannelObserverList::Iterator it(event_handlers_);
- EventHandler* obs;
- while ((obs = it.GetNext()) != NULL) {
- static_cast<ChannelEventHandler<EventType>* >(obs)->
- HandleChannelEvent(event);
- }
-
- // Set back to an invalid thread id.
- locking_thread_ = 0;
- }
-
- private:
- base::Lock event_handlers_mutex_;
- base::PlatformThreadId locking_thread_;
- ObserverList<EventHandler> event_handlers_;
-};
-
-} // namespace browser_sync
-
-#endif // CHROME_BROWSER_SYNC_UTIL_CHANNEL_H_
diff --git a/chrome/browser/sync/util/channel_unittest.cc b/chrome/browser/sync/util/channel_unittest.cc
deleted file mode 100644
index f2317dc..0000000
--- a/chrome/browser/sync/util/channel_unittest.cc
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) 2010 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.
-
-#include "chrome/browser/sync/util/channel.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-struct TestEvent {
- explicit TestEvent(int foo) : data(foo) {}
- int data;
-};
-
-class TestObserver : public browser_sync::ChannelEventHandler<TestEvent> {
- public:
- virtual void HandleChannelEvent(const TestEvent& event) {
- delete hookup;
- hookup = 0;
- }
-
- browser_sync::ChannelHookup<TestEvent>* hookup;
-};
-
-TEST(ChannelTest, RemoveOnNotify) {
- browser_sync::Channel<TestEvent> channel;
- TestObserver observer;
-
- observer.hookup = channel.AddObserver(&observer);
-
- ASSERT_TRUE(0 != observer.hookup);
- channel.Notify(TestEvent(1));
- ASSERT_EQ(0, observer.hookup);
-}
diff --git a/chrome/browser/sync/util/cryptographer.cc b/chrome/browser/sync/util/cryptographer.cc
index da94681..82ec2c2 100644
--- a/chrome/browser/sync/util/cryptographer.cc
+++ b/chrome/browser/sync/util/cryptographer.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -36,6 +36,11 @@ bool Cryptographer::CanDecrypt(const sync_pb::EncryptedData& data) const {
return nigoris_.end() != nigoris_.find(data.key_name());
}
+bool Cryptographer::CanDecryptUsingDefaultKey(
+ const sync_pb::EncryptedData& data) const {
+ return default_nigori_ && (data.key_name() == default_nigori_->first);
+}
+
bool Cryptographer::Encrypt(const ::google::protobuf::MessageLite& message,
sync_pb::EncryptedData* encrypted) const {
DCHECK(encrypted);
diff --git a/chrome/browser/sync/util/cryptographer.h b/chrome/browser/sync/util/cryptographer.h
index adb809b..d5410a2 100644
--- a/chrome/browser/sync/util/cryptographer.h
+++ b/chrome/browser/sync/util/cryptographer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,8 +10,8 @@
#include <string>
#include "base/gtest_prod_util.h"
-#include "base/linked_ptr.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "chrome/browser/sync/protocol/nigori_specifics.pb.h"
#include "chrome/browser/sync/util/nigori.h"
@@ -60,6 +60,10 @@ class Cryptographer {
// about.
bool CanDecrypt(const sync_pb::EncryptedData& encrypted) const;
+ // Returns whether |encrypted| can be decrypted using the default encryption
+ // key.
+ bool CanDecryptUsingDefaultKey(const sync_pb::EncryptedData& encrypted) const;
+
// Encrypts |message| into |encrypted|. Returns true unless encryption fails.
// Note that encryption will fail if |message| isn't valid (eg. a required
// field isn't set).
@@ -94,7 +98,7 @@ class Cryptographer {
// false.
void SetPendingKeys(const sync_pb::EncryptedData& encrypted);
- // Attepmts to decrypt the set of keys that was copied in the previous call to
+ // Attempts to decrypt the set of keys that was copied in the previous call to
// SetPendingKeys using |params|. Returns true if the pending keys were
// successfully decrypted and installed.
bool DecryptPendingKeys(const KeyParams& params);
diff --git a/chrome/browser/sync/util/cryptographer_unittest.cc b/chrome/browser/sync/util/cryptographer_unittest.cc
index 30b3be0..f6881f4 100644
--- a/chrome/browser/sync/util/cryptographer_unittest.cc
+++ b/chrome/browser/sync/util/cryptographer_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,7 +6,7 @@
#include <string>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "chrome/browser/password_manager/encryptor.h"
#include "chrome/browser/sync/protocol/password_specifics.pb.h"
diff --git a/chrome/browser/sync/util/extensions_activity_monitor.cc b/chrome/browser/sync/util/extensions_activity_monitor.cc
index 1048ee2..60f6d1a 100644
--- a/chrome/browser/sync/util/extensions_activity_monitor.cc
+++ b/chrome/browser/sync/util/extensions_activity_monitor.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -7,8 +7,8 @@
#include "base/task.h"
#include "chrome/browser/extensions/extension_bookmarks_module.h"
#include "chrome/common/extensions/extension.h"
-#include "chrome/common/notification_service.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/util/extensions_activity_monitor.h b/chrome/browser/sync/util/extensions_activity_monitor.h
index f19090d..3cce238 100644
--- a/chrome/browser/sync/util/extensions_activity_monitor.h
+++ b/chrome/browser/sync/util/extensions_activity_monitor.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,8 +10,8 @@
#include "base/message_loop.h"
#include "base/synchronization/lock.h"
-#include "chrome/common/notification_observer.h"
-#include "chrome/common/notification_registrar.h"
+#include "content/common/notification_observer.h"
+#include "content/common/notification_registrar.h"
namespace browser_sync {
diff --git a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc
index ed51a36..b7e8e7c 100644
--- a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc
+++ b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -11,8 +11,8 @@
#include "chrome/browser/extensions/extension_bookmarks_module.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
-#include "chrome/common/notification_service.h"
#include "content/browser/browser_thread.h"
+#include "content/common/notification_service.h"
#include "testing/gtest/include/gtest/gtest.h"
using browser_sync::ExtensionsActivityMonitor;
@@ -73,8 +73,8 @@ class BookmarkAPIEventGenerator {
input.SetString(keys::kVersion, kTestExtensionVersion);
input.SetString(keys::kName, kTestExtensionName);
scoped_refptr<Extension> extension(Extension::Create(
- FilePath(extension_path), Extension::INVALID, input, false, true,
- &error));
+ FilePath(extension_path), Extension::INVALID, input,
+ Extension::STRICT_ERROR_CHECKS, &error));
bookmarks_function->set_name(T::function_name());
base::WaitableEvent done_event(false, false);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
@@ -132,8 +132,8 @@ class ExtensionsActivityMonitorTest : public testing::Test {
input.SetString(keys::kVersion, kTestExtensionVersion);
input.SetString(keys::kName, kTestExtensionName);
scoped_refptr<Extension> extension(Extension::Create(
- FilePath(extension_path), Extension::INVALID, input, false, true,
- &error));
+ FilePath(extension_path), Extension::INVALID, input,
+ Extension::STRICT_ERROR_CHECKS, &error));
EXPECT_EQ("", error);
return extension->id();
}
diff --git a/chrome/browser/sync/util/nigori.cc b/chrome/browser/sync/util/nigori.cc
index 51f767b..cc17499 100644
--- a/chrome/browser/sync/util/nigori.cc
+++ b/chrome/browser/sync/util/nigori.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -14,18 +14,18 @@
#include <vector>
#include "base/base64.h"
-#include "base/crypto/encryptor.h"
-#include "base/hmac.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/string_util.h"
+#include "crypto/encryptor.h"
+#include "crypto/hmac.h"
using base::Base64Encode;
using base::Base64Decode;
-using base::Encryptor;
-using base::HMAC;
using base::RandInt;
-using base::SymmetricKey;
+using crypto::Encryptor;
+using crypto::HMAC;
+using crypto::SymmetricKey;
namespace browser_sync {
diff --git a/chrome/browser/sync/util/nigori.h b/chrome/browser/sync/util/nigori.h
index 38d6fde..74fd6ec 100644
--- a/chrome/browser/sync/util/nigori.h
+++ b/chrome/browser/sync/util/nigori.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -8,8 +8,8 @@
#include <string>
-#include "base/crypto/symmetric_key.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
+#include "crypto/symmetric_key.h"
namespace browser_sync {
@@ -73,9 +73,9 @@ class Nigori {
static const size_t kSigningIterations = 1004;
private:
- scoped_ptr<base::SymmetricKey> user_key_;
- scoped_ptr<base::SymmetricKey> encryption_key_;
- scoped_ptr<base::SymmetricKey> mac_key_;
+ scoped_ptr<crypto::SymmetricKey> user_key_;
+ scoped_ptr<crypto::SymmetricKey> encryption_key_;
+ scoped_ptr<crypto::SymmetricKey> mac_key_;
};
} // namespace browser_sync
diff --git a/chrome/browser/sync/util/nigori_unittest.cc b/chrome/browser/sync/util/nigori_unittest.cc
index 8505e55..9c17762 100644
--- a/chrome/browser/sync/util/nigori_unittest.cc
+++ b/chrome/browser/sync/util/nigori_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,7 +6,7 @@
#include <string>
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/string_util.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/util/user_settings_unittest.cc b/chrome/browser/sync/util/user_settings_unittest.cc
index 0545b94..c4fcfcf 100644
--- a/chrome/browser/sync/util/user_settings_unittest.cc
+++ b/chrome/browser/sync/util/user_settings_unittest.cc
@@ -1,35 +1,36 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
#include <limits>
#include <string>
+#include "app/sql/statement.h"
#include "base/file_util.h"
-#include "base/scoped_temp_dir.h"
+#include "base/memory/scoped_temp_dir.h"
+#include "base/utf_string_conversions.h"
+#include "build/build_config.h"
#include "chrome/browser/password_manager/encryptor.h"
#include "chrome/browser/sync/syncable/directory_manager.h"
#include "chrome/browser/sync/util/user_settings.h"
-#include "chrome/common/sqlite_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
-using browser_sync::APEncode;
-using browser_sync::APDecode;
-using browser_sync::ExecOrDie;
-using browser_sync::UserSettings;
-
using std::numeric_limits;
-static const FilePath::CharType kV10UserSettingsDB[] =
+namespace {
+
+const FilePath::CharType kV10UserSettingsDB[] =
FILE_PATH_LITERAL("Version10Settings.sqlite3");
-static const FilePath::CharType kV11UserSettingsDB[] =
+const FilePath::CharType kV11UserSettingsDB[] =
FILE_PATH_LITERAL("Version11Settings.sqlite3");
-static const FilePath::CharType kOldStyleSyncDataDB[] =
+const FilePath::CharType kOldStyleSyncDataDB[] =
FILE_PATH_LITERAL("OldStyleSyncData.sqlite3");
+} // namespace
+
class UserSettingsTest : public testing::Test {
public:
- UserSettingsTest() : sync_data_("Some sync data") { }
+ UserSettingsTest() : sync_data_("Some sync data") {}
virtual void SetUp() {
#if defined(OS_MACOSX)
@@ -42,12 +43,11 @@ class UserSettingsTest : public testing::Test {
// Creates and populates the V10 database files within
// |destination_directory|.
void SetUpVersion10Databases(const FilePath& destination_directory) {
- sqlite3* primer_handle = NULL;
v10_user_setting_db_path_ =
destination_directory.Append(FilePath(kV10UserSettingsDB));
- ASSERT_EQ(SQLITE_OK, sqlite_utils::OpenSqliteDb(v10_user_setting_db_path_,
- &primer_handle));
- sqlite_utils::scoped_sqlite_db_ptr db(primer_handle);
+
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(v10_user_setting_db_path_));
old_style_sync_data_path_ =
destination_directory.Append(FilePath(kOldStyleSyncDataDB));
@@ -58,84 +58,89 @@ class UserSettingsTest : public testing::Test {
sync_data_.length())));
// Create settings table.
- ExecOrDie(primer_handle, "CREATE TABLE settings"
- " (email, key, value, "
- " PRIMARY KEY(email, key) ON CONFLICT REPLACE)");
+ ASSERT_TRUE(db.Execute(
+ "CREATE TABLE settings (email, key, value, PRIMARY KEY(email, key)"
+ " ON CONFLICT REPLACE)"));
+
// Add a blank signin table.
- ExecOrDie(primer_handle, "CREATE TABLE signin_types"
- " (signin, signin_type)");
+ ASSERT_TRUE(db.Execute(
+ "CREATE TABLE signin_types (signin, signin_type)"));
+
// Create and populate version table.
- ExecOrDie(primer_handle, "CREATE TABLE db_version ( version )");
+ ASSERT_TRUE(db.Execute("CREATE TABLE db_version (version)"));
{
- SQLStatement statement;
- const char query[] = "INSERT INTO db_version values ( ? )";
- statement.prepare(primer_handle, query);
- statement.bind_int(0, 10);
- if (SQLITE_DONE != statement.step()) {
- LOG(FATAL) << query << "\n" << sqlite3_errmsg(primer_handle);
- }
+ const char* query = "INSERT INTO db_version VALUES(?)";
+ sql::Statement s(db.GetUniqueStatement(query));
+ if (!s)
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
+
+ s.BindInt(0, 10);
+ if (!s.Run())
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
}
+
// Create shares table.
- ExecOrDie(primer_handle, "CREATE TABLE shares"
- " (email, share_name, file_name,"
- " PRIMARY KEY(email, share_name) ON CONFLICT REPLACE)");
+ ASSERT_TRUE(db.Execute(
+ "CREATE TABLE shares (email, share_name, file_name,"
+ " PRIMARY KEY(email, share_name) ON CONFLICT REPLACE)"));
// Populate a share.
{
- SQLStatement statement;
- const char query[] = "INSERT INTO shares values ( ?, ?, ? )";
- statement.prepare(primer_handle, query);
- statement.bind_string(0, "foo@foo.com");
- statement.bind_string(1, "foo@foo.com");
+ const char* query = "INSERT INTO shares VALUES(?, ?, ?)";
+ sql::Statement s(db.GetUniqueStatement(query));
+ if (!s)
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
+
+ s.BindString(0, "foo@foo.com");
+ s.BindString(1, "foo@foo.com");
#if defined(OS_WIN)
- statement.bind_string(2, WideToUTF8(old_style_sync_data_path_.value()));
+ s.BindString(2, WideToUTF8(old_style_sync_data_path_.value()));
#elif defined(OS_POSIX)
- statement.bind_string(2, old_style_sync_data_path_.value());
+ s.BindString(2, old_style_sync_data_path_.value());
#endif
- if (SQLITE_DONE != statement.step()) {
- LOG(FATAL) << query << "\n" << sqlite3_errmsg(primer_handle);
- }
+ if (!s.Run())
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
}
}
// Creates and populates the V11 database file within
// |destination_directory|.
void SetUpVersion11Database(const FilePath& destination_directory) {
- sqlite3* primer_handle = NULL;
v11_user_setting_db_path_ =
destination_directory.Append(FilePath(kV11UserSettingsDB));
- ASSERT_EQ(SQLITE_OK, sqlite_utils::OpenSqliteDb(v11_user_setting_db_path_,
- &primer_handle));
- sqlite_utils::scoped_sqlite_db_ptr db(primer_handle);
+
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(v11_user_setting_db_path_));
// Create settings table.
- ExecOrDie(primer_handle, "CREATE TABLE settings"
- " (email, key, value, "
- " PRIMARY KEY(email, key) ON CONFLICT REPLACE)");
+ ASSERT_TRUE(db.Execute(
+ "CREATE TABLE settings (email, key, value, PRIMARY KEY(email, key)"
+ " ON CONFLICT REPLACE)"));
// Create and populate version table.
- ExecOrDie(primer_handle, "CREATE TABLE db_version ( version )");
+ ASSERT_TRUE(db.Execute("CREATE TABLE db_version (version)"));
{
- SQLStatement statement;
- const char query[] = "INSERT INTO db_version values ( ? )";
- statement.prepare(primer_handle, query);
- statement.bind_int(0, 11);
- if (SQLITE_DONE != statement.step()) {
- LOG(FATAL) << query << "\n" << sqlite3_errmsg(primer_handle);
- }
+ const char* query = "INSERT INTO db_version VALUES(?)";
+ sql::Statement s(db.GetUniqueStatement(query));
+ if (!s)
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
+
+ s.BindInt(0, 11);
+ if (!s.Run())
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
}
- ExecOrDie(primer_handle, "CREATE TABLE signin_types"
- " (signin, signin_type)");
-
+ ASSERT_TRUE(db.Execute(
+ "CREATE TABLE signin_types (signin, signin_type)"));
{
- SQLStatement statement;
- const char query[] = "INSERT INTO signin_types values ( ?, ? )";
- statement.prepare(primer_handle, query);
- statement.bind_string(0, "test");
- statement.bind_string(1, "test");
- if (SQLITE_DONE != statement.step()) {
- LOG(FATAL) << query << "\n" << sqlite3_errmsg(primer_handle);
- }
+ const char* query = "INSERT INTO signin_types VALUES(?, ?)";
+ sql::Statement s(db.GetUniqueStatement(query));
+ if (!s)
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
+
+ s.BindString(0, "test");
+ s.BindString(1, "test");
+ if (!s.Run())
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
}
}
@@ -167,23 +172,24 @@ TEST_F(UserSettingsTest, MigrateFromV10ToV11) {
// Create a UserSettings, which should trigger migration code. We do this
// inside a scoped block so it closes itself and we can poke around to see
// what happened later.
- UserSettings settings;
+ browser_sync::UserSettings settings;
settings.Init(v10_user_setting_db_path());
}
// Now poke around using sqlite to see if UserSettings migrated properly.
- sqlite3* handle = NULL;
- ASSERT_EQ(SQLITE_OK, sqlite_utils::OpenSqliteDb(v10_user_setting_db_path(),
- &handle));
- sqlite_utils::scoped_sqlite_db_ptr db(handle);
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(v10_user_setting_db_path()));
// Note that we don't use ScopedStatement to avoid closing the sqlite handle
// before finalizing the statement.
{
- SQLStatement version_query;
- version_query.prepare(handle, "SELECT version FROM db_version");
- ASSERT_EQ(SQLITE_ROW, version_query.step());
- const int version = version_query.column_int(0);
+ const char* query = "SELECT version FROM db_version";
+ sql::Statement version_query(db.GetUniqueStatement(query));
+ if (!version_query)
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
+
+ ASSERT_TRUE(version_query.Step());
+ const int version = version_query.ColumnInt(0);
EXPECT_GE(version, 11);
}
@@ -202,25 +208,29 @@ TEST_F(UserSettingsTest, MigrateFromV11ToV12) {
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
SetUpVersion11Database(temp_dir.path());
{
- UserSettings settings;
+ browser_sync::UserSettings settings;
settings.Init(v11_user_setting_db_path());
}
- sqlite3* handle = NULL;
- ASSERT_EQ(SQLITE_OK, sqlite_utils::OpenSqliteDb(v11_user_setting_db_path(),
- &handle));
- sqlite_utils::scoped_sqlite_db_ptr db(handle);
+ sql::Connection db;
+ ASSERT_TRUE(db.Open(v11_user_setting_db_path()));
{
- SQLStatement version_query;
- version_query.prepare(handle, "SELECT version FROM db_version");
- ASSERT_EQ(SQLITE_ROW, version_query.step());
- const int version = version_query.column_int(0);
+ const char* query = "SELECT version FROM db_version";
+ sql::Statement version_query(db.GetUniqueStatement(query));
+ if (!version_query)
+ LOG(FATAL) << query << "\n" << db.GetErrorMessage();
+
+ ASSERT_TRUE(version_query.Step());
+ const int version = version_query.ColumnInt(0);
EXPECT_GE(version, 12);
- SQLStatement table_query;
- table_query.prepare(handle, "SELECT name FROM sqlite_master "
- "WHERE type='table' AND name='signin_types'");
- ASSERT_NE(SQLITE_ROW, table_query.step());
+ const char* query2 = "SELECT name FROM sqlite_master "
+ "WHERE type='table' AND name='signin_types'";
+ sql::Statement table_query(db.GetUniqueStatement(query2));
+ if (!table_query)
+ LOG(FATAL) << query2 << "\n" << db.GetErrorMessage();
+
+ ASSERT_FALSE(table_query.Step());
}
}
@@ -230,15 +240,15 @@ TEST_F(UserSettingsTest, APEncode) {
for (i = numeric_limits<char>::min(); i < numeric_limits<char>::max(); ++i)
test.push_back(i);
test.push_back(i);
- const std::string encoded = APEncode(test);
- const std::string decoded = APDecode(encoded);
+ const std::string encoded = browser_sync::APEncode(test);
+ const std::string decoded = browser_sync::APDecode(encoded);
ASSERT_EQ(test, decoded);
}
TEST_F(UserSettingsTest, PersistEmptyToken) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- UserSettings settings;
+ browser_sync::UserSettings settings;
settings.Init(temp_dir.path().AppendASCII("UserSettings.sqlite3"));
settings.SetAuthTokenForService("username", "service", "");
std::string username;
@@ -252,7 +262,7 @@ TEST_F(UserSettingsTest, PersistEmptyToken) {
TEST_F(UserSettingsTest, PersistNonEmptyToken) {
ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
- UserSettings settings;
+ browser_sync::UserSettings settings;
settings.Init(temp_dir.path().AppendASCII("UserSettings.sqlite3"));
settings.SetAuthTokenForService("username", "service",
"oonetuhasonteuhasonetuhasonetuhasonetuhasouhasonetuhasonetuhasonetuhah"