summaryrefslogtreecommitdiffstats
path: root/chrome/browser/sync
diff options
context:
space:
mode:
authorKristian Monsen <kristianm@google.com>2011-05-24 16:24:13 +0100
committerKristian Monsen <kristianm@google.com>2011-05-25 14:13:32 +0100
commit3f50c38dc070f4bb515c1b64450dae14f316474e (patch)
tree29f309f9534e05c47244eedb438fc612578d133b /chrome/browser/sync
parente23bef148f7be2bdf9c3cb2cd3aa5ceebf1190fb (diff)
downloadexternal_chromium-3f50c38dc070f4bb515c1b64450dae14f316474e.zip
external_chromium-3f50c38dc070f4bb515c1b64450dae14f316474e.tar.gz
external_chromium-3f50c38dc070f4bb515c1b64450dae14f316474e.tar.bz2
Merge Chromium at r10.0.634.0: Initial merge by git.
Change-Id: Iac2af492818d119bcc2562eb5fdabf5ab0b6df9c
Diffstat (limited to 'chrome/browser/sync')
-rw-r--r--chrome/browser/sync/abstract_profile_sync_service_test.h4
-rw-r--r--chrome/browser/sync/engine/read_node_mock.h6
-rw-r--r--chrome/browser/sync/engine/syncapi.cc5
-rw-r--r--chrome/browser/sync/engine/syncapi.h3
-rw-r--r--chrome/browser/sync/engine/syncer_thread.cc22
-rw-r--r--chrome/browser/sync/engine/syncer_thread.h17
-rw-r--r--chrome/browser/sync/engine/syncer_thread_unittest.cc2
-rw-r--r--chrome/browser/sync/glue/autofill_data_type_controller.h2
-rw-r--r--chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc2
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator.cc53
-rw-r--r--chrome/browser/sync/glue/autofill_model_associator.h2
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator.cc129
-rw-r--r--chrome/browser/sync/glue/autofill_profile_model_associator.h5
-rw-r--r--chrome/browser/sync/glue/change_processor_mock.cc13
-rw-r--r--chrome/browser/sync/glue/change_processor_mock.h5
-rw-r--r--chrome/browser/sync/glue/data_type_controller_mock.cc13
-rw-r--r--chrome/browser/sync/glue/data_type_controller_mock.h5
-rw-r--r--chrome/browser/sync/glue/data_type_manager_mock.cc24
-rw-r--r--chrome/browser/sync/glue/data_type_manager_mock.h15
-rw-r--r--chrome/browser/sync/glue/database_model_worker.cc2
-rw-r--r--chrome/browser/sync/glue/database_model_worker_unittest.cc2
-rw-r--r--chrome/browser/sync/glue/foreign_session_tracker.cc143
-rw-r--r--chrome/browser/sync/glue/foreign_session_tracker.h111
-rw-r--r--chrome/browser/sync/glue/history_model_worker.cc2
-rw-r--r--chrome/browser/sync/glue/http_bridge.cc1
-rw-r--r--chrome/browser/sync/glue/http_bridge.h23
-rw-r--r--chrome/browser/sync/glue/http_bridge_unittest.cc2
-rw-r--r--chrome/browser/sync/glue/password_model_worker.cc2
-rw-r--r--chrome/browser/sync/glue/session_change_processor.cc156
-rw-r--r--chrome/browser/sync/glue/session_model_associator.cc1028
-rw-r--r--chrome/browser/sync/glue/session_model_associator.h411
-rw-r--r--chrome/browser/sync/glue/session_model_associator_unittest.cc163
-rw-r--r--chrome/browser/sync/glue/sync_backend_host.h2
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_mock.cc26
-rw-r--r--chrome/browser/sync/glue/sync_backend_host_mock.h18
-rw-r--r--chrome/browser/sync/glue/ui_model_worker.cc8
-rw-r--r--chrome/browser/sync/glue/ui_model_worker.h8
-rw-r--r--chrome/browser/sync/glue/ui_model_worker_unittest.cc4
-rw-r--r--chrome/browser/sync/notifier/cache_invalidation_packet_handler.h4
-rw-r--r--chrome/browser/sync/notifier/chrome_invalidation_client.h4
-rw-r--r--chrome/browser/sync/notifier/chrome_system_resources.h4
-rw-r--r--chrome/browser/sync/notifier/registration_manager.h4
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.cc6
-rw-r--r--chrome/browser/sync/profile_sync_factory_mock.h5
-rw-r--r--chrome/browser/sync/profile_sync_service.cc55
-rw-r--r--chrome/browser/sync/profile_sync_service.h14
-rw-r--r--chrome/browser/sync/profile_sync_service_autofill_unittest.cc757
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.cc75
-rw-r--r--chrome/browser/sync/profile_sync_service_harness.h31
-rw-r--r--chrome/browser/sync/profile_sync_service_mock.cc9
-rw-r--r--chrome/browser/sync/profile_sync_service_mock.h6
-rw-r--r--chrome/browser/sync/profile_sync_service_session_unittest.cc188
-rw-r--r--chrome/browser/sync/profile_sync_service_typed_url_unittest.cc2
-rw-r--r--chrome/browser/sync/profile_sync_test_util.h4
-rw-r--r--chrome/browser/sync/protocol/session_specifics.proto30
-rw-r--r--chrome/browser/sync/resources/configure.html199
-rw-r--r--chrome/browser/sync/resources/encryption_login.html6
-rw-r--r--chrome/browser/sync/resources/firstpassphrase.html204
-rw-r--r--chrome/browser/sync/resources/setup_flow.html7
-rw-r--r--chrome/browser/sync/sync_setup_flow.cc142
-rw-r--r--chrome/browser/sync/sync_setup_flow.h14
-rw-r--r--chrome/browser/sync/sync_setup_wizard.cc42
-rw-r--r--chrome/browser/sync/sync_setup_wizard.h5
-rw-r--r--chrome/browser/sync/sync_setup_wizard_unittest.cc17
-rw-r--r--chrome/browser/sync/sync_ui_util.cc46
-rw-r--r--chrome/browser/sync/sync_ui_util.h5
-rw-r--r--chrome/browser/sync/syncable/syncable_unittest.cc72
-rw-r--r--chrome/browser/sync/test_profile_sync_service.h6
-rw-r--r--chrome/browser/sync/tools/sync_listen_notifications.cc1
-rw-r--r--chrome/browser/sync/util/channel.h8
-rw-r--r--chrome/browser/sync/util/extensions_activity_monitor_unittest.cc2
71 files changed, 3040 insertions, 1373 deletions
diff --git a/chrome/browser/sync/abstract_profile_sync_service_test.h b/chrome/browser/sync/abstract_profile_sync_service_test.h
index 57b3791..b5fc1c2 100644
--- a/chrome/browser/sync/abstract_profile_sync_service_test.h
+++ b/chrome/browser/sync/abstract_profile_sync_service_test.h
@@ -15,6 +15,7 @@
#include "chrome/browser/net/gaia/token_service.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/autofill_model_associator.h"
+#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
#include "chrome/browser/sync/glue/password_model_associator.h"
#include "chrome/browser/sync/glue/preference_model_associator.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
@@ -65,6 +66,9 @@ class ProfileSyncServiceTestHelper {
case syncable::AUTOFILL:
tag_name = browser_sync::kAutofillTag;
break;
+ case syncable::AUTOFILL_PROFILE:
+ tag_name = browser_sync::kAutofillProfileTag;
+ break;
case syncable::PREFERENCES:
tag_name = browser_sync::kPreferencesTag;
break;
diff --git a/chrome/browser/sync/engine/read_node_mock.h b/chrome/browser/sync/engine/read_node_mock.h
index f13eddd..564f01d 100644
--- a/chrome/browser/sync/engine/read_node_mock.h
+++ b/chrome/browser/sync/engine/read_node_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.
@@ -13,8 +13,9 @@
class ReadNodeMock : public sync_api::ReadNode {
public:
- ReadNodeMock() { }
+ ReadNodeMock() {}
virtual ~ReadNodeMock() {}
+
MOCK_METHOD2(InitByClientTagLookup,
bool(syncable::ModelType model_type, const std::string& tag));
MOCK_CONST_METHOD0(GetAutofillProfileSpecifics,
@@ -25,5 +26,6 @@ class ReadNodeMock : public sync_api::ReadNode {
MOCK_CONST_METHOD0(GetSuccessorId, int64());
MOCK_METHOD1(InitByIdLookup, bool(int64 id));
};
+
#endif // CHROME_BROWSER_SYNC_ENGINE_READ_NODE_MOCK_H_
diff --git a/chrome/browser/sync/engine/syncapi.cc b/chrome/browser/sync/engine/syncapi.cc
index 112878d..206125b 100644
--- a/chrome/browser/sync/engine/syncapi.cc
+++ b/chrome/browser/sync/engine/syncapi.cc
@@ -14,7 +14,6 @@
#include "base/lock.h"
#include "base/logging.h"
#include "base/message_loop.h"
-#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/sha1.h"
#include "base/string_util.h"
@@ -217,6 +216,10 @@ int64 BaseNode::GetId() const {
return GetEntry()->Get(syncable::META_HANDLE);
}
+int64 BaseNode::GetModificationTime() const {
+ return GetEntry()->Get(syncable::MTIME);
+}
+
bool BaseNode::GetIsFolder() const {
return GetEntry()->Get(syncable::IS_DIR);
}
diff --git a/chrome/browser/sync/engine/syncapi.h b/chrome/browser/sync/engine/syncapi.h
index e2503cb..d86ed71 100644
--- a/chrome/browser/sync/engine/syncapi.h
+++ b/chrome/browser/sync/engine/syncapi.h
@@ -153,6 +153,9 @@ class BaseNode {
// different ID value.
virtual int64 GetId() const;
+ // Returns the modification time of the object (in TimeTicks internal format).
+ int64 GetModificationTime() const;
+
// Nodes are hierarchically arranged into a single-rooted tree.
// InitByRootLookup on ReadNode allows access to the root. GetParentId is
// how you find a node's parent.
diff --git a/chrome/browser/sync/engine/syncer_thread.cc b/chrome/browser/sync/engine/syncer_thread.cc
index 501577d..3c0cd06 100644
--- a/chrome/browser/sync/engine/syncer_thread.cc
+++ b/chrome/browser/sync/engine/syncer_thread.cc
@@ -59,7 +59,7 @@ void SyncerThread::NudgeSyncerWithDataTypes(
int milliseconds_from_now,
NudgeSource source,
const syncable::ModelTypeBitSet& model_types) {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
if (vault_.syncer_ == NULL) {
return;
}
@@ -70,7 +70,7 @@ void SyncerThread::NudgeSyncerWithDataTypes(
void SyncerThread::NudgeSyncer(
int milliseconds_from_now,
NudgeSource source) {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
if (vault_.syncer_ == NULL) {
return;
}
@@ -107,7 +107,7 @@ SyncerThread::~SyncerThread() {
// and false otherwise.
bool SyncerThread::Start() {
{
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
if (thread_.IsRunning()) {
return true;
}
@@ -141,7 +141,7 @@ bool SyncerThread::Stop(int max_wait) {
void SyncerThread::RequestSyncerExitAndSetThreadStopConditions() {
{
- AutoLock lock(lock_);
+ 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
@@ -170,7 +170,7 @@ void SyncerThread::RequestSyncerExitAndSetThreadStopConditions() {
}
bool SyncerThread::RequestPause() {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
if (vault_.pause_requested_ || vault_.paused_)
return false;
@@ -195,7 +195,7 @@ void SyncerThread::Notify(SyncEngineEvent::EventCause cause) {
}
bool SyncerThread::RequestResume() {
- AutoLock lock(lock_);
+ 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_))
@@ -530,7 +530,7 @@ SyncerThread::WaitInterval SyncerThread::CalculatePollingWaitTime(
}
void SyncerThread::ThreadMain() {
- AutoLock lock(lock_);
+ 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();
@@ -637,7 +637,7 @@ SyncSourceInfo SyncerThread::MakeSyncSourceInfo(bool nudged,
}
void SyncerThread::CreateSyncer(const std::string& dirname) {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
VLOG(1) << "Creating syncer up for: " << dirname;
// The underlying database structure is ready, and we should create
// the syncer.
@@ -655,7 +655,7 @@ void SyncerThread::CreateSyncer(const std::string& dirname) {
// server.
static inline void CheckConnected(bool* connected,
HttpResponse::ServerConnectionCode code,
- ConditionVariable* condvar) {
+ 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
@@ -686,7 +686,7 @@ void SyncerThread::WatchConnectionManager(ServerConnectionManager* conn_mgr) {
void SyncerThread::HandleServerConnectionEvent(
const ServerConnectionEvent& event) {
if (ServerConnectionEvent::STATUS_CHANGED == event.what_happened) {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
CheckConnected(&vault_.connected_, event.connection_code,
&vault_field_changed_);
}
@@ -771,7 +771,7 @@ void SyncerThread::NudgeSyncImpl(int milliseconds_from_now,
}
void SyncerThread::SetNotificationsEnabled(bool notifications_enabled) {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
session_context_->set_notifications_enabled(notifications_enabled);
}
diff --git a/chrome/browser/sync/engine/syncer_thread.h b/chrome/browser/sync/engine/syncer_thread.h
index 6681225..22531d8 100644
--- a/chrome/browser/sync/engine/syncer_thread.h
+++ b/chrome/browser/sync/engine/syncer_thread.h
@@ -14,21 +14,22 @@
#include <vector>
#include "base/basictypes.h"
-#include "base/condition_variable.h"
#include "base/gtest_prod_util.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
-#include "base/thread.h"
+#include "base/synchronization/condition_variable.h"
+#include "base/threading/thread.h"
#include "base/time.h"
-#include "base/waitable_event.h"
-#if defined(OS_LINUX)
-#include "chrome/browser/sync/engine/idle_query_linux.h"
-#endif
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/sync/engine/syncer_types.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;
namespace browser_sync {
@@ -213,10 +214,10 @@ class SyncerThread : public base::RefCountedThreadSafe<SyncerThread>,
// Gets signaled whenever a thread outside of the syncer thread changes a
// protected field in the vault_.
- ConditionVariable vault_field_changed_;
+ base::ConditionVariable vault_field_changed_;
// Used to lock everything in |vault_|.
- Lock lock_;
+ base::Lock lock_;
private:
// Threshold multipler for how long before user should be considered idle.
diff --git a/chrome/browser/sync/engine/syncer_thread_unittest.cc b/chrome/browser/sync/engine/syncer_thread_unittest.cc
index 887bcc1..df0733b 100644
--- a/chrome/browser/sync/engine/syncer_thread_unittest.cc
+++ b/chrome/browser/sync/engine/syncer_thread_unittest.cc
@@ -8,7 +8,7 @@
#include "base/lock.h"
#include "base/scoped_ptr.h"
#include "base/time.h"
-#include "base/waitable_event.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"
diff --git a/chrome/browser/sync/glue/autofill_data_type_controller.h b/chrome/browser/sync/glue/autofill_data_type_controller.h
index d73edf2..2daedb4 100644
--- a/chrome/browser/sync/glue/autofill_data_type_controller.h
+++ b/chrome/browser/sync/glue/autofill_data_type_controller.h
@@ -10,7 +10,7 @@
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/profile_sync_service.h"
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 6ff8af6..127b591 100644
--- a/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc
+++ b/chrome/browser/sync/glue/autofill_data_type_controller_unittest.cc
@@ -8,7 +8,7 @@
#include "base/message_loop.h"
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/autofill/personal_data_manager.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/sync/glue/autofill_data_type_controller.h"
diff --git a/chrome/browser/sync/glue/autofill_model_associator.cc b/chrome/browser/sync/glue/autofill_model_associator.cc
index f1340df..289d5fb 100644
--- a/chrome/browser/sync/glue/autofill_model_associator.cc
+++ b/chrome/browser/sync/glue/autofill_model_associator.cc
@@ -237,8 +237,7 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
bool autofill_profile_not_migrated = HasNotMigratedYet(write_trans);
- if (MigrationLoggingEnabled() &&
- autofill_profile_not_migrated) {
+ if (VLOG_IS_ON(1) && autofill_profile_not_migrated) {
VLOG(1) << "[AUTOFILL MIGRATION]"
<< "Printing profiles from web db";
@@ -251,7 +250,7 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
}
}
- if (MigrationLoggingEnabled() && autofill_profile_not_migrated) {
+ if (autofill_profile_not_migrated) {
VLOG(1) << "[AUTOFILL MIGRATION]"
<< "Iterating over sync db";
}
@@ -271,11 +270,9 @@ bool AutofillModelAssociator::TraverseAndAssociateAllSyncNodes(
} else if (autofill.has_profile()) {
// Ignore autofill profiles if we are not upgrading.
if (autofill_profile_not_migrated) {
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION] Looking for "
- << autofill.profile().name_first()
- << autofill.profile().name_last();
- }
+ VLOG(1) << "[AUTOFILL MIGRATION] Looking for "
+ << autofill.profile().name_first()
+ << autofill.profile().name_last();
AddNativeProfileIfNeeded(
autofill.profile(),
bundle,
@@ -355,19 +352,15 @@ void AutofillModelAssociator::AddNativeProfileIfNeeded(
profile, all_profiles_from_db);
if (profile_in_web_db != NULL) {
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "Node found in web db. So associating";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "Node found in web db. So associating";
int64 sync_id = node.GetId();
std::string guid = profile_in_web_db->guid();
Associate(&guid, sync_id);
return;
} else { // Create a new node.
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "Node not found in web db so creating and associating";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "Node not found in web db so creating and associating";
std::string guid = guid::GenerateGUID();
Associate(&guid, node.GetId());
AutoFillProfile* p = new AutoFillProfile(guid);
@@ -555,11 +548,9 @@ bool AutofillModelAssociator::HasNotMigratedYet(
}
if (autofill_migration_state == syncable::INSUFFICIENT_INFO_TO_DETERMINE) {
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "current autofill migration state is insufficient info to"
- << "determine.";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "current autofill migration state is insufficient info to"
+ << "determine.";
sync_api::ReadNode autofill_profile_root_node(trans);
if (!autofill_profile_root_node.InitByTagLookup(
browser_sync::kAutofillProfileTag) ||
@@ -568,29 +559,21 @@ bool AutofillModelAssociator::HasNotMigratedYet(
sync_service()->backend()->SetAutofillMigrationState(
syncable::NOT_MIGRATED);
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "Current autofill migration state is NOT Migrated because"
- << "legacy autofill root node is present whereas new "
- << "Autofill profile root node is absent.";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "Current autofill migration state is NOT Migrated because"
+ << "legacy autofill root node is present whereas new "
+ << "Autofill profile root node is absent.";
return true;
}
sync_service()->backend()->SetAutofillMigrationState(syncable::MIGRATED);
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "Current autofill migration state is migrated.";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "Current autofill migration state is migrated.";
}
return false;
}
-bool AutofillModelAssociator::MigrationLoggingEnabled() {
- // [TODO] enable logging via a command line flag.
- return false;
-}
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/autofill_model_associator.h b/chrome/browser/sync/glue/autofill_model_associator.h
index 3f1104e..553580f 100644
--- a/chrome/browser/sync/glue/autofill_model_associator.h
+++ b/chrome/browser/sync/glue/autofill_model_associator.h
@@ -173,8 +173,6 @@ class AutofillModelAssociator
// user requested an abort.
bool IsAbortPending();
- bool MigrationLoggingEnabled();
-
ProfileSyncService* sync_service_;
WebDatabase* web_database_;
PersonalDataManager* personal_data_;
diff --git a/chrome/browser/sync/glue/autofill_profile_model_associator.cc b/chrome/browser/sync/glue/autofill_profile_model_associator.cc
index b2d2e2c..c517e2c 100644
--- a/chrome/browser/sync/glue/autofill_profile_model_associator.cc
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.cc
@@ -46,7 +46,7 @@ bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
std::vector<AutoFillProfile*>* new_profiles,
std::vector<std::string>* profiles_to_delete) {
- if (MigrationLoggingEnabled()) {
+ if (VLOG_IS_ON(1)) {
VLOG(1) << "[AUTOFILL MIGRATION]"
<< "Printing profiles from web db";
@@ -60,10 +60,9 @@ bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
}
}
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "Looking for the above data in sync db..";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "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();
@@ -72,15 +71,18 @@ bool AutofillProfileModelAssociator::TraverseAndAssociateChromeAutoFillProfiles(
std::string guid((*ix)->guid());
ReadNode node(write_trans);
- if (node.InitByClientTagLookup(syncable::AUTOFILL_PROFILE, guid)) {
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << " Found in sync db: "
- << (*ix)->GetFieldText(AutoFillType(NAME_FIRST))
- << (*ix)->GetFieldText(AutoFillType(NAME_LAST))
- << (*ix)->guid()
- << " so associating";
- }
+ if (node.InitByClientTagLookup(syncable::AUTOFILL_PROFILE, guid) &&
+ // The following check is to ensure the given sync node is not already
+ // associated with another profile. That could happen if the user has
+ // the same profile duplicated.
+ current_profiles->find(guid) == current_profiles->end()) {
+
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << " Found in sync db: "
+ << (*ix)->GetFieldText(AutoFillType(NAME_FIRST))
+ << (*ix)->GetFieldText(AutoFillType(NAME_LAST))
+ << (*ix)->guid()
+ << " so associating with node id " << node.GetId();
const sync_pb::AutofillProfileSpecifics& autofill(
node.GetAutofillProfileSpecifics());
if (OverwriteProfileWithServerData(*ix, autofill)) {
@@ -138,11 +140,9 @@ bool AutofillProfileModelAssociator::AssociateModels() {
return false;
}
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << " Now associating to the new autofill profile model associator"
- << " root node";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << " Now associating to the new autofill profile model associator"
+ << " root node";
DataBundle bundle;
{
// The write transaction lock is held inside this block.
@@ -251,7 +251,8 @@ 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);
@@ -263,9 +264,14 @@ int64 AutofillProfileModelAssociator::FindSyncNodeWithProfile(
}
const sync_pb::AutofillProfileSpecifics& autofill_specifics(
read_node.GetAutofillProfileSpecifics());
- OverwriteProfileWithServerData(&p, autofill_specifics);
- if (p.Compare(profile_from_db) == 0) {
- return sync_child_id;
+
+ // This find should be fast as the set uses tree.
+ if (current_profiles->find(autofill_specifics.guid())
+ == current_profiles->end()) {
+ OverwriteProfileWithServerData(&p, autofill_specifics);
+ if (p.Compare(profile_from_db) == 0) {
+ return sync_child_id;
+ }
}
sync_child_id = read_node.GetSuccessorId();
}
@@ -280,7 +286,10 @@ bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
std::set<std::string>* current_profiles,
std::vector<std::string>* profiles_to_delete) {
- int64 sync_node_id = FindSyncNodeWithProfile(trans, autofill_root, profile);
+ int64 sync_node_id = FindSyncNodeWithProfile(trans,
+ autofill_root,
+ profile,
+ current_profiles);
if (sync_node_id != sync_api::kInvalidId) {
// In case of duplicates throw away the local profile and apply the
// server profile.(The only difference between the 2 profiles are the guids)
@@ -298,14 +307,13 @@ bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
std::string guid = autofill_specifics.guid();
Associate(&guid, sync_node_id);
current_profiles->insert(autofill_specifics.guid());
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "Found in sync db but with a different guid: "
- << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST)))
- << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST)))
- << "New guid " << autofill_specifics.guid()
- << " so associating";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "Found in sync db but with a different guid: "
+ << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST)))
+ << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST)))
+ << "New guid " << autofill_specifics.guid() << " sync node id "
+ << sync_node_id << " so associating. Profile to be deleted "
+ << profile.guid();
} else {
sync_api::WriteNode node(trans);
if (!node.InitUniqueByCreation(
@@ -314,16 +322,17 @@ bool AutofillProfileModelAssociator::MakeNewAutofillProfileSyncNodeIfNeeded(
return false;
}
node.SetTitle(UTF8ToWide(profile.guid()));
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION]"
- << "NOT Found in sync db "
- << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST)))
- << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST)))
- << profile.guid()
- << " so creating a new sync node.";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION]"
+ << "NOT Found in sync db "
+ << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_FIRST)))
+ << UTF16ToUTF8(profile.GetFieldText(AutoFillType(NAME_LAST)))
+ << profile.guid()
+ << " so creating a new sync node. Sync node id "
+ << node.GetId();
AutofillProfileChangeProcessor::WriteAutofillProfile(profile, &node);
current_profiles->insert(profile.guid());
+ std::string guid = profile.guid();
+ Associate(&guid, node.GetId());
number_of_profiles_created_++;
}
@@ -335,11 +344,9 @@ bool AutofillProfileModelAssociator::TraverseAndAssociateAllSyncNodes(
const sync_api::ReadNode& autofill_root,
DataBundle* bundle) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ VLOG(1) << "[AUTOFILL MIGRATION] "
+ << " Iterating over sync nodes of autofill profile root node";
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION] "
- << " Iterating over sync nodes of autofill profile root node";
- }
int64 sync_child_id = autofill_root.GetFirstChildId();
while (sync_child_id != sync_api::kInvalidId) {
ReadNode sync_child(write_trans);
@@ -363,14 +370,15 @@ void AutofillProfileModelAssociator::AddNativeProfileIfNeeded(
const sync_api::ReadNode& node) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB));
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION] "
- << "Trying to lookup "
- << profile.name_first()
- << " "
- << profile.name_last()
- << " in the web db";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION] "
+ << "Trying to lookup "
+ << profile.name_first()
+ << " "
+ << profile.name_last()
+ << "sync node id " << node.GetId()
+ << " Guid " << profile.guid()
+ << " in the web db";
+
if (bundle->current_profiles.find(profile.guid()) ==
bundle->current_profiles.end()) {
std::string guid(profile.guid());
@@ -378,15 +386,11 @@ void AutofillProfileModelAssociator::AddNativeProfileIfNeeded(
AutoFillProfile* p = new AutoFillProfile(profile.guid());
OverwriteProfileWithServerData(p, profile);
bundle->new_profiles.push_back(p);
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION] "
- << " Did not find one so creating it on web db";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION] "
+ << " Did not find one so creating it on web db";
} else {
- if (MigrationLoggingEnabled()) {
- VLOG(1) << "[AUTOFILL MIGRATION] "
- << " Found it on web db. Moving on ";
- }
+ VLOG(1) << "[AUTOFILL MIGRATION] "
+ << " Found it on web db. Moving on ";
}
}
@@ -469,10 +473,5 @@ bool AutofillProfileModelAssociator::IsAbortPending() {
return abort_association_pending_;
}
-bool AutofillProfileModelAssociator::MigrationLoggingEnabled() {
- // TODO(lipalani) enable logging via a command line flag.
- return false;
-}
-
} // 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 f636b63..e2abdaf 100644
--- a/chrome/browser/sync/glue/autofill_profile_model_associator.h
+++ b/chrome/browser/sync/glue/autofill_profile_model_associator.h
@@ -161,9 +161,8 @@ class AutofillProfileModelAssociator
int64 FindSyncNodeWithProfile(sync_api::WriteTransaction* trans,
const sync_api::BaseNode& autofill_root,
- const AutoFillProfile& profile);
-
- bool MigrationLoggingEnabled();
+ const AutoFillProfile& profile,
+ std::set<std::string>* current_profiles);
ProfileSyncService* sync_service_;
WebDatabase* web_database_;
diff --git a/chrome/browser/sync/glue/change_processor_mock.cc b/chrome/browser/sync/glue/change_processor_mock.cc
new file mode 100644
index 0000000..faea86b
--- /dev/null
+++ b/chrome/browser/sync/glue/change_processor_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/change_processor_mock.h"
+
+namespace browser_sync {
+
+ChangeProcessorMock::ChangeProcessorMock() : ChangeProcessor(NULL) {}
+
+ChangeProcessorMock::~ChangeProcessorMock() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/change_processor_mock.h b/chrome/browser/sync/glue/change_processor_mock.h
index e138937..d74e40e 100644
--- a/chrome/browser/sync/glue/change_processor_mock.h
+++ b/chrome/browser/sync/glue/change_processor_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.
@@ -17,7 +17,8 @@ namespace browser_sync {
class ChangeProcessorMock : public ChangeProcessor {
public:
- ChangeProcessorMock() : ChangeProcessor(NULL) {}
+ ChangeProcessorMock();
+ virtual ~ChangeProcessorMock();
MOCK_METHOD3(ApplyChangesFromSyncModel,
void(const sync_api::BaseTransaction* trans,
const sync_api::SyncManager::ChangeRecord* changes,
diff --git a/chrome/browser/sync/glue/data_type_controller_mock.cc b/chrome/browser/sync/glue/data_type_controller_mock.cc
new file mode 100644
index 0000000..7909b86
--- /dev/null
+++ b/chrome/browser/sync/glue/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/data_type_controller_mock.h"
+
+namespace browser_sync {
+
+DataTypeControllerMock::DataTypeControllerMock() {}
+
+DataTypeControllerMock::~DataTypeControllerMock() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/data_type_controller_mock.h b/chrome/browser/sync/glue/data_type_controller_mock.h
index 09ec652..83d0b6b 100644
--- a/chrome/browser/sync/glue/data_type_controller_mock.h
+++ b/chrome/browser/sync/glue/data_type_controller_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.
@@ -13,6 +13,9 @@ namespace browser_sync {
class DataTypeControllerMock : public DataTypeController {
public:
+ DataTypeControllerMock();
+ virtual ~DataTypeControllerMock();
+
MOCK_METHOD1(Start, void(StartCallback* start_callback));
MOCK_METHOD0(Stop, void());
MOCK_METHOD0(enabled, bool());
diff --git a/chrome/browser/sync/glue/data_type_manager_mock.cc b/chrome/browser/sync/glue/data_type_manager_mock.cc
new file mode 100644
index 0000000..9cc445e
--- /dev/null
+++ b/chrome/browser/sync/glue/data_type_manager_mock.cc
@@ -0,0 +1,24 @@
+// 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_mock.h"
+
+namespace browser_sync {
+
+DataTypeManagerMock::DataTypeManagerMock()
+ : result_(OK) {
+ // By default, calling Configure will send a SYNC_CONFIGURE_START
+ // and SYNC_CONFIGURE_DONE notification with a DataTypeManager::OK
+ // detail.
+ ON_CALL(*this, Configure(testing::_)).
+ WillByDefault(testing::DoAll(
+ NotifyFromDataTypeManager(this,
+ NotificationType::SYNC_CONFIGURE_START),
+ NotifyFromDataTypeManagerWithResult
+ (this, NotificationType::SYNC_CONFIGURE_DONE, &result_)));
+}
+
+DataTypeManagerMock::~DataTypeManagerMock() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/data_type_manager_mock.h b/chrome/browser/sync/glue/data_type_manager_mock.h
index 2a1705a..05a3665 100644
--- a/chrome/browser/sync/glue/data_type_manager_mock.h
+++ b/chrome/browser/sync/glue/data_type_manager_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.
@@ -30,17 +30,8 @@ namespace browser_sync {
class DataTypeManagerMock : public DataTypeManager {
public:
- DataTypeManagerMock() : result_(OK) {
- // By default, calling Configure will send a SYNC_CONFIGURE_START
- // and SYNC_CONFIGURE_DONE notification with a DataTypeManager::OK
- // detail.
- ON_CALL(*this, Configure(testing::_)).
- WillByDefault(testing::DoAll(
- NotifyFromDataTypeManager(this,
- NotificationType::SYNC_CONFIGURE_START),
- NotifyFromDataTypeManagerWithResult
- (this, NotificationType::SYNC_CONFIGURE_DONE, &result_)));
- }
+ DataTypeManagerMock();
+ virtual ~DataTypeManagerMock();
MOCK_METHOD1(Configure, void(const TypeSet&));
MOCK_METHOD0(Stop, void());
diff --git a/chrome/browser/sync/glue/database_model_worker.cc b/chrome/browser/sync/glue/database_model_worker.cc
index b09c6c6..91bba5b 100644
--- a/chrome/browser/sync/glue/database_model_worker.cc
+++ b/chrome/browser/sync/glue/database_model_worker.cc
@@ -4,7 +4,7 @@
#include "chrome/browser/sync/glue/database_model_worker.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/browser_thread.h"
using base::WaitableEvent;
diff --git a/chrome/browser/sync/glue/database_model_worker_unittest.cc b/chrome/browser/sync/glue/database_model_worker_unittest.cc
index 8d399b1..7f60cea 100644
--- a/chrome/browser/sync/glue/database_model_worker_unittest.cc
+++ b/chrome/browser/sync/glue/database_model_worker_unittest.cc
@@ -5,7 +5,7 @@
#include "base/callback.h"
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
-#include "base/thread.h"
+#include "base/threading/thread.h"
#include "base/timer.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/sync/glue/database_model_worker.h"
diff --git a/chrome/browser/sync/glue/foreign_session_tracker.cc b/chrome/browser/sync/glue/foreign_session_tracker.cc
new file mode 100644
index 0000000..6db5c9c
--- /dev/null
+++ b/chrome/browser/sync/glue/foreign_session_tracker.cc
@@ -0,0 +1,143 @@
+// 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/glue/foreign_session_tracker.h"
+#include "chrome/browser/sync/glue/session_model_associator.h"
+
+namespace browser_sync {
+
+
+ForeignSessionTracker::ForeignSessionTracker() {
+}
+
+ForeignSessionTracker::~ForeignSessionTracker() {
+ clear();
+}
+
+bool ForeignSessionTracker::LookupAllForeignSessions(
+ std::vector<const ForeignSession*>* sessions) {
+ DCHECK(sessions);
+ // Fill vector of sessions from our foreign session map.
+ for (ForeignSessionMap::const_iterator i =
+ foreign_session_map_.begin(); i != foreign_session_map_.end(); ++i) {
+ // Only include sessions with open tabs.
+ ForeignSession* foreign_session = i->second;
+ if (foreign_session->windows.size() > 0 &&
+ !SessionModelAssociator::SessionWindowHasNoTabsToSync(
+ *foreign_session->windows[0])) {
+ sessions->push_back(foreign_session);
+ }
+ }
+
+ if (sessions->size() > 0)
+ return true;
+ else
+ return false;
+}
+
+bool ForeignSessionTracker::LookupSessionWindows(
+ const std::string& foreign_session_tag,
+ std::vector<SessionWindow*>* windows) {
+ DCHECK(windows);
+ ForeignSessionMap::iterator iter = foreign_session_map_.find(
+ foreign_session_tag);
+ if (iter == foreign_session_map_.end())
+ return false;
+ *windows = iter->second->windows;
+ return true;
+}
+
+bool ForeignSessionTracker::LookupSessionTab(
+ const std::string& tag,
+ SessionID::id_type tab_id,
+ const SessionTab** tab) {
+ DCHECK(tab);
+ if (foreign_tab_map_.find(tag) == foreign_tab_map_.end()) {
+ // We have no record of this session.
+ *tab = NULL;
+ return false;
+ }
+ if (foreign_tab_map_[tag]->find(tab_id) == foreign_tab_map_[tag]->end()) {
+ // We have no record of this tab.
+ *tab = NULL;
+ return false;
+ }
+ *tab = (*foreign_tab_map_[tag])[tab_id];
+ return true;
+}
+
+ForeignSession* ForeignSessionTracker::GetForeignSession(
+ const std::string& foreign_session_tag) {
+ scoped_ptr<ForeignSession> foreign_session;
+ if (foreign_session_map_.find(foreign_session_tag) !=
+ foreign_session_map_.end()) {
+ foreign_session.reset(foreign_session_map_[foreign_session_tag]);
+ } else {
+ foreign_session.reset(new ForeignSession);
+ foreign_session->foreign_session_tag = foreign_session_tag;
+ foreign_session_map_[foreign_session_tag] = foreign_session.get();
+ }
+ DCHECK(foreign_session.get());
+ return foreign_session.release();
+}
+
+bool ForeignSessionTracker::DeleteForeignSession(
+ const std::string& foreign_session_tag) {
+ ForeignSessionMap::iterator iter =
+ foreign_session_map_.find(foreign_session_tag);
+ if (iter != foreign_session_map_.end()) {
+ delete iter->second; // Delete the ForeignSession object.
+ foreign_session_map_.erase(iter);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+SessionTab* ForeignSessionTracker::GetSessionTab(
+ const std::string& foreign_session_tag,
+ SessionID::id_type tab_id,
+ bool has_window) {
+ if (foreign_tab_map_.find(foreign_session_tag) == foreign_tab_map_.end())
+ foreign_tab_map_[foreign_session_tag] = new IDToSessionTabMap;
+ scoped_ptr<SessionTab> tab;
+ IDToSessionTabMap::iterator iter =
+ foreign_tab_map_[foreign_session_tag]->find(tab_id);
+ if (iter != foreign_tab_map_[foreign_session_tag]->end()) {
+ tab.reset(iter->second);
+ if (has_window) // This tab is linked to a window, so it's not an orphan.
+ unmapped_tabs_.erase(tab.get());
+ VLOG(1) << "Associating " << foreign_session_tag << "'s seen tab " <<
+ tab_id << " at " << tab.get();
+ } else {
+ tab.reset(new SessionTab());
+ (*foreign_tab_map_[foreign_session_tag])[tab_id] = tab.get();
+ if (!has_window) // This tab is not linked to a window, it's an orphan.
+ unmapped_tabs_.insert(tab.get());
+ VLOG(1) << "Associating " << foreign_session_tag << "'s new tab " <<
+ tab_id << " at " << tab.get();
+ }
+ DCHECK(tab.get());
+ return tab.release();
+}
+
+void ForeignSessionTracker::clear() {
+ // Delete ForeignSession objects (which also deletes all their windows/tabs).
+ STLDeleteContainerPairSecondPointers(foreign_session_map_.begin(),
+ foreign_session_map_.end());
+ foreign_session_map_.clear();
+
+ // Delete IDToSessTab maps. Does not delete the SessionTab objects, because
+ // they should already be referenced through foreign_session_map_.
+ STLDeleteContainerPairSecondPointers(foreign_tab_map_.begin(),
+ foreign_tab_map_.end());
+ foreign_tab_map_.clear();
+
+ // Go through and delete any tabs we had allocated but had not yet placed into
+ // a ForeignSessionobject.
+ STLDeleteContainerPointers(unmapped_tabs_.begin(), unmapped_tabs_.end());
+ unmapped_tabs_.clear();
+}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/foreign_session_tracker.h b/chrome/browser/sync/glue/foreign_session_tracker.h
new file mode 100644
index 0000000..dd99455
--- /dev/null
+++ b/chrome/browser/sync/glue/foreign_session_tracker.h
@@ -0,0 +1,111 @@
+// 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_GLUE_FOREIGN_SESSION_TRACKER_H_
+#define CHROME_BROWSER_SYNC_GLUE_FOREIGN_SESSION_TRACKER_H_
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/scoped_vector.h"
+#include "chrome/browser/sessions/session_id.h"
+#include "chrome/browser/sessions/session_types.h"
+
+namespace browser_sync {
+
+// Class to manage foreign sessions. The tracker will own all ForeignSession
+// and SessionTab objects it creates, and deletes them appropriately on
+// destruction.
+class ForeignSessionTracker {
+ public:
+ ForeignSessionTracker();
+ ~ForeignSessionTracker();
+
+ // Fill a preallocated vector with all foreign sessions we're tracking.
+ // Returns true if we had foreign sessions to fill it with, false otherwise.
+ bool LookupAllForeignSessions(std::vector<const ForeignSession*>* sessions);
+
+ // Attempts to look up the session windows associatd with the foreign session
+ // given by |foreign_session_tag|.
+ // If lookup succeeds:
+ // - Fills windows with the SessionWindow pointers, returns true.
+ // Else
+ // - Returns false.
+ bool LookupSessionWindows(const std::string& foreign_session_tag,
+ std::vector<SessionWindow*>* windows);
+
+ // Attempts to look up the foreign tab associated with the given tag and tab
+ // id.
+ // If lookup succeeds:
+ // - Sets tab to point to the SessionTab, and returns true.
+ // Else
+ // - Returns false, tab is set to NULL.
+ bool LookupSessionTab(const std::string& foreign_session_tag,
+ SessionID::id_type tab_id,
+ const SessionTab** tab);
+
+ // Returns a pointer to the ForeignSession object associated with
+ // foreign_session_tag. If none exists, creates one and returns its pointer.
+ ForeignSession* GetForeignSession(const std::string& foreign_session_tag);
+
+ // Deletes the foreign session associated with |foreign_session_tag| if it
+ // exists.
+ // Returns true if the session existed and was deleted, false otherwise.
+ bool DeleteForeignSession(const std::string& foreign_session_tag);
+
+ // Returns a pointer to the SessionTab object associated with |tab_id| for
+ // the session specified with |foreign_session_tag|. If none exists, creates
+ // one and returns its pointer.
+ // |has_window| determines if newly created tabs are added to the pool of
+ // orphaned tabs (thos which can't be reached by traversing foreign sessions).
+ SessionTab* GetSessionTab(const std::string& foreign_session_tag,
+ SessionID::id_type tab_id,
+ bool has_window);
+
+ // Free the memory for all dynamically allocated objects and clear the
+ // tracking structures.
+ void clear();
+
+ inline bool empty() {
+ return foreign_tab_map_.empty() && foreign_session_map_.empty();
+ }
+
+ inline size_t num_foreign_sessions() {
+ return foreign_session_map_.size();
+ }
+
+ inline size_t num_foreign_tabs(const std::string& foreign_session_tag) {
+ if (foreign_tab_map_.find(foreign_session_tag) != foreign_tab_map_.end()) {
+ return foreign_tab_map_[foreign_session_tag]->size();
+ } else {
+ return 0;
+ }
+ }
+ private:
+ // Datatypes for accessing foreign tab data.
+ typedef std::map<SessionID::id_type, SessionTab*> IDToSessionTabMap;
+ typedef std::map<std::string, IDToSessionTabMap*> ForeignTabMap;
+ typedef std::map<std::string, ForeignSession*> ForeignSessionMap;
+
+ // Per foreign client mapping of their tab id's to their SessionTab objects.
+ ForeignTabMap foreign_tab_map_;
+
+ // Map of foreign sessions, accessed by the foreign client id.
+ ForeignSessionMap foreign_session_map_;
+
+ // The set of foreign tabs that we have seen, and created SessionTab objects
+ // for, but have not yet mapped to ForeignSessions. These are temporarily
+ // orphaned tabs, and won't be deleted if we delete foreign_session_map_.
+ std::set<SessionTab*> unmapped_tabs_;
+
+ DISALLOW_COPY_AND_ASSIGN(ForeignSessionTracker);
+};
+
+} // namespace browser_sync
+
+#endif // CHROME_BROWSER_SYNC_GLUE_FOREIGN_SESSION_TRACKER_H_
diff --git a/chrome/browser/sync/glue/history_model_worker.cc b/chrome/browser/sync/glue/history_model_worker.cc
index 9b0ea50..e880476 100644
--- a/chrome/browser/sync/glue/history_model_worker.cc
+++ b/chrome/browser/sync/glue/history_model_worker.cc
@@ -7,7 +7,7 @@
#include "base/message_loop.h"
#include "base/ref_counted.h"
#include "base/task.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/history/history.h"
using base::WaitableEvent;
diff --git a/chrome/browser/sync/glue/http_bridge.cc b/chrome/browser/sync/glue/http_bridge.cc
index 8dc2b6f..2ec5a1b 100644
--- a/chrome/browser/sync/glue/http_bridge.cc
+++ b/chrome/browser/sync/glue/http_bridge.cc
@@ -193,6 +193,7 @@ void HttpBridge::MakeAsynchronousPost() {
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();
}
diff --git a/chrome/browser/sync/glue/http_bridge.h b/chrome/browser/sync/glue/http_bridge.h
index 530c215..5fb2419 100644
--- a/chrome/browser/sync/glue/http_bridge.h
+++ b/chrome/browser/sync/glue/http_bridge.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.
@@ -9,7 +9,7 @@
#include <string>
#include "base/ref_counted.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/common/net/url_fetcher.h"
#include "chrome/common/net/url_request_context_getter.h"
@@ -41,13 +41,13 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
// 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 URLRequestContext {
+ class RequestContext : public net::URLRequestContext {
public:
// |baseline_context| is used to obtain the accept-language,
// accept-charsets, and proxy service information for bridged requests.
// Typically |baseline_context| should be the URLRequestContext of the
// currently active profile.
- explicit RequestContext(URLRequestContext* baseline_context);
+ explicit RequestContext(net::URLRequestContext* baseline_context);
// Set the user agent for requests using this context. The default is
// the browser's UA string.
@@ -57,11 +57,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
// If the user agent is set explicitly return that, otherwise call the
// base class method to return default value.
return user_agent_.empty() ?
- URLRequestContext::GetUserAgent(url) : user_agent_;
- }
-
- virtual bool AllowSendingCookies(const net::URLRequest* request) const {
- return false;
+ net::URLRequestContext::GetUserAgent(url) : user_agent_;
}
private:
@@ -69,7 +65,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
~RequestContext();
std::string user_agent_;
- URLRequestContext* baseline_context_;
+ net::URLRequestContext* baseline_context_;
DISALLOW_COPY_AND_ASSIGN(RequestContext);
};
@@ -84,7 +80,7 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
bool is_user_agent_set() const { return !user_agent_.empty(); }
// URLRequestContextGetter implementation.
- virtual URLRequestContext* GetURLRequestContext();
+ virtual net::URLRequestContext* GetURLRequestContext();
virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const;
private:
@@ -121,8 +117,9 @@ class HttpBridge : public base::RefCountedThreadSafe<HttpBridge>,
const std::string& name) const;
// URLFetcher::Delegate implementation.
- virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url,
- const URLRequestStatus& status,
+ virtual void OnURLFetchComplete(const URLFetcher* source,
+ const GURL& url,
+ const net::URLRequestStatus& status,
int response_code,
const ResponseCookies& cookies,
const std::string& data);
diff --git a/chrome/browser/sync/glue/http_bridge_unittest.cc b/chrome/browser/sync/glue/http_bridge_unittest.cc
index 57fca13..f9aa32c 100644
--- a/chrome/browser/sync/glue/http_bridge_unittest.cc
+++ b/chrome/browser/sync/glue/http_bridge_unittest.cc
@@ -3,7 +3,7 @@
// found in the LICENSE file.
#include "base/message_loop_proxy.h"
-#include "base/thread.h"
+#include "base/threading/thread.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/sync/glue/http_bridge.h"
#include "chrome/common/net/test_url_fetcher_factory.h"
diff --git a/chrome/browser/sync/glue/password_model_worker.cc b/chrome/browser/sync/glue/password_model_worker.cc
index c06f33f..6132a03 100644
--- a/chrome/browser/sync/glue/password_model_worker.cc
+++ b/chrome/browser/sync/glue/password_model_worker.cc
@@ -7,7 +7,7 @@
#include "base/callback.h"
#include "base/ref_counted.h"
#include "base/task.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/password_manager/password_store.h"
using base::WaitableEvent;
diff --git a/chrome/browser/sync/glue/session_change_processor.cc b/chrome/browser/sync/glue/session_change_processor.cc
index ca0b166..8823af0 100644
--- a/chrome/browser/sync/glue/session_change_processor.cc
+++ b/chrome/browser/sync/glue/session_change_processor.cc
@@ -10,9 +10,10 @@
#include "base/logging.h"
#include "base/scoped_vector.h"
-#include "chrome/browser/browser_thread.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/tab_contents.h"
#include "chrome/common/notification_details.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/notification_source.h"
@@ -40,18 +41,95 @@ void SessionChangeProcessor::Observe(NotificationType type,
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(running());
DCHECK(profile_);
+
+ // Track which windows and/or tabs are modified.
+ std::vector<TabContents*> modified_tabs;
+ bool windows_changed = false;
switch (type.value) {
- case NotificationType::SESSION_SERVICE_SAVED: {
- std::string tag = session_model_associator_->GetCurrentMachineTag();
- DCHECK_EQ(Source<Profile>(source).ptr(), profile_);
- session_model_associator_->UpdateSyncModelDataFromClient();
+ case NotificationType::BROWSER_OPENED: {
+ Browser* browser = Source<Browser>(source).ptr();
+ if (browser->profile() != profile_) {
+ return;
+ }
+
+ windows_changed = true;
+ break;
+ }
+
+ case NotificationType::TAB_PARENTED: {
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ if (controller->profile() != profile_) {
+ return;
+ }
+ windows_changed = true;
+ modified_tabs.push_back(controller->tab_contents());
+ break;
+ }
+
+ case NotificationType::TAB_CLOSED: {
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ if (controller->profile() != profile_) {
+ return;
+ }
+ windows_changed = true;
+ modified_tabs.push_back(controller->tab_contents());
+ break;
+ }
+
+ case NotificationType::NAV_LIST_PRUNED: {
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ if (controller->profile() != profile_) {
+ return;
+ }
+ modified_tabs.push_back(controller->tab_contents());
+ break;
+ }
+
+ case NotificationType::NAV_ENTRY_CHANGED: {
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ if (controller->profile() != profile_) {
+ return;
+ }
+ modified_tabs.push_back(controller->tab_contents());
+ break;
+ }
+
+ case NotificationType::NAV_ENTRY_COMMITTED: {
+ NavigationController* controller =
+ Source<NavigationController>(source).ptr();
+ if (controller->profile() != profile_) {
+ return;
+ }
+ modified_tabs.push_back(controller->tab_contents());
+ break;
+ }
+
+ case NotificationType::TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
+ TabContents* tab_contents = Source<TabContents>(source).ptr();
+ DCHECK(tab_contents);
+ if (tab_contents->profile() != profile_) {
+ return;
+ }
+ if (tab_contents->extension_app()) {
+ modified_tabs.push_back(tab_contents);
+ }
break;
}
default:
- LOG(DFATAL) << "Received unexpected notification of type "
+ LOG(ERROR) << "Received unexpected notification of type "
<< type.value;
break;
}
+
+ // Associate windows first to ensure tabs have homes.
+ if (windows_changed)
+ session_model_associator_->ReassociateWindows(false);
+ if (!modified_tabs.empty())
+ session_model_associator_->ReassociateTabs(modified_tabs);
}
void SessionChangeProcessor::ApplyChangesFromSyncModel(
@@ -65,10 +143,50 @@ void SessionChangeProcessor::ApplyChangesFromSyncModel(
StopObserving();
- // This currently ignores the tracked changes and rebuilds the sessions from
- // all the session sync nodes.
- // TODO(zea): Make use of |changes| to adjust only modified sessions.
- session_model_associator_->UpdateFromSyncModel(trans);
+ sync_api::ReadNode root(trans);
+ if (!root.InitByTagLookup(kSessionsTag)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Sessions root node lookup failed.");
+ return;
+ }
+
+ for (int i = 0; i < change_count; ++i) {
+ const sync_api::SyncManager::ChangeRecord& change = changes[i];
+ sync_api::SyncManager::ChangeRecord::Action action(change.action);
+ if (sync_api::SyncManager::ChangeRecord::ACTION_DELETE == action) {
+ // Deletions should only be for a foreign client itself, and hence affect
+ // the header node, never a tab node.
+ sync_api::ReadNode node(trans);
+ if (!node.InitByIdLookup(change.id)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Session node lookup failed.");
+ return;
+ }
+ DCHECK_EQ(node.GetModelType(), syncable::SESSIONS);
+ const sync_pb::SessionSpecifics& specifics = node.GetSessionSpecifics();
+ session_model_associator_->DisassociateForeignSession(
+ specifics.session_tag());
+ continue;
+ }
+
+ // Handle an update or add.
+ sync_api::ReadNode sync_node(trans);
+ if (!sync_node.InitByIdLookup(change.id)) {
+ error_handler()->OnUnrecoverableError(FROM_HERE,
+ "Session node lookup failed.");
+ return;
+ }
+
+ // Check that the changed node is a child of the session folder.
+ DCHECK(root.GetId() == sync_node.GetParentId());
+ DCHECK(syncable::SESSIONS == sync_node.GetModelType());
+
+ const sync_pb::SessionSpecifics& specifics(
+ sync_node.GetSessionSpecifics());
+ const int64 mtime = sync_node.GetModificationTime();
+ // Model associator handles foreign session update and add the same.
+ session_model_associator_->AssociateForeignSpecifics(specifics, mtime);
+ }
// Notify foreign session handlers that there are new sessions.
NotificationService::current()->Notify(
@@ -96,9 +214,21 @@ void SessionChangeProcessor::StopImpl() {
void SessionChangeProcessor::StartObserving() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(profile_);
- notification_registrar_.Add(
- this, NotificationType::SESSION_SERVICE_SAVED,
- Source<Profile>(profile_));
+ notification_registrar_.Add(this, NotificationType::TAB_PARENTED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this, NotificationType::TAB_CLOSED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this, NotificationType::NAV_LIST_PRUNED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this, NotificationType::NAV_ENTRY_CHANGED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this, NotificationType::NAV_ENTRY_COMMITTED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this, NotificationType::BROWSER_OPENED,
+ NotificationService::AllSources());
+ notification_registrar_.Add(this,
+ NotificationType::TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
+ NotificationService::AllSources());
}
void SessionChangeProcessor::StopObserving() {
diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc
index 5ec89be..8c8cb6d 100644
--- a/chrome/browser/sync/glue/session_model_associator.cc
+++ b/chrome/browser/sync/glue/session_model_associator.cc
@@ -4,15 +4,19 @@
#include "chrome/browser/sync/glue/session_model_associator.h"
+#include <algorithm>
#include <utility>
#include "base/logging.h"
-#include "base/string_util.h"
-#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_window.h"
#include "chrome/browser/profiles/profile.h"
-#include "chrome/browser/sessions/session_id.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/syncable/syncable.h"
+#include "chrome/browser/tab_contents/navigation_controller.h"
+#include "chrome/browser/tab_contents/navigation_entry.h"
+#include "chrome/browser/tabs/tab_strip_model.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"
@@ -20,15 +24,18 @@
namespace browser_sync {
namespace {
-
static const char kNoSessionsFolderError[] =
"Server did not create the top-level sessions node. We "
"might be running against an out-of-date server.";
+// The maximum number of navigations in each direction we care to sync.
+static const int max_sync_navigation_count = 6;
} // namespace
-SessionModelAssociator::SessionModelAssociator(
- ProfileSyncService* sync_service) : sync_service_(sync_service) {
+SessionModelAssociator::SessionModelAssociator(ProfileSyncService* sync_service)
+ : tab_pool_(sync_service),
+ local_session_syncid_(sync_api::kInvalidId),
+ sync_service_(sync_service) {
DCHECK(CalledOnValidThread());
DCHECK(sync_service_);
}
@@ -37,80 +44,10 @@ SessionModelAssociator::~SessionModelAssociator() {
DCHECK(CalledOnValidThread());
}
-bool SessionModelAssociator::AssociateModels() {
- DCHECK(CalledOnValidThread());
-
- // Make sure we have a machine tag.
- if (current_machine_tag_.empty())
- InitializeCurrentMachineTag(); // Creates a syncable::BaseTransaction.
-
- {
- // Do an initial update from sync model (in case we just re-enabled and
- // already had data).
- sync_api::ReadTransaction trans(
- sync_service_->backend()->GetUserShareHandle());
- UpdateFromSyncModel(&trans);
- }
-
- // Check if anything has changed on the client side.
- UpdateSyncModelDataFromClient();
- return true;
-}
-
-bool SessionModelAssociator::ChromeModelHasUserCreatedNodes(
- bool* has_nodes) {
- DCHECK(CalledOnValidThread());
- CHECK(has_nodes);
- // This is wrong, but this function is unused, anyway.
- *has_nodes = true;
- return true;
-}
-
-bool SessionModelAssociator::DisassociateModels() {
- specifics_.clear();
-
- // There is no local model stored with which to disassociate, just notify
- // foreign session handlers.
- NotificationService::current()->Notify(
- NotificationType::FOREIGN_SESSION_DISABLED,
- NotificationService::AllSources(),
- NotificationService::NoDetails());
- return true;
-}
-
-const sync_pb::SessionSpecifics* SessionModelAssociator::
- GetChromeNodeFromSyncId(int64 sync_id) {
- sync_api::ReadTransaction trans(
- sync_service_->backend()->GetUserShareHandle());
- sync_api::ReadNode node(&trans);
- if (!node.InitByIdLookup(sync_id))
- return NULL;
- return new sync_pb::SessionSpecifics(node.GetSessionSpecifics());
-}
-
-bool SessionModelAssociator::GetSyncIdForTaggedNode(const std::string* tag,
- int64* sync_id) {
- sync_api::ReadTransaction trans(
- sync_service_->backend()->GetUserShareHandle());
- sync_api::ReadNode node(&trans);
- if (!node.InitByClientTagLookup(syncable::SESSIONS, *tag))
- return false;
- *sync_id = node.GetId();
- return true;
-}
-
-int64 SessionModelAssociator::GetSyncIdFromChromeId(const std::string& id) {
- sync_api::ReadTransaction trans(
- sync_service_->backend()->GetUserShareHandle());
- sync_api::ReadNode node(&trans);
- if (!node.InitByClientTagLookup(syncable::SESSIONS, id))
- return sync_api::kInvalidId;
- return node.GetId();
-}
-
bool SessionModelAssociator::InitSyncNodeFromChromeId(
const std::string& id,
sync_api::BaseNode* sync_node) {
+ NOTREACHED();
return false;
}
@@ -131,138 +68,212 @@ bool SessionModelAssociator::SyncModelHasUserCreatedNodes(bool* has_nodes) {
return true;
}
-std::string SessionModelAssociator::GetCurrentMachineTag() {
- DCHECK(!current_machine_tag_.empty());
- return current_machine_tag_;
+int64 SessionModelAssociator::GetSyncIdFromChromeId(const size_t& id) {
+ DCHECK(CalledOnValidThread());
+ return GetSyncIdFromSessionTag(TabIdToTag(GetCurrentMachineTag(), id));
}
-void SessionModelAssociator::UpdateSyncModelDataFromClient() {
+int64 SessionModelAssociator::GetSyncIdFromSessionTag(const std::string& tag) {
DCHECK(CalledOnValidThread());
- SessionService::SessionCallback* callback =
- NewCallback(this, &SessionModelAssociator::OnGotSession);
- // TODO(jerrica): Stop current race condition, possibly make new method in
- // session service, which only grabs the windows from memory.
- GetSessionService()->GetCurrentSession(&consumer_, callback);
+ sync_api::ReadTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ sync_api::ReadNode node(&trans);
+ if (!node.InitByClientTagLookup(syncable::SESSIONS, tag))
+ return sync_api::kInvalidId;
+ return node.GetId();
}
-
-// TODO(zea): Don't recreate sessions_ vector from scratch each time. This
-// will involve knowing which sessions have been changed (a different data
-// structure will probably be better too).
-bool SessionModelAssociator::UpdateFromSyncModel(
- const sync_api::BaseTransaction* trans) {
+void SessionModelAssociator::ReassociateWindows(bool reload_tabs) {
DCHECK(CalledOnValidThread());
+ sync_pb::SessionSpecifics specifics;
+ specifics.set_session_tag(GetCurrentMachineTag());
+ sync_pb::SessionHeader* header_s = specifics.mutable_header();
+
+ for (BrowserList::const_iterator i = BrowserList::begin();
+ i != BrowserList::end(); ++i) {
+ // Make sure the browser has tabs and a window. Browsers destructor
+ // removes itself from the BrowserList. When a browser is closed the
+ // destructor is not necessarily run immediately. This means its possible
+ // for us to get a handle to a browser that is about to be removed. If
+ // the tab count is 0 or the window is NULL, the browser is about to be
+ // deleted, so we ignore it.
+ if (ShouldSyncWindowType((*i)->type()) && (*i)->tab_count() &&
+ (*i)->window()) {
+ sync_pb::SessionWindow window_s;
+ SessionID::id_type window_id = (*i)->session_id().id();
+ 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());
+ if ((*i)->type() ==
+ Browser::TYPE_NORMAL) {
+ window_s.set_browser_type(
+ sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
+ } else {
+ window_s.set_browser_type(
+ sync_pb::SessionWindow_BrowserType_TYPE_POPUP);
+ }
- // Rebuild specifics_ vector
- specifics_.clear();
- if (!QuerySyncModel(trans, specifics_)) {
- LOG(ERROR) << "SessionModelAssociator failed to updated from sync model";
- return false;
+ // Store the order of tabs.
+ bool found_tabs = false;
+ for (int j = 0; j < (*i)->tab_count(); ++j) {
+ TabContents* tab = (*i)->GetTabContentsAt(j);
+ DCHECK(tab);
+ if (IsValidTab(*tab)) {
+ found_tabs = true;
+ window_s.add_tab(tab->controller().session_id().id());
+ if (reload_tabs) {
+ ReassociateTab(*tab);
+ }
+ }
+ }
+ // Only add a window if it contains valid tabs.
+ if (found_tabs) {
+ sync_pb::SessionWindow* header_window = header_s->add_window();
+ *header_window = window_s;
+ }
+ }
}
- return true;
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ sync_api::WriteNode header_node(&trans);
+ if (!header_node.InitByIdLookup(local_session_syncid_)) {
+ LOG(ERROR) << "Failed to load local session header node.";
+ return;
+ }
+ header_node.SetSessionSpecifics(specifics);
}
-bool SessionModelAssociator::QuerySyncModel(
- const sync_api::BaseTransaction* trans,
- std::vector<const sync_pb::SessionSpecifics*>& specifics) {
- DCHECK(CalledOnValidThread());
- sync_api::ReadNode root(trans);
- if (!root.InitByTagLookup(kSessionsTag)) {
- LOG(ERROR) << kNoSessionsFolderError;
- return false;
+// Static.
+bool SessionModelAssociator::ShouldSyncWindowType(const Browser::Type& type) {
+ switch (type) {
+ case Browser::TYPE_POPUP:
+ return true;
+ case Browser::TYPE_APP:
+ return false;
+ case Browser::TYPE_APP_POPUP:
+ return false;
+ case Browser::TYPE_DEVTOOLS:
+ return false;
+ case Browser::TYPE_APP_PANEL:
+ return false;
+ case Browser::TYPE_NORMAL:
+ default:
+ return true;
}
- sync_api::ReadNode current_machine(trans);
- int64 current_id = (current_machine.InitByClientTagLookup(syncable::SESSIONS,
- GetCurrentMachineTag())) ? current_machine.GetId() : sync_api::kInvalidId;
+}
- // Iterate through the nodes and populate the session model.
- int64 id = root.GetFirstChildId();
- while (id != sync_api::kInvalidId) {
- sync_api::ReadNode sync_node(trans);
- if (!sync_node.InitByIdLookup(id)) {
- LOG(ERROR) << "Failed to fetch sync node for id " << id;
- return false;
- }
- if (id != current_id) {
- specifics.insert(specifics.end(), &sync_node.GetSessionSpecifics());
- }
- id = sync_node.GetSuccessorId();
+void SessionModelAssociator::ReassociateTabs(
+ const std::vector<TabContents*>& tabs) {
+ DCHECK(CalledOnValidThread());
+ for (std::vector<TabContents*>::const_iterator i = tabs.begin();
+ i != tabs.end();
+ ++i) {
+ ReassociateTab(**i);
}
- return true;
}
-bool SessionModelAssociator::GetSessionData(
- std::vector<ForeignSession*>* sessions) {
+void SessionModelAssociator::ReassociateTab(const TabContents& tab) {
DCHECK(CalledOnValidThread());
+ if (!IsValidTab(tab))
+ return;
- // Build vector of sessions from specifics data
- for (std::vector<const sync_pb::SessionSpecifics*>::const_iterator i =
- specifics_.begin(); i != specifics_.end(); ++i) {
- // Only include sessions with open windows.
- if ((*i)->session_window_size() > 0)
- AppendForeignSessionFromSpecifics(*i, sessions);
+ int64 sync_id;
+ SessionID::id_type id = tab.controller().session_id().id();
+ if (tab.is_being_destroyed()) {
+ // This tab is closing.
+ TabLinksMap::iterator tab_iter = tab_map_.find(id);
+ if (tab_iter == tab_map_.end()) {
+ // We aren't tracking this tab (for example, sync setting page).
+ return;
+ }
+ tab_pool_.FreeTabNode(tab_iter->second.sync_id());
+ tab_map_.erase(tab_iter);
+ return;
}
- return true;
-}
-
-void SessionModelAssociator::AppendForeignSessionFromSpecifics(
- const sync_pb::SessionSpecifics* specifics,
- std::vector<ForeignSession*>* session) {
- ForeignSession* foreign_session = new ForeignSession();
- foreign_session->foreign_session_tag = specifics->session_tag();
- session->insert(session->end(), foreign_session);
- for (int i = 0; i < specifics->session_window_size(); i++) {
- const sync_pb::SessionWindow* window = &specifics->session_window(i);
- SessionWindow* session_window = new SessionWindow();
- PopulateSessionWindowFromSpecifics(session_window, window);
- foreign_session->windows.insert(
- foreign_session->windows.end(), session_window);
+ TabLinksMap::const_iterator tablink = tab_map_.find(id);
+ if (tablink == tab_map_.end()) {
+ // This is a new tab, get a sync node for it.
+ sync_id = tab_pool_.GetFreeTabNode();
+ } else {
+ // This tab is already associated with a sync node, reuse it.
+ sync_id = tablink->second.sync_id();
}
+ Associate(&tab, sync_id);
}
-// Fills the given vector with foreign session windows to restore.
-void SessionModelAssociator::AppendForeignSessionWithID(int64 id,
- std::vector<ForeignSession*>* session, sync_api::BaseTransaction* trans) {
- if (id == sync_api::kInvalidId)
- return;
- sync_api::ReadNode node(trans);
- if (!node.InitByIdLookup(id))
- return;
- const sync_pb::SessionSpecifics* ref = &node.GetSessionSpecifics();
- AppendForeignSessionFromSpecifics(ref, session);
-}
+void SessionModelAssociator::Associate(const TabContents* tab, int64 sync_id) {
+ DCHECK(CalledOnValidThread());
+ SessionID::id_type session_id = tab->controller().session_id().id();
-SessionService* SessionModelAssociator::GetSessionService() {
- DCHECK(sync_service_);
- Profile* profile = sync_service_->profile();
- DCHECK(profile);
- SessionService* sessions_service = profile->GetSessionService();
- DCHECK(sessions_service);
- return sessions_service;
+ TabLinks t(sync_id, tab);
+ tab_map_[session_id] = t;
+
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ WriteTabContentsToSyncModel(*tab, sync_id, &trans);
}
-void SessionModelAssociator::InitializeCurrentMachineTag() {
- sync_api::WriteTransaction trans(sync_service_->backend()->
- GetUserShareHandle());
- syncable::Directory* dir =
- trans.GetWrappedWriteTrans()->directory();
+bool SessionModelAssociator::WriteTabContentsToSyncModel(
+ const TabContents& tab,
+ int64 sync_id,
+ sync_api::WriteTransaction* trans) {
+ DCHECK(CalledOnValidThread());
+ sync_api::WriteNode tab_node(trans);
+ if (!tab_node.InitByIdLookup(sync_id)) {
+ LOG(ERROR) << "Failed to look up tab node " << sync_id;
+ return false;
+ }
- // TODO(zea): We need a better way of creating a machine tag. The directory
- // kernel's cache_guid changes every time syncing is turned on and off. This
- // will result in session's associated with stale machine tags persisting on
- // the server since that tag will not be reused. Eventually this should
- // become some string identifiable to the user. (Home, Work, Laptop, etc.)
- // See issue 59672
- current_machine_tag_ = "session_sync";
- current_machine_tag_.append(dir->cache_guid());
- VLOG(1) << "Creating machine tag: " << current_machine_tag_;
+ sync_pb::SessionSpecifics session_s;
+ session_s.set_session_tag(GetCurrentMachineTag());
+ sync_pb::SessionTab* tab_s = session_s.mutable_tab();
+
+ SessionID::id_type tab_id = tab.controller().session_id().id();
+ tab_s->set_tab_id(tab_id);
+ tab_s->set_window_id(tab.controller().window_id().id());
+ const int current_index = tab.controller().GetCurrentEntryIndex();
+ const int min_index = std::max(0,
+ current_index - max_sync_navigation_count);
+ const int max_index = std::min(current_index + max_sync_navigation_count,
+ tab.controller().entry_count());
+ const int pending_index = tab.controller().pending_entry_index();
+ Browser* browser = BrowserList::FindBrowserWithID(
+ tab.controller().window_id().id());
+ DCHECK(browser);
+ 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());
+ for (int i = min_index; i < max_index; ++i) {
+ const NavigationEntry* entry = (i == pending_index) ?
+ tab.controller().pending_entry() : tab.controller().GetEntryAtIndex(i);
+ DCHECK(entry);
+ if (entry->virtual_url().is_valid()) {
+ if (i == max_index - 1) {
+ VLOG(1) << "Associating tab " << tab_id << " with sync id " << sync_id
+ << " and url " << entry->virtual_url().possibly_invalid_spec();
+ }
+ TabNavigation tab_nav;
+ tab_nav.SetFromNavigationEntry(*entry);
+ sync_pb::TabNavigation* nav_s = tab_s->add_navigation();
+ PopulateSessionSpecificsNavigation(&tab_nav, nav_s);
+ }
+ }
+ tab_s->set_current_navigation_index(current_index);
+
+ tab_node.SetSessionSpecifics(session_s);
+ return true;
}
-// See PopulateSessionSpecificsTab for use. May add functionality that includes
-// the state later.
+// Static
+// TODO(zea): perhaps sync state (scroll position, form entries, etc.) as well?
+// See http://crbug.com/67068.
void SessionModelAssociator::PopulateSessionSpecificsNavigation(
- const TabNavigation* navigation, sync_pb::TabNavigation* tab_navigation) {
+ const TabNavigation* navigation,
+ sync_pb::TabNavigation* tab_navigation) {
tab_navigation->set_index(navigation->index());
tab_navigation->set_virtual_url(navigation->virtual_url().spec());
tab_navigation->set_referrer(navigation->referrer().spec());
@@ -334,125 +345,280 @@ void SessionModelAssociator::PopulateSessionSpecificsNavigation(
}
}
-// See PopulateSessionSpecificsWindow for use.
-void SessionModelAssociator::PopulateSessionSpecificsTab(
- const SessionTab* tab, sync_pb::SessionTab* session_tab) {
- session_tab->set_tab_visual_index(tab->tab_visual_index);
- session_tab->set_current_navigation_index(
- tab->current_navigation_index);
- session_tab->set_pinned(tab->pinned);
- session_tab->set_extension_app_id(tab->extension_app_id);
- for (std::vector<TabNavigation>::const_iterator i3 =
- tab->navigations.begin(); i3 != tab->navigations.end(); ++i3) {
- const TabNavigation navigation = *i3;
- sync_pb::TabNavigation* tab_navigation =
- session_tab->add_navigation();
- PopulateSessionSpecificsNavigation(&navigation, tab_navigation);
- }
+void SessionModelAssociator::Disassociate(int64 sync_id) {
+ DCHECK(CalledOnValidThread());
+ NOTIMPLEMENTED();
+ // TODO(zea): we will need this once we support deleting foreign sessions.
}
-// Called when populating session specifics to send to the sync model, called
-// when associating models, or updating the sync model.
-void SessionModelAssociator::PopulateSessionSpecificsWindow(
- const SessionWindow* window, sync_pb::SessionWindow* session_window) {
- session_window->set_selected_tab_index(window->selected_tab_index);
- if (window->type == 1) {
- session_window->set_browser_type(
- sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
- } else {
- session_window->set_browser_type(
- sync_pb::SessionWindow_BrowserType_TYPE_POPUP);
+bool SessionModelAssociator::AssociateModels() {
+ DCHECK(CalledOnValidThread());
+
+ // Ensure that we disassociated properly, otherwise memory might leak.
+ DCHECK(foreign_session_tracker_.empty());
+ DCHECK_EQ(0U, tab_pool_.capacity());
+
+ local_session_syncid_ = sync_api::kInvalidId;
+
+ // Read any available foreign sessions and load any session data we may have.
+ // If we don't have any local session data in the db, create a header node.
+ {
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+
+ sync_api::ReadNode root(&trans);
+ if (!root.InitByTagLookup(kSessionsTag)) {
+ LOG(ERROR) << kNoSessionsFolderError;
+ return false;
}
- for (std::vector<SessionTab*>::const_iterator i2 = window->tabs.begin();
- i2 != window->tabs.end(); ++i2) {
- const SessionTab* tab = *i2;
- if (tab->navigations.at(tab->current_navigation_index).virtual_url() ==
- GURL(chrome::kChromeUINewTabURL)) {
- continue;
+
+ // Make sure we have a machine tag.
+ if (current_machine_tag_.empty())
+ InitializeCurrentMachineTag(&trans);
+
+ UpdateAssociationsFromSyncModel(root, &trans);
+
+ if (local_session_syncid_ == sync_api::kInvalidId) {
+ // The sync db didn't have a header node for us, we need to create one.
+ sync_api::WriteNode write_node(&trans);
+ if (!write_node.InitUniqueByCreation(syncable::SESSIONS, root,
+ current_machine_tag_)) {
+ LOG(ERROR) << "Failed to create sessions header sync node.";
+ return false;
}
- sync_pb::SessionTab* session_tab = session_window->add_session_tab();
- PopulateSessionSpecificsTab(tab, session_tab);
+ write_node.SetTitle(UTF8ToWide(current_machine_tag_));
+ local_session_syncid_ = write_node.GetId();
}
+ }
+
+ // Check if anything has changed on the client side.
+ UpdateSyncModelDataFromClient();
+
+ VLOG(1) << "Session models associated.";
+
+ return true;
}
-bool SessionModelAssociator::WindowHasNoTabsToSync(
- const SessionWindow* window) {
- int num_populated = 0;
- for (std::vector<SessionTab*>::const_iterator i = window->tabs.begin();
- i != window->tabs.end(); ++i) {
- const SessionTab* tab = *i;
- if (tab->navigations.at(tab->current_navigation_index).virtual_url() ==
- GURL(chrome::kChromeUINewTabURL)) {
- continue;
+bool SessionModelAssociator::DisassociateModels() {
+ DCHECK(CalledOnValidThread());
+ foreign_session_tracker_.clear();
+ tab_map_.clear();
+ tab_pool_.clear();
+ local_session_syncid_ = sync_api::kInvalidId;
+
+ // There is no local model stored with which to disassociate, just notify
+ // foreign session handlers.
+ NotificationService::current()->Notify(
+ NotificationType::FOREIGN_SESSION_DISABLED,
+ NotificationService::AllSources(),
+ NotificationService::NoDetails());
+ return true;
+}
+
+void SessionModelAssociator::InitializeCurrentMachineTag(
+ sync_api::WriteTransaction* trans) {
+ DCHECK(CalledOnValidThread());
+ syncable::Directory* dir = trans->GetWrappedWriteTrans()->directory();
+
+ // TODO(zea): We need a better way of creating a machine tag. The directory
+ // kernel's cache_guid changes every time syncing is turned on and off. This
+ // will result in session's associated with stale machine tags persisting on
+ // the server since that tag will not be reused. Eventually this should
+ // become some string identifiable to the user. (Home, Work, Laptop, etc.)
+ // See issue at http://crbug.com/59672
+ current_machine_tag_ = "session_sync";
+ current_machine_tag_.append(dir->cache_guid());
+ VLOG(1) << "Creating machine tag: " << current_machine_tag_;
+ tab_pool_.set_machine_tag(current_machine_tag_);
+}
+
+bool SessionModelAssociator::UpdateAssociationsFromSyncModel(
+ const sync_api::ReadNode& root,
+ const sync_api::BaseTransaction* trans) {
+ DCHECK(CalledOnValidThread());
+
+ // Iterate through the nodes and associate any foreign sessions.
+ int64 id = root.GetFirstChildId();
+ while (id != sync_api::kInvalidId) {
+ sync_api::ReadNode sync_node(trans);
+ if (!sync_node.InitByIdLookup(id)) {
+ LOG(ERROR) << "Failed to fetch sync node for id " << id;
+ return false;
}
- num_populated++;
+
+ const sync_pb::SessionSpecifics& specifics =
+ sync_node.GetSessionSpecifics();
+ const int64 modification_time = sync_node.GetModificationTime();
+ if (specifics.session_tag() != GetCurrentMachineTag()) {
+ if (!AssociateForeignSpecifics(specifics, modification_time)) {
+ return false;
+ }
+ } else if (id != local_session_syncid_) {
+ // This is previously stored local session information.
+ if (specifics.has_header()) {
+ DCHECK_EQ(sync_api::kInvalidId, local_session_syncid_);
+
+ // This is our previous header node, reuse it.
+ local_session_syncid_ = id;
+ } else {
+ DCHECK(specifics.has_tab());
+
+ // This is a tab node. We want to track these to reuse them in our free
+ // tab node pool. They will be overwritten eventually, so need to do
+ // anything else.
+ tab_pool_.AddTabNode(id);
+ }
+ }
+
+ id = sync_node.GetSuccessorId();
}
- if (num_populated == 0)
- return true;
- return false;
+
+ // After updating from sync model all tabid's should be free.
+ DCHECK(tab_pool_.full());
+
+ return true;
}
-void SessionModelAssociator::OnGotSession(int handle,
- std::vector<SessionWindow*>* windows) {
- sync_pb::SessionSpecifics specifics;
- // Set the tag, then iterate through the vector of windows, extracting the
- // window data, along with the tabs data and tab navigation data to populate
- // the session specifics.
- specifics.set_session_tag(GetCurrentMachineTag());
- FillSpecificsFromSessions(windows, &specifics);
- bool has_nodes = false;
- if (!SyncModelHasUserCreatedNodes(&has_nodes))
- return;
- if (specifics.session_window_size() == 0 && has_nodes)
- return;
- sync_api::WriteTransaction trans(
- sync_service_->backend()->GetUserShareHandle());
- sync_api::ReadNode root(&trans);
- if (!root.InitByTagLookup(kSessionsTag)) {
- LOG(ERROR) << kNoSessionsFolderError;
- return;
+bool SessionModelAssociator::AssociateForeignSpecifics(
+ const sync_pb::SessionSpecifics& specifics,
+ const int64 modification_time) {
+ DCHECK(CalledOnValidThread());
+ std::string foreign_session_tag = specifics.session_tag();
+ DCHECK(foreign_session_tag != GetCurrentMachineTag() ||
+ sync_service_->cros_user() == "test user"); // For tests.
+
+ if (specifics.has_header()) {
+ // Read in the header data for this foreign session.
+ // Header data contains window information and ordered tab id's for each
+ // window.
+
+ // Load (or create) the ForeignSession object for this client.
+ ForeignSession* foreign_session =
+ foreign_session_tracker_.GetForeignSession(foreign_session_tag);
+
+ const sync_pb::SessionHeader& header = specifics.header();
+ foreign_session->windows.reserve(header.window_size());
+ VLOG(1) << "Associating " << foreign_session_tag << " with " <<
+ header.window_size() << " windows.";
+ size_t i;
+ for (i = 0; i < static_cast<size_t>(header.window_size()); ++i) {
+ if (i >= foreign_session->windows.size()) {
+ // This a new window, create it.
+ foreign_session->windows.push_back(new SessionWindow());
+ }
+ const sync_pb::SessionWindow& window_s = header.window(i);
+ PopulateSessionWindowFromSpecifics(foreign_session_tag,
+ window_s,
+ modification_time,
+ foreign_session->windows[i],
+ &foreign_session_tracker_);
+ }
+ // Remove any remaining windows (in case windows were closed)
+ for (; i < foreign_session->windows.size(); ++i) {
+ delete foreign_session->windows[i];
+ }
+ foreign_session->windows.resize(header.window_size());
+ } else if (specifics.has_tab()) {
+ const sync_pb::SessionTab& tab_s = specifics.tab();
+ SessionID::id_type tab_id = tab_s.tab_id();
+ SessionTab* tab =
+ foreign_session_tracker_.GetSessionTab(foreign_session_tag,
+ tab_id,
+ false);
+ PopulateSessionTabFromSpecifics(tab_s, modification_time, tab);
+ } else {
+ NOTREACHED();
+ return false;
}
- UpdateSyncModel(&specifics, &trans, &root);
+
+ return true;
}
-void SessionModelAssociator::FillSpecificsFromSessions(
- std::vector<SessionWindow*>* windows,
- sync_pb::SessionSpecifics* session) {
- for (std::vector<SessionWindow*>::const_iterator i = windows->begin();
- i != windows->end(); ++i) {
- const SessionWindow* window = *i;
- if (WindowHasNoTabsToSync(window)) {
- continue;
+void SessionModelAssociator::DisassociateForeignSession(
+ const std::string& foreign_session_tag) {
+ DCHECK(CalledOnValidThread());
+ foreign_session_tracker_.DeleteForeignSession(foreign_session_tag);
+}
+
+// Static
+void SessionModelAssociator::PopulateSessionWindowFromSpecifics(
+ std::string foreign_session_tag,
+ const sync_pb::SessionWindow& specifics,
+ int64 mtime,
+ SessionWindow* session_window,
+ ForeignSessionTracker* tracker) {
+ if (specifics.has_window_id())
+ session_window->window_id.set_id(specifics.window_id());
+ if (specifics.has_selected_tab_index())
+ session_window->selected_tab_index = specifics.selected_tab_index();
+ if (specifics.has_browser_type()) {
+ if (specifics.browser_type() ==
+ sync_pb::SessionWindow_BrowserType_TYPE_NORMAL) {
+ session_window->type = 1;
+ } else {
+ session_window->type = 2;
}
- sync_pb::SessionWindow* session_window = session->add_session_window();
- PopulateSessionSpecificsWindow(window, session_window);
+ }
+ session_window->timestamp = base::Time::FromInternalValue(mtime);
+ session_window->tabs.resize(specifics.tab_size());
+ for (int i = 0; i < specifics.tab_size(); i++) {
+ SessionID::id_type tab_id = specifics.tab(i);
+ session_window->tabs[i] =
+ tracker->GetSessionTab(foreign_session_tag, tab_id, true);
}
}
+// Static
+void SessionModelAssociator::PopulateSessionTabFromSpecifics(
+ const sync_pb::SessionTab& specifics,
+ const int64 mtime,
+ SessionTab* tab) {
+ if (specifics.has_tab_id())
+ tab->tab_id.set_id(specifics.tab_id());
+ if (specifics.has_window_id())
+ tab->window_id.set_id(specifics.window_id());
+ if (specifics.has_tab_visual_index())
+ tab->tab_visual_index = specifics.tab_visual_index();
+ if (specifics.has_current_navigation_index())
+ tab->current_navigation_index = specifics.current_navigation_index();
+ if (specifics.has_pinned())
+ tab->pinned = specifics.pinned();
+ if (specifics.has_extension_app_id())
+ tab->extension_app_id = specifics.extension_app_id();
+ tab->timestamp = base::Time::FromInternalValue(mtime);
+ tab->navigations.clear(); // In case we are reusing a previous SessionTab.
+ for (int i = 0; i < specifics.navigation_size(); i++) {
+ AppendSessionTabNavigation(specifics.navigation(i), &tab->navigations);
+ }
+}
+
+// Static
void SessionModelAssociator::AppendSessionTabNavigation(
- std::vector<TabNavigation>* navigations,
- const sync_pb::TabNavigation* navigation) {
+ const sync_pb::TabNavigation& specifics,
+ std::vector<TabNavigation>* navigations) {
int index = 0;
GURL virtual_url;
GURL referrer;
string16 title;
std::string state;
PageTransition::Type transition(PageTransition::LINK);
- if (navigation->has_index())
- index = navigation->index();
- if (navigation->has_virtual_url()) {
- GURL gurl(navigation->virtual_url());
+ if (specifics.has_index())
+ index = specifics.index();
+ if (specifics.has_virtual_url()) {
+ GURL gurl(specifics.virtual_url());
virtual_url = gurl;
}
- if (navigation->has_referrer()) {
- GURL gurl(navigation->referrer());
+ if (specifics.has_referrer()) {
+ GURL gurl(specifics.referrer());
referrer = gurl;
}
- if (navigation->has_title())
- title = UTF8ToUTF16(navigation->title());
- if (navigation->has_page_transition() ||
- navigation->has_navigation_qualifier()) {
- switch (navigation->page_transition()) {
+ if (specifics.has_title())
+ title = UTF8ToUTF16(specifics.title());
+ if (specifics.has_state())
+ state = specifics.state();
+ if (specifics.has_page_transition() ||
+ specifics.has_navigation_qualifier()) {
+ switch (specifics.page_transition()) {
case sync_pb::TabNavigation_PageTransition_LINK:
transition = PageTransition::LINK;
break;
@@ -493,7 +659,7 @@ void SessionModelAssociator::AppendSessionTabNavigation(
transition = PageTransition::CHAIN_END;
break;
default:
- switch (navigation->navigation_qualifier()) {
+ switch (specifics.navigation_qualifier()) {
case sync_pb::
TabNavigation_PageTransitionQualifier_CLIENT_REDIRECT:
transition = PageTransition::CLIENT_REDIRECT;
@@ -512,67 +678,291 @@ void SessionModelAssociator::AppendSessionTabNavigation(
navigations->insert(navigations->end(), tab_navigation);
}
-void SessionModelAssociator::PopulateSessionTabFromSpecifics(
- SessionTab* session_tab,
- const sync_pb::SessionTab* tab, SessionID id) {
- session_tab->window_id = id;
- SessionID tabID;
- session_tab->tab_id = tabID;
- if (tab->has_tab_visual_index())
- session_tab->tab_visual_index = tab->tab_visual_index();
- if (tab->has_current_navigation_index()) {
- session_tab->current_navigation_index =
- tab->current_navigation_index();
+void SessionModelAssociator::UpdateSyncModelDataFromClient() {
+ DCHECK(CalledOnValidThread());
+ // TODO(zea): the logic for determining if we want to sync and the loading of
+ // the previous session should go here. We can probably reuse the code for
+ // loading the current session from the old session implementation.
+ // SessionService::SessionCallback* callback =
+ // NewCallback(this, &SessionModelAssociator::OnGotSession);
+ // GetSessionService()->GetCurrentSession(&consumer_, callback);
+
+ // Associate all open windows and their tabs.
+ ReassociateWindows(true);
+}
+
+SessionModelAssociator::TabNodePool::TabNodePool(
+ ProfileSyncService* sync_service)
+ : tab_pool_fp_(-1),
+ sync_service_(sync_service) {
+}
+
+void SessionModelAssociator::TabNodePool::AddTabNode(int64 sync_id) {
+ tab_syncid_pool_.resize(tab_syncid_pool_.size() + 1);
+ tab_syncid_pool_[static_cast<size_t>(++tab_pool_fp_)] = sync_id;
+}
+
+int64 SessionModelAssociator::TabNodePool::GetFreeTabNode() {
+ DCHECK_GT(machine_tag_.length(), 0U);
+ if (tab_pool_fp_ == -1) {
+ // Tab pool has no free nodes, allocate new one.
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ sync_api::ReadNode root(&trans);
+ if (!root.InitByTagLookup(kSessionsTag)) {
+ LOG(ERROR) << kNoSessionsFolderError;
+ return 0;
+ }
+ size_t tab_node_id = tab_syncid_pool_.size();
+ std::string tab_node_tag = TabIdToTag(machine_tag_, tab_node_id);
+ sync_api::WriteNode tab_node(&trans);
+ if (!tab_node.InitUniqueByCreation(syncable::SESSIONS, root,
+ tab_node_tag)) {
+ LOG(ERROR) << "Could not create new node!";
+ return -1;
+ }
+ tab_node.SetTitle(UTF8ToWide(tab_node_tag));
+
+ // Grow the pool by 1 since we created a new node. We don't actually need
+ // to put the node's id in the pool now, since the pool is still empty.
+ // The id will be added when that tab is closed and the node is freed.
+ tab_syncid_pool_.resize(tab_node_id + 1);
+ VLOG(1) << "Adding sync node " << tab_node.GetId() << " to tab syncid pool";
+ return tab_node.GetId();
+ } else {
+ // There are nodes available, grab next free and decrement free pointer.
+ return tab_syncid_pool_[static_cast<size_t>(tab_pool_fp_--)];
}
- if (tab->has_pinned())
- session_tab->pinned = tab->pinned();
- if (tab->has_extension_app_id())
- session_tab->extension_app_id = tab->extension_app_id();
- for (int i3 = 0; i3 < tab->navigation_size(); i3++) {
- const sync_pb::TabNavigation* navigation = &tab->navigation(i3);
- AppendSessionTabNavigation(&session_tab->navigations, navigation);
+}
+
+void SessionModelAssociator::TabNodePool::FreeTabNode(int64 sync_id) {
+ // Pool size should always match # of free tab nodes.
+ DCHECK_LT(tab_pool_fp_, static_cast<int64>(tab_syncid_pool_.size()));
+ tab_syncid_pool_[static_cast<size_t>(++tab_pool_fp_)] = sync_id;
+}
+
+bool SessionModelAssociator::GetAllForeignSessions(
+ std::vector<const ForeignSession*>* sessions) {
+ DCHECK(CalledOnValidThread());
+ return foreign_session_tracker_.LookupAllForeignSessions(sessions);
+}
+
+bool SessionModelAssociator::GetForeignSession(
+ const std::string& tag,
+ std::vector<SessionWindow*>* windows) {
+ DCHECK(CalledOnValidThread());
+ return foreign_session_tracker_.LookupSessionWindows(tag, windows);
+}
+
+bool SessionModelAssociator::GetForeignTab(
+ const std::string& tag,
+ const SessionID::id_type tab_id,
+ const SessionTab** tab) {
+ DCHECK(CalledOnValidThread());
+ return foreign_session_tracker_.LookupSessionTab(tag, tab_id, tab);
+}
+
+// Static
+bool SessionModelAssociator::SessionWindowHasNoTabsToSync(
+ const SessionWindow& window) {
+ int num_populated = 0;
+ for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin();
+ i != window.tabs.end(); ++i) {
+ const SessionTab* tab = *i;
+ if (IsValidSessionTab(*tab))
+ num_populated++;
}
+ if (num_populated == 0)
+ return true;
+ return false;
}
-void SessionModelAssociator::PopulateSessionWindowFromSpecifics(
- SessionWindow* session_window, const sync_pb::SessionWindow* window) {
- SessionID id;
- session_window->window_id = id;
- if (window->has_selected_tab_index())
- session_window->selected_tab_index = window->selected_tab_index();
- if (window->has_browser_type()) {
- if (window->browser_type() ==
- sync_pb::SessionWindow_BrowserType_TYPE_NORMAL) {
- session_window->type = 1;
- } else {
- session_window->type = 2;
+// Valid local tab?
+bool SessionModelAssociator::IsValidTab(const TabContents& tab) {
+ DCHECK(CalledOnValidThread());
+ if ((tab.profile() == sync_service_->profile() ||
+ sync_service_->profile() == NULL)) {
+ const NavigationEntry* entry = tab.controller().GetActiveEntry();
+ if (!entry)
+ return false;
+ if (entry->virtual_url().is_valid() &&
+ (entry->virtual_url() != GURL(chrome::kChromeUINewTabURL) ||
+ tab.controller().entry_count() > 1)) {
+ return true;
}
}
- for (int i = 0; i < window->session_tab_size(); i++) {
- const sync_pb::SessionTab& tab = window->session_tab(i);
- SessionTab* session_tab = new SessionTab();
- PopulateSessionTabFromSpecifics(session_tab, &tab, id);
- session_window->tabs.insert(session_window->tabs.end(), session_tab);
+ return false;
+}
+
+// Static
+bool SessionModelAssociator::IsValidSessionTab(const SessionTab& tab) {
+ if (tab.navigations.empty())
+ return false;
+ int selected_index = tab.current_navigation_index;
+ selected_index = std::max(
+ 0,
+ std::min(selected_index,
+ static_cast<int>(tab.navigations.size() - 1)));
+ if (selected_index == 0 &&
+ tab.navigations.size() == 1 &&
+ tab.navigations.at(selected_index).virtual_url() ==
+ GURL(chrome::kChromeUINewTabURL)) {
+ // This is a new tab with no further history, skip.
+ return false;
+ }
+ return true;
+}
+
+// ==========================================================================
+// The following methods are not currently used but will likely become useful
+// if we choose to sync the previous browser session.
+
+SessionService* SessionModelAssociator::GetSessionService() {
+ DCHECK(CalledOnValidThread());
+ DCHECK(sync_service_);
+ Profile* profile = sync_service_->profile();
+ DCHECK(profile);
+ SessionService* sessions_service = profile->GetSessionService();
+ DCHECK(sessions_service);
+ return sessions_service;
+}
+
+void SessionModelAssociator::OnGotSession(
+ int handle,
+ std::vector<SessionWindow*>* windows) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(local_session_syncid_);
+
+ sync_pb::SessionSpecifics specifics;
+ specifics.set_session_tag(GetCurrentMachineTag());
+ sync_pb::SessionHeader* header_s = specifics.mutable_header();
+ PopulateSessionSpecificsHeader(*windows, header_s);
+
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ sync_api::ReadNode root(&trans);
+ if (!root.InitByTagLookup(kSessionsTag)) {
+ LOG(ERROR) << kNoSessionsFolderError;
+ return;
+ }
+
+ sync_api::WriteNode header_node(&trans);
+ if (!header_node.InitByIdLookup(local_session_syncid_)) {
+ LOG(ERROR) << "Failed to load local session header node.";
+ return;
+ }
+
+ header_node.SetSessionSpecifics(specifics);
+}
+
+void SessionModelAssociator::PopulateSessionSpecificsHeader(
+ const std::vector<SessionWindow*>& windows,
+ sync_pb::SessionHeader* header_s) {
+ DCHECK(CalledOnValidThread());
+
+ // Iterate through the vector of windows, extracting the window data, along
+ // with the tab data to populate the session specifics.
+ for (size_t i = 0; i < windows.size(); ++i) {
+ if (SessionWindowHasNoTabsToSync(*(windows[i])))
+ continue;
+ sync_pb::SessionWindow* window_s = header_s->add_window();
+ PopulateSessionSpecificsWindow(*(windows[i]), window_s);
+ if (!SyncLocalWindowToSyncModel(*(windows[i])))
+ return;
+ }
+}
+
+// Called when populating session specifics to send to the sync model, called
+// when associating models, or updating the sync model.
+void SessionModelAssociator::PopulateSessionSpecificsWindow(
+ const SessionWindow& window,
+ sync_pb::SessionWindow* session_window) {
+ DCHECK(CalledOnValidThread());
+ session_window->set_window_id(window.window_id.id());
+ session_window->set_selected_tab_index(window.selected_tab_index);
+ if (window.type == Browser::TYPE_NORMAL) {
+ session_window->set_browser_type(
+ sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
+ } else if (window.type == Browser::TYPE_POPUP) {
+ session_window->set_browser_type(
+ sync_pb::SessionWindow_BrowserType_TYPE_POPUP);
+ } else {
+ // ignore
+ LOG(WARNING) << "Session Sync unable to handle windows of type" <<
+ window.type;
+ return;
+ }
+ for (std::vector<SessionTab*>::const_iterator i = window.tabs.begin();
+ i != window.tabs.end(); ++i) {
+ const SessionTab* tab = *i;
+ if (!IsValidSessionTab(*tab))
+ continue;
+ session_window->add_tab(tab->tab_id.id());
}
}
-bool SessionModelAssociator::UpdateSyncModel(
- sync_pb::SessionSpecifics* session_data,
- sync_api::WriteTransaction* trans,
- const sync_api::ReadNode* root) {
- const std::string id = session_data->session_tag();
- sync_api::WriteNode write_node(trans);
- if (!write_node.InitByClientTagLookup(syncable::SESSIONS, id)) {
- sync_api::WriteNode create_node(trans);
- if (!create_node.InitUniqueByCreation(syncable::SESSIONS, *root, id)) {
- LOG(ERROR) << "Could not create node for session " << id;
+bool SessionModelAssociator::SyncLocalWindowToSyncModel(
+ const SessionWindow& window) {
+ DCHECK(CalledOnValidThread());
+ DCHECK(tab_map_.empty());
+ for (size_t i = 0; i < window.tabs.size(); ++i) {
+ SessionTab* tab = window.tabs[i];
+ int64 id = tab_pool_.GetFreeTabNode();
+ if (id == -1) {
+ LOG(ERROR) << "Failed to find/generate free sync node for tab.";
return false;
}
- create_node.SetSessionSpecifics(*session_data);
- return true;
+
+ sync_api::WriteTransaction trans(
+ sync_service_->backend()->GetUserShareHandle());
+ if (!WriteSessionTabToSyncModel(*tab, id, &trans)) {
+ return false;
+ }
+
+ TabLinks t(id, tab);
+ tab_map_[tab->tab_id.id()] = t;
}
- write_node.SetSessionSpecifics(*session_data);
return true;
}
+bool SessionModelAssociator::WriteSessionTabToSyncModel(
+ const SessionTab& tab,
+ const int64 sync_id,
+ sync_api::WriteTransaction* trans) {
+ DCHECK(CalledOnValidThread());
+ sync_api::WriteNode tab_node(trans);
+ if (!tab_node.InitByIdLookup(sync_id)) {
+ LOG(ERROR) << "Failed to look up tab node " << sync_id;
+ return false;
+ }
+
+ sync_pb::SessionSpecifics specifics;
+ specifics.set_session_tag(GetCurrentMachineTag());
+ sync_pb::SessionTab* tab_s = specifics.mutable_tab();
+ PopulateSessionSpecificsTab(tab, tab_s);
+ tab_node.SetSessionSpecifics(specifics);
+ return true;
+}
+
+// See PopulateSessionSpecificsWindow for use.
+void SessionModelAssociator::PopulateSessionSpecificsTab(
+ const SessionTab& tab,
+ sync_pb::SessionTab* session_tab) {
+ DCHECK(CalledOnValidThread());
+ session_tab->set_tab_id(tab.tab_id.id());
+ session_tab->set_window_id(tab.window_id.id());
+ session_tab->set_tab_visual_index(tab.tab_visual_index);
+ session_tab->set_current_navigation_index(
+ tab.current_navigation_index);
+ session_tab->set_pinned(tab.pinned);
+ session_tab->set_extension_app_id(tab.extension_app_id);
+ for (std::vector<TabNavigation>::const_iterator i =
+ tab.navigations.begin(); i != tab.navigations.end(); ++i) {
+ const TabNavigation navigation = *i;
+ sync_pb::TabNavigation* tab_navigation =
+ session_tab->add_navigation();
+ PopulateSessionSpecificsNavigation(&navigation, tab_navigation);
+ }
+}
+
} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/session_model_associator.h b/chrome/browser/sync/glue/session_model_associator.h
index 502af18..cff376f 100644
--- a/chrome/browser/sync/glue/session_model_associator.h
+++ b/chrome/browser/sync/glue/session_model_associator.h
@@ -6,6 +6,7 @@
#define CHROME_BROWSER_SYNC_GLUE_SESSION_MODEL_ASSOCIATOR_H_
#pragma once
+#include <map>
#include <string>
#include <vector>
@@ -13,12 +14,19 @@
#include "base/gtest_prod_util.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"
#include "chrome/browser/sync/engine/syncapi.h"
+#include "chrome/browser/sync/glue/foreign_session_tracker.h"
#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/tab_contents/tab_contents.h"
#include "chrome/common/notification_registrar.h"
class Profile;
@@ -40,59 +48,76 @@ static const char kSessionsTag[] = "google_chrome_sessions";
// Contains all logic for associating the Chrome sessions model and
// the sync sessions model.
-// In the case of sessions, our local model is nothing but a buffer (specifics_)
-// that gets overwritten everytime there is an update. From it, we build a new
-// foreign session windows list each time |GetSessionData| is called by the
-// ForeignSessionHandler.
-class SessionModelAssociator : public PerDataTypeAssociatorInterface<
- sync_pb::SessionSpecifics, std::string>, public NonThreadSafe {
+class SessionModelAssociator
+ : public PerDataTypeAssociatorInterface<TabContents, size_t>,
+ public base::NonThreadSafe {
public:
-
// Does not take ownership of sync_service.
explicit SessionModelAssociator(ProfileSyncService* sync_service);
virtual ~SessionModelAssociator();
+ // The has_nodes out parameter is set to true if the sync model has
+ // nodes other than the permanent tagged nodes. The method may
+ // return false if an error occurred.
+ virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes);
+
// AssociatorInterface and PerDataTypeAssociator Interface implementation.
virtual void AbortAssociation() {
- // No implementation needed, this associator runs on the main
- // thread.
- }
-
- // Dummy method, we do everything all-at-once in UpdateFromSyncModel.
- virtual void Associate(const sync_pb::SessionSpecifics* specifics,
- int64 sync_id) {
+ // No implementation needed, this associator runs on the main thread.
}
- // Updates the sync model with the local client data. (calls
- // UpdateFromSyncModel)
- virtual bool AssociateModels();
+ // 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.
+ virtual int64 GetSyncIdFromChromeId(const size_t& id);
- // The has_nodes out parameter is set to true if the chrome model
- // has user-created nodes. The method may return false if an error
- // occurred.
- virtual bool ChromeModelHasUserCreatedNodes(bool* has_nodes);
+ // Returns sync id for the given session tag.
+ // Returns sync_api::kInvalidId if the sync node is not found for the given
+ // tag
+ virtual int64 GetSyncIdFromSessionTag(const std::string& tag);
- // Dummy method, we do everything all-at-once in UpdateFromSyncModel.
- virtual void Disassociate(int64 sync_id) {
+ // Not used.
+ virtual const TabContents* GetChromeNodeFromSyncId(int64 sync_id) {
+ NOTREACHED();
+ return NULL;
}
- // Clear specifics_ buffer and notify foreign session handlers.
- virtual bool DisassociateModels();
-
- // Returns the chrome session specifics for the given sync id.
- // Returns NULL if no specifics are found for the given sync id.
- virtual const sync_pb::SessionSpecifics* GetChromeNodeFromSyncId(
- int64 sync_id);
-
- // Returns whether a node with the given permanent tag was found and update
- // |sync_id| with that node's id.
- virtual bool GetSyncIdForTaggedNode(const std::string* tag, int64* sync_id);
-
- // 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.
- virtual int64 GetSyncIdFromChromeId(const std::string& id);
+ // Not used.
+ bool InitSyncNodeFromChromeId(const size_t& id,
+ sync_api::BaseNode* sync_node) {
+ NOTREACHED();
+ return false;
+ }
+ // Resync local window information. Updates the local sessions header node
+ // with the status of open windows and the order of tabs they contain. Should
+ // only be called for changes that affect a window, not a change within a
+ // single tab.
+ //
+ // If |reload_tabs| is true, will also resync all tabs (same as calling
+ // ReassociateTabs with a vector of all tabs).
+ void ReassociateWindows(bool reload_tabs);
+
+ // Loads and reassociates the local tabs referenced in |tabs|.
+ void ReassociateTabs(const std::vector<TabContents*>& tabs);
+
+ // Reassociates a single tab with the sync model. Will check if the tab
+ // already is associated with a sync node and allocate one if necessary.
+ void ReassociateTab(const TabContents& tab);
+
+ // Associate a local tab and it's sync node. Will overwrite the contents of
+ // the sync node with new specifics built from the tab.
+ virtual void Associate(const TabContents* tab, int64 sync_id);
+
+ // Looks up the specified sync node, and marks that tab as closed, then marks
+ // the node as free and deletes association.
+ virtual void Disassociate(int64 sync_id);
+
+ // Load any foreign session info stored in sync db and update the sync db
+ // with local client data. Processes/reuses any sync nodes owned by this
+ // client and creates any further sync nodes needed to store local header and
+ // tab info.
+ virtual bool AssociateModels();
// Initializes the given sync node from the given chrome node id.
// Returns false if no sync node was found for the given chrome node id or
@@ -100,46 +125,53 @@ class SessionModelAssociator : public PerDataTypeAssociatorInterface<
virtual bool InitSyncNodeFromChromeId(const std::string& id,
sync_api::BaseNode* sync_node);
- // The has_nodes out parameter is set to true if the sync model has
- // nodes other than the permanent tagged nodes. The method may
- // return false if an error occurred.
- virtual bool SyncModelHasUserCreatedNodes(bool* has_nodes);
+ // Clear local sync data buffers. Does not delete sync nodes to avoid
+ // tombstones. TODO(zea): way to eventually delete orphaned nodes.
+ virtual bool DisassociateModels();
// Returns the tag used to uniquely identify this machine's session in the
// sync model.
- std::string GetCurrentMachineTag();
+ inline const std::string& GetCurrentMachineTag() {
+ DCHECK(!current_machine_tag_.empty());
+ return current_machine_tag_;
+ }
- // Updates the server data based upon the current client session. If no node
- // corresponding to this machine exists in the sync model, one is created.
- void UpdateSyncModelDataFromClient();
+ // Load and associate window and tab data for a foreign session
+ bool AssociateForeignSpecifics(const sync_pb::SessionSpecifics& specifics,
+ int64 modification_time);
- // Pulls the current sync model from the sync database and returns true upon
- // update of the client model. Called by SessionChangeProcessor.
- // Note that the specifics_ vector is reset and built from scratch each time.
- bool UpdateFromSyncModel(const sync_api::BaseTransaction* trans);
+ // Removes a foreign session from our internal bookkeeping.
+ void DisassociateForeignSession(const std::string& foreign_session_tag);
+
+ // Builds a list of all foreign sessions.
+ // Caller does NOT own ForeignSession objects.
+ bool GetAllForeignSessions(std::vector<const ForeignSession*>* sessions);
- // Helper for UpdateFromSyncModel. Appends sync data to a vector of specifics.
- bool QuerySyncModel(const sync_api::BaseTransaction* trans,
- std::vector<const sync_pb::SessionSpecifics*>& specifics);
+ // Loads all windows for foreign session with session tag |tag|.
+ // Caller does NOT own ForeignSession objects.
+ bool GetForeignSession(const std::string& tag,
+ std::vector<SessionWindow*>* windows);
- // Builds sessions from buffered specifics data
- bool GetSessionData(std::vector<ForeignSession*>* sessions);
+ // Looks up the foreign tab identified by |tab_id| and belonging to foreign
+ // session |tag|.
+ // Caller does NOT own the SessionTab object.
+ bool GetForeignTab(const std::string& tag,
+ const SessionID::id_type tab_id,
+ const SessionTab** tab);
- // Helper method to generate session specifics from session windows.
- void FillSpecificsFromSessions(std::vector<SessionWindow*>* windows,
- sync_pb::SessionSpecifics* session);
+ // Specifies whether the window has tabs to sync. The new tab page does not
+ // count. If no tabs to sync, it returns true, otherwise false;
+ static bool SessionWindowHasNoTabsToSync(const SessionWindow& window);
- // Helper method for converting session specifics to windows.
- void AppendForeignSessionFromSpecifics(
- const sync_pb::SessionSpecifics* specifics,
- std::vector<ForeignSession*>* session);
+ // Control which local tabs we're interested in syncing.
+ // Ensures the profile matches sync's profile and that the tab has at least
+ // one navigation entry and is not an empty tab.
+ bool IsValidTab(const TabContents& tab);
- // Fills the given empty vector with foreign session windows to restore.
- // If the vector is returned empty, then the session data could not be
- // converted back into windows.
- void AppendForeignSessionWithID(int64 id,
- std::vector<ForeignSession*>* session,
- sync_api::BaseTransaction* trans);
+ // Control which foreign tabs we're interested in displaying.
+ // Checks that the tab has navigations and is not a new tab.
+ // Note: a new tab page with back/forward history is valid.
+ static bool IsValidSessionTab(const SessionTab& tab);
// Returns the syncable model type.
static syncable::ModelType model_type() { return syncable::SESSIONS; }
@@ -147,60 +179,225 @@ class SessionModelAssociator : public PerDataTypeAssociatorInterface<
private:
FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, WriteSessionToNode);
FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest,
+ WriteFilledSessionToNode);
+ FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest,
WriteForeignSessionToNode);
-
- // Returns the session service from |sync_service_|.
- SessionService* GetSessionService();
+ FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolEmpty);
+ FRIEND_TEST_ALL_PREFIXES(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty);
+ FRIEND_TEST_ALL_PREFIXES(SessionModelAssociatorTest, PopulateSessionWindow);
+ FRIEND_TEST_ALL_PREFIXES(SessionModelAssociatorTest, PopulateSessionTab);
+
+ // Keep all the links to local tab data in one place.
+ class TabLinks {
+ public:
+ // To support usage as second value in maps we need default and copy
+ // constructors.
+ TabLinks()
+ : sync_id_(0),
+ session_tab_(NULL),
+ tab_(NULL) {}
+
+ // We only ever have either a SessionTab (for foreign tabs), or a
+ // TabContents (for local tabs).
+ TabLinks(int64 sync_id, const TabContents* tab)
+ : sync_id_(sync_id),
+ session_tab_(NULL) {
+ tab_ = const_cast<TabContents*>(tab);
+ }
+ TabLinks(int64 sync_id, const SessionTab* session_tab)
+ : sync_id_(sync_id),
+ tab_(NULL) {
+ session_tab_ = const_cast<SessionTab*>(session_tab);
+ }
+
+ inline int64 sync_id() const { return sync_id_; }
+ inline const SessionTab* session_tab() const { return session_tab_; }
+ inline const TabContents* tab() const { return tab_; }
+ private:
+ int64 sync_id_;
+ SessionTab* session_tab_;
+ TabContents* tab_;
+ };
+
+ // A pool for managing free/used tab sync nodes. Performs lazy creation
+ // of sync nodes when necessary.
+ class TabNodePool {
+ public:
+ explicit TabNodePool(ProfileSyncService* sync_service);
+
+ // Add a previously allocated tab sync node to our pool. Increases the size
+ // of tab_syncid_pool_ by one and marks the new tab node as free.
+ // Note: this should only be called when we discover tab sync nodes from
+ // previous sessions, not for freeing tab nodes we created through
+ // GetFreeTabNode (use FreeTabNode below for that).
+ void AddTabNode(int64 sync_id);
+
+ // Returns the sync_id for the next free tab node. If none are available,
+ // creates a new tab node.
+ // Note: We make use of the following "id's"
+ // - a sync_id: an int64 used in |sync_api::InitByIdLookup|
+ // - a tab_id: created by session service, unique to this client
+ // - a tab_node_id: the id for a particular sync tab node. This is used
+ // to generate the sync tab node tag through:
+ // tab_tag = StringPrintf("%s_%ui", local_session_tag, tab_node_id);
+ // tab_node_id and sync_id are both unique to a particular sync node. The
+ // difference is that tab_node_id is controlled by the model associator and
+ // is used when creating a new sync node, which returns the sync_id, created
+ // by the sync db.
+ int64 GetFreeTabNode();
+
+ // Return a tab node to our free pool.
+ // Note: the difference between FreeTabNode and AddTabNode is that
+ // FreeTabNode does not modify the size of |tab_syncid_pool_|, while
+ // AddTabNode increases it by one. In the case of FreeTabNode, the size of
+ // the |tab_syncid_pool_| should always be equal to the amount of tab nodes
+ // associated with this machine.
+ void FreeTabNode(int64 sync_id);
+
+ // Clear tab pool.
+ inline void clear() { tab_syncid_pool_.clear(); }
+
+ // Return the number of tab nodes this client currently has allocated
+ // (including both free and used nodes)
+ inline size_t capacity() const { return tab_syncid_pool_.size(); }
+
+ // Return empty status (all tab nodes are in use).
+ inline bool empty() const { return tab_pool_fp_ == -1; }
+
+ // Return full status (no tab nodes are in use).
+ inline bool full() {
+ return tab_pool_fp_ == static_cast<int64>(tab_syncid_pool_.size())-1;
+ }
+
+ inline void set_machine_tag(const std::string& machine_tag) {
+ machine_tag_ = machine_tag;
+ }
+ private:
+ // Pool of all available syncid's for tab's we have created.
+ std::vector<int64> tab_syncid_pool_;
+
+ // Free pointer for tab pool. Only those node id's, up to and including the
+ // one indexed by the free pointer, are valid and free. The rest of the
+ // |tab_syncid_pool_| is invalid because the nodes are in use.
+ // To get the next free node, use tab_syncid_pool_[tab_pool_fp_--].
+ int64 tab_pool_fp_;
+
+ // The machiine tag associated with this tab pool. Used in the title of new
+ // sync nodes.
+ std::string machine_tag_;
+
+ // Our sync service profile (for making changes to the sync db)
+ ProfileSyncService* sync_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(TabNodePool);
+ };
+
+ // Datatypes for accessing local tab data.
+ typedef std::map<SessionID::id_type, TabLinks> TabLinksMap;
+
+ // Delete all foreign session/window/tab objects allocated dynamically.
+ // This is comprised of ForeignSession*, IDToSessionTabMap*, and any orphaned
+ // SessionTab*'s.
+ void DeleteForeignSessions();
+
+ // Determine if a window is of a type we're interested in syncing.
+ static bool ShouldSyncWindowType(const Browser::Type& type);
+
+ // Build a sync tag from tab_node_id.
+ static inline std::string TabIdToTag(
+ const std::string machine_tag,
+ size_t tab_node_id) {
+ return StringPrintf("%s %lu",
+ machine_tag.c_str(), static_cast<unsigned long>(tab_node_id));
+ }
// Initializes the tag corresponding to this machine.
- // Note: creates a syncable::BaseTransaction.
- void InitializeCurrentMachineTag();
+ void InitializeCurrentMachineTag(sync_api::WriteTransaction* trans);
- // Populates the navigation portion of the session specifics.
- void PopulateSessionSpecificsNavigation(const TabNavigation* navigation,
- sync_pb::TabNavigation* tab_navigation);
+ // Updates the server data based upon the current client session. If no node
+ // corresponding to this machine exists in the sync model, one is created.
+ void UpdateSyncModelDataFromClient();
- // Populates the tab portion of the session specifics.
- void PopulateSessionSpecificsTab(const SessionTab* tab,
- sync_pb::SessionTab* session_tab);
+ // Pulls the current sync model from the sync database and returns true upon
+ // update of the client model. Will associate any foreign sessions as well as
+ // keep track of any local tab nodes, adding them to our free tab node pool.
+ bool UpdateAssociationsFromSyncModel(const sync_api::ReadNode& root,
+ const sync_api::BaseTransaction* trans);
- // Populates the window portion of the session specifics.
- void PopulateSessionSpecificsWindow(const SessionWindow* window,
- sync_pb::SessionWindow* session_window);
+ // Fills a tab sync node with data from a TabContents object.
+ // (from a local navigation event)
+ bool WriteTabContentsToSyncModel(const TabContents& tab,
+ const int64 sync_id,
+ sync_api::WriteTransaction* trans);
+
+ // 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 sync_pb::SessionWindow& window,
+ const int64 mtime,
+ SessionWindow* session_window,
+ ForeignSessionTracker* tracker);
+
+ // Used to populate a session tab from the session specifics tab provided.
+ static void PopulateSessionTabFromSpecifics(const sync_pb::SessionTab& tab,
+ const int64 mtime,
+ SessionTab* session_tab);
- // Specifies whether the window has tabs to sync. The new tab page does not
- // count. If no tabs to sync, it returns true, otherwise false;
- bool WindowHasNoTabsToSync(const SessionWindow* window);
+ // Used to populate a session tab from the session specifics tab provided.
+ static void AppendSessionTabNavigation(
+ const sync_pb::TabNavigation& navigation,
+ std::vector<TabNavigation>* navigations);
+
+ // Populates the navigation portion of the session specifics.
+ static void PopulateSessionSpecificsNavigation(
+ const TabNavigation* navigation,
+ sync_pb::TabNavigation* tab_navigation);
+
+ // Returns the session service from |sync_service_|.
+ SessionService* GetSessionService();
// Internal method used in the callback to obtain the current session.
// We don't own |windows|.
void OnGotSession(int handle, std::vector<SessionWindow*>* windows);
- // Used to populate a session tab from the session specifics tab provided.
- void AppendSessionTabNavigation(std::vector<TabNavigation>* navigations,
- const sync_pb::TabNavigation* navigation);
+ // Populate a session specifics header from a list of SessionWindows
+ void PopulateSessionSpecificsHeader(
+ const std::vector<SessionWindow*>& windows,
+ sync_pb::SessionHeader* header_s);
- // Used to populate a session tab from the session specifics tab provided.
- void PopulateSessionTabFromSpecifics(SessionTab* session_tab,
- const sync_pb::SessionTab* tab, SessionID id);
+ // Populates the window portion of the session specifics.
+ void PopulateSessionSpecificsWindow(const SessionWindow& window,
+ sync_pb::SessionWindow* session_window);
- // Used to populate a session window from the session specifics window
- // provided.
- void PopulateSessionWindowFromSpecifics(SessionWindow* session_window,
- const sync_pb::SessionWindow* window);
-
- // Updates the current session on the server. Creates a node for this machine
- // if there is not one already.
- bool UpdateSyncModel(sync_pb::SessionSpecifics* session_data,
- sync_api::WriteTransaction* trans,
- const sync_api::ReadNode* root);
- // Stores the machine tag.
+ // Syncs all the tabs in |window| with the local sync db. Will allocate tab
+ // nodes if needed.
+ bool SyncLocalWindowToSyncModel(const SessionWindow& window);
+
+ // Fills a tab sync node with data from a SessionTab object.
+ // (from ReadCurrentSessions)
+ bool WriteSessionTabToSyncModel(const SessionTab& tab,
+ const int64 sync_id,
+ sync_api::WriteTransaction* trans);
+
+ // Populates the tab portion of the session specifics.
+ void PopulateSessionSpecificsTab(const SessionTab& tab,
+ sync_pb::SessionTab* session_tab);
+
+ // Local client name.
std::string current_machine_tag_;
- // Stores the current set of foreign session specifics.
- // Used by ForeignSessionHandler through |GetSessionData|.
- // Built by |QuerySyncModel| via |UpdateFromSyncModel|.
- std::vector<const sync_pb::SessionSpecifics*> specifics_;
+ // Pool of all used/available sync nodes associated with tabs.
+ TabNodePool tab_pool_;
+
+ // SyncID for the sync node containing all the window information for this
+ // client.
+ int64 local_session_syncid_;
+
+ // Mapping of current open (local) tabs to their sync identifiers.
+ TabLinksMap tab_map_;
+
+ ForeignSessionTracker foreign_session_tracker_;
// Weak pointer.
ProfileSyncService* sync_service_;
diff --git a/chrome/browser/sync/glue/session_model_associator_unittest.cc b/chrome/browser/sync/glue/session_model_associator_unittest.cc
new file mode 100644
index 0000000..77f3866
--- /dev/null
+++ b/chrome/browser/sync/glue/session_model_associator_unittest.cc
@@ -0,0 +1,163 @@
+// 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 <vector>
+
+#include "base/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 "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using browser_sync::SessionModelAssociator;
+using browser_sync::ForeignSessionTracker;
+namespace browser_sync {
+
+typedef testing::Test SessionModelAssociatorTest;
+
+TEST_F(SessionModelAssociatorTest, SessionWindowHasNoTabsToSync) {
+ SessionWindow win;
+ ASSERT_TRUE(SessionModelAssociator::SessionWindowHasNoTabsToSync(win));
+ scoped_ptr<SessionTab> tab(new SessionTab());
+ win.tabs.push_back(tab.release());
+ ASSERT_TRUE(SessionModelAssociator::SessionWindowHasNoTabsToSync(win));
+ TabNavigation nav(0, GURL("about:bubba"), GURL("about:referrer"),
+ string16(ASCIIToUTF16("title")),
+ std::string("state"), 0U);
+ win.tabs[0]->navigations.push_back(nav);
+ ASSERT_FALSE(SessionModelAssociator::SessionWindowHasNoTabsToSync(win));
+}
+
+TEST_F(SessionModelAssociatorTest, IsValidSessionTab) {
+ SessionTab tab;
+ ASSERT_FALSE(SessionModelAssociator::IsValidSessionTab(tab));
+ TabNavigation nav(0, GURL(chrome::kChromeUINewTabURL),
+ GURL("about:referrer"),
+ string16(ASCIIToUTF16("title")),
+ std::string("state"), 0U);
+ tab.navigations.push_back(nav);
+ // NewTab does not count as valid if it's the only navigation.
+ ASSERT_FALSE(SessionModelAssociator::IsValidSessionTab(tab));
+ TabNavigation nav2(0, GURL("about:bubba"),
+ GURL("about:referrer"),
+ string16(ASCIIToUTF16("title")),
+ std::string("state"), 0U);
+ tab.navigations.push_back(nav2);
+ // Once there's another navigation, the tab is valid.
+ ASSERT_TRUE(SessionModelAssociator::IsValidSessionTab(tab));
+}
+
+TEST_F(SessionModelAssociatorTest, PopulateSessionWindow) {
+ sync_pb::SessionWindow window_s;
+ window_s.add_tab(0);
+ window_s.set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
+ window_s.set_selected_tab_index(1);
+
+ std::string tag = "tag";
+ ForeignSessionTracker tracker;
+ ForeignSession* session = tracker.GetForeignSession(tag);
+ SessionWindow* win = new SessionWindow();
+ session->windows.push_back(win);
+ SessionModelAssociator::PopulateSessionWindowFromSpecifics(
+ tag, window_s, 0, win, &tracker);
+ ASSERT_EQ(1U, win->tabs.size());
+ ASSERT_EQ(1, win->selected_tab_index);
+ ASSERT_EQ(1, win->type);
+ ASSERT_EQ(1U, tracker.num_foreign_sessions());
+ ASSERT_EQ(1U, tracker.num_foreign_tabs(std::string("tag")));
+
+ // We do this so that when the destructor for the tracker is called, it will
+ // be able to delete the session, window, and tab. We can't delete these
+ // ourselves, otherwise we would run into double free errors when the
+ // destructor was invoked (the true argument tells the tracker the tab
+ // is now associated with a window).
+ ASSERT_TRUE(tracker.GetSessionTab(tag, 0, true));
+}
+
+TEST_F(SessionModelAssociatorTest, PopulateSessionTab) {
+ sync_pb::SessionTab tab_s;
+ tab_s.set_tab_visual_index(13);
+ tab_s.set_current_navigation_index(3);
+ tab_s.set_pinned(true);
+ tab_s.set_extension_app_id("app_id");
+ sync_pb::TabNavigation* navigation = tab_s.add_navigation();
+ navigation->set_index(12);
+ navigation->set_virtual_url("http://foo/1");
+ navigation->set_referrer("referrer");
+ navigation->set_title("title");
+ navigation->set_page_transition(sync_pb::TabNavigation_PageTransition_TYPED);
+
+ SessionTab tab;
+ SessionModelAssociator::PopulateSessionTabFromSpecifics(tab_s, 0, &tab);
+ ASSERT_EQ(13, tab.tab_visual_index);
+ ASSERT_EQ(3, tab.current_navigation_index);
+ ASSERT_TRUE(tab.pinned);
+ ASSERT_EQ("app_id", tab.extension_app_id);
+ ASSERT_EQ(12, tab.navigations[0].index());
+ ASSERT_EQ(GURL("referrer"), tab.navigations[0].referrer());
+ ASSERT_EQ(string16(ASCIIToUTF16("title")), tab.navigations[0].title());
+ ASSERT_EQ(PageTransition::TYPED, tab.navigations[0].transition());
+ ASSERT_EQ(GURL("http://foo/1"), tab.navigations[0].virtual_url());
+}
+
+TEST_F(SessionModelAssociatorTest, ForeignSessionTracker) {
+ const std::string tag1 = "tag";
+ const std::string tag2 = "tag2";
+ const std::string tag3 = "tag3";
+ ForeignSessionTracker tracker;
+ ASSERT_TRUE(tracker.empty());
+ ASSERT_EQ(0U, tracker.num_foreign_sessions());
+ ASSERT_EQ(0U, tracker.num_foreign_tabs(tag1));
+ SessionTab* tab = tracker.GetSessionTab(tag1, 0, false);
+ ASSERT_EQ(1U, tracker.num_foreign_tabs(tag1));
+ ASSERT_EQ(0U, tracker.num_foreign_sessions());
+ SessionTab* tab2 = tracker.GetSessionTab(tag1, 0, false);
+ ASSERT_EQ(1U, tracker.num_foreign_tabs(tag1));
+ ASSERT_EQ(0U, tracker.num_foreign_sessions());
+ ASSERT_EQ(tab, tab2);
+ tab2 = tracker.GetSessionTab(tag2, 0, false);
+ ASSERT_EQ(1U, tracker.num_foreign_tabs(tag1));
+ ASSERT_EQ(1U, tracker.num_foreign_tabs(tag2));
+ ASSERT_EQ(0U, tracker.num_foreign_sessions());
+
+ ASSERT_FALSE(tracker.DeleteForeignSession(tag1));
+ ASSERT_FALSE(tracker.DeleteForeignSession(tag3));
+
+ ForeignSession* session = tracker.GetForeignSession(tag1);
+ ForeignSession* session2 = tracker.GetForeignSession(tag2);
+ ForeignSession* session3 = tracker.GetForeignSession(tag3);
+ ASSERT_EQ(3U, tracker.num_foreign_sessions());
+
+ ASSERT_TRUE(session);
+ ASSERT_TRUE(session2);
+ ASSERT_TRUE(session3);
+ ASSERT_NE(session, session2);
+ ASSERT_NE(session2, session3);
+ ASSERT_TRUE(tracker.DeleteForeignSession(tag3));
+ ASSERT_EQ(2U, tracker.num_foreign_sessions());
+
+ const SessionTab *tab_ptr;
+ ASSERT_TRUE(tracker.LookupSessionTab(tag1, 0, &tab_ptr));
+ ASSERT_EQ(tab_ptr, tab);
+
+ std::vector<SessionWindow*> windows;
+ ASSERT_TRUE(tracker.LookupSessionWindows(tag1, &windows));
+ ASSERT_EQ(0U, windows.size());
+
+ // The sessions don't have valid windows, lookup should not succeed.
+ std::vector<const ForeignSession*> sessions;
+ ASSERT_FALSE(tracker.LookupAllForeignSessions(&sessions));
+
+ tracker.clear();
+ ASSERT_EQ(0U, tracker.num_foreign_tabs(tag1));
+ ASSERT_EQ(0U, tracker.num_foreign_tabs(tag2));
+ ASSERT_EQ(0U, tracker.num_foreign_sessions());
+}
+
+} // namespace browser_sync
+
diff --git a/chrome/browser/sync/glue/sync_backend_host.h b/chrome/browser/sync/glue/sync_backend_host.h
index fdfc7b4..3543a4f 100644
--- a/chrome/browser/sync/glue/sync_backend_host.h
+++ b/chrome/browser/sync/glue/sync_backend_host.h
@@ -14,7 +14,7 @@
#include "base/lock.h"
#include "base/message_loop.h"
#include "base/ref_counted.h"
-#include "base/thread.h"
+#include "base/threading/thread.h"
#include "base/timer.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/sync/engine/syncapi.h"
diff --git a/chrome/browser/sync/glue/sync_backend_host_mock.cc b/chrome/browser/sync/glue/sync_backend_host_mock.cc
new file mode 100644
index 0000000..fe488ad
--- /dev/null
+++ b/chrome/browser/sync/glue/sync_backend_host_mock.cc
@@ -0,0 +1,26 @@
+// 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_mock.h"
+
+namespace browser_sync {
+
+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::_)).
+ WillByDefault(InvokeTask());
+}
+
+SyncBackendHostMock::~SyncBackendHostMock() {}
+
+} // namespace browser_sync
diff --git a/chrome/browser/sync/glue/sync_backend_host_mock.h b/chrome/browser/sync/glue/sync_backend_host_mock.h
index 4035f97..e133a57 100644
--- a/chrome/browser/sync/glue/sync_backend_host_mock.h
+++ b/chrome/browser/sync/glue/sync_backend_host_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.
@@ -23,20 +23,8 @@ ACTION(InvokeTask) {
class SyncBackendHostMock : public SyncBackendHost {
public:
- 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::_)).
- WillByDefault(InvokeTask());
- }
+ SyncBackendHostMock();
+ virtual ~SyncBackendHostMock();
MOCK_METHOD2(ConfigureDataTypes,
void(const std::set<syncable::ModelType>&, CancelableTask*));
diff --git a/chrome/browser/sync/glue/ui_model_worker.cc b/chrome/browser/sync/glue/ui_model_worker.cc
index 24622ae..38f4a98 100644
--- a/chrome/browser/sync/glue/ui_model_worker.cc
+++ b/chrome/browser/sync/glue/ui_model_worker.cc
@@ -6,7 +6,7 @@
#include "base/message_loop.h"
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
namespace browser_sync {
@@ -32,7 +32,7 @@ void UIModelWorker::DoWorkAndWaitUntilDone(Callback0::Type* work) {
// We lock only to avoid PostTask'ing a NULL pending_work_ (because it
// could get Run() in Stop() and call OnTaskCompleted before we post).
// The task is owned by the message loop as per usual.
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
DCHECK(!pending_work_);
pending_work_ = new CallDoWorkAndSignalTask(work, &work_done, this);
ui_loop_->PostTask(FROM_HERE, pending_work_);
@@ -54,7 +54,7 @@ UIModelWorker::~UIModelWorker() {
}
void UIModelWorker::OnSyncerShutdownComplete() {
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
// The SyncerThread has terminated and we are no longer needed by syncapi.
// The UI loop initiated shutdown and is (or will be) waiting in Stop().
// We could either be WORKING or RUNNING_MANUAL_SHUTDOWN_PUMP, depending
@@ -69,7 +69,7 @@ void UIModelWorker::OnSyncerShutdownComplete() {
void UIModelWorker::Stop() {
DCHECK_EQ(MessageLoop::current(), ui_loop_);
- AutoLock lock(lock_);
+ base::AutoLock lock(lock_);
DCHECK_EQ(state_, WORKING);
// We're on our own now, the beloved UI MessageLoop is no longer running.
diff --git a/chrome/browser/sync/glue/ui_model_worker.h b/chrome/browser/sync/glue/ui_model_worker.h
index f3a988b..503d8cb 100644
--- a/chrome/browser/sync/glue/ui_model_worker.h
+++ b/chrome/browser/sync/glue/ui_model_worker.h
@@ -7,8 +7,8 @@
#pragma once
#include "base/callback.h"
-#include "base/condition_variable.h"
-#include "base/lock.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/condition_variable.h"
#include "base/task.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/engine/model_safe_worker.h"
@@ -119,14 +119,14 @@ class UIModelWorker : public browser_sync::ModelSafeWorker {
// barrier permits instructions to be reordered by compiler optimizations.
// Possible or not, that route makes for very fragile code due to existence
// of theoretical races.
- Lock lock_;
+ base::Lock lock_;
// Used as a barrier at shutdown to ensure the SyncerThread terminates before
// we allow the UI thread to return from Stop(). This gets signalled whenever
// one of two events occur: a new pending_work_ task was scheduled, or the
// SyncerThread has terminated. We only care about (1) when we are in Stop(),
// because we have to manually Run() the task.
- ConditionVariable syncapi_event_;
+ base::ConditionVariable syncapi_event_;
DISALLOW_COPY_AND_ASSIGN(UIModelWorker);
};
diff --git a/chrome/browser/sync/glue/ui_model_worker_unittest.cc b/chrome/browser/sync/glue/ui_model_worker_unittest.cc
index e71a254..a2a1dde 100644
--- a/chrome/browser/sync/glue/ui_model_worker_unittest.cc
+++ b/chrome/browser/sync/glue/ui_model_worker_unittest.cc
@@ -5,8 +5,8 @@
#include "base/callback.h"
#include "base/message_loop.h"
#include "base/ref_counted.h"
-#include "base/thread.h"
-#include "base/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/ui_model_worker.h"
#include "testing/gtest/include/gtest/gtest.h"
diff --git a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h
index ac2d0c4..a879836 100644
--- a/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h
+++ b/chrome/browser/sync/notifier/cache_invalidation_packet_handler.h
@@ -13,8 +13,8 @@
#include "base/basictypes.h"
#include "base/gtest_prod_util.h"
-#include "base/non_thread_safe.h"
#include "base/scoped_callback_factory.h"
+#include "base/threading/non_thread_safe.h"
#include "base/weak_ptr.h"
namespace invalidation {
@@ -52,7 +52,7 @@ class CacheInvalidationPacketHandler {
void HandleInboundPacket(const std::string& packet);
- NonThreadSafe non_thread_safe_;
+ base::NonThreadSafe non_thread_safe_;
base::ScopedCallbackFactory<CacheInvalidationPacketHandler>
scoped_callback_factory_;
diff --git a/chrome/browser/sync/notifier/chrome_invalidation_client.h b/chrome/browser/sync/notifier/chrome_invalidation_client.h
index e408085..99af02c 100644
--- a/chrome/browser/sync/notifier/chrome_invalidation_client.h
+++ b/chrome/browser/sync/notifier/chrome_invalidation_client.h
@@ -12,9 +12,9 @@
#include <string>
#include "base/basictypes.h"
-#include "base/non_thread_safe.h"
#include "base/scoped_callback_factory.h"
#include "base/scoped_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"
@@ -99,7 +99,7 @@ class ChromeInvalidationClient
void HandleOutboundPacket(
invalidation::NetworkEndpoint* const& network_endpoint);
- NonThreadSafe non_thread_safe_;
+ base::NonThreadSafe non_thread_safe_;
ChromeSystemResources chrome_system_resources_;
base::ScopedCallbackFactory<ChromeInvalidationClient>
scoped_callback_factory_;
diff --git a/chrome/browser/sync/notifier/chrome_system_resources.h b/chrome/browser/sync/notifier/chrome_system_resources.h
index b6e6256..06b8001 100644
--- a/chrome/browser/sync/notifier/chrome_system_resources.h
+++ b/chrome/browser/sync/notifier/chrome_system_resources.h
@@ -14,9 +14,9 @@
#include <string>
#include "base/message_loop.h"
-#include "base/non_thread_safe.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"
#include "google/cacheinvalidation/invalidation-client.h"
@@ -52,7 +52,7 @@ class ChromeSystemResources : public invalidation::SystemResources {
invalidation::StorageCallback* callback);
private:
- NonThreadSafe non_thread_safe_;
+ base::NonThreadSafe non_thread_safe_;
scoped_ptr<ScopedRunnableMethodFactory<ChromeSystemResources> >
scoped_runnable_method_factory_;
// Holds all posted tasks that have not yet been run.
diff --git a/chrome/browser/sync/notifier/registration_manager.h b/chrome/browser/sync/notifier/registration_manager.h
index 1b1a534..2694373 100644
--- a/chrome/browser/sync/notifier/registration_manager.h
+++ b/chrome/browser/sync/notifier/registration_manager.h
@@ -12,7 +12,7 @@
#include <map>
#include "base/basictypes.h"
-#include "base/non_thread_safe.h"
+#include "base/threading/non_thread_safe.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "google/cacheinvalidation/invalidation-client.h"
@@ -54,7 +54,7 @@ class RegistrationManager {
void OnRegister(const invalidation::RegistrationUpdateResult& result);
- NonThreadSafe non_thread_safe_;
+ base::NonThreadSafe non_thread_safe_;
// Weak pointer.
invalidation::InvalidationClient* invalidation_client_;
RegistrationStatusMap registration_status_;
diff --git a/chrome/browser/sync/profile_sync_factory_mock.cc b/chrome/browser/sync/profile_sync_factory_mock.cc
index 68fc045..2d92100 100644
--- a/chrome/browser/sync/profile_sync_factory_mock.cc
+++ b/chrome/browser/sync/profile_sync_factory_mock.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.
@@ -11,6 +11,8 @@ using browser_sync::ChangeProcessor;
using testing::_;
using testing::InvokeWithoutArgs;
+ProfileSyncFactoryMock::ProfileSyncFactoryMock() {}
+
ProfileSyncFactoryMock::ProfileSyncFactoryMock(
AssociatorInterface* bookmark_model_associator,
ChangeProcessor* bookmark_change_processor)
@@ -23,6 +25,8 @@ ProfileSyncFactoryMock::ProfileSyncFactoryMock(
&ProfileSyncFactoryMock::MakeBookmarkSyncComponents));
}
+ProfileSyncFactoryMock::~ProfileSyncFactoryMock() {}
+
ProfileSyncFactory::SyncComponents
ProfileSyncFactoryMock::MakeBookmarkSyncComponents() {
return SyncComponents(bookmark_model_associator_.release(),
diff --git a/chrome/browser/sync/profile_sync_factory_mock.h b/chrome/browser/sync/profile_sync_factory_mock.h
index f5210a0..a1d1a15 100644
--- a/chrome/browser/sync/profile_sync_factory_mock.h
+++ b/chrome/browser/sync/profile_sync_factory_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.
@@ -18,10 +18,11 @@ class ChangeProcessor;
class ProfileSyncFactoryMock : public ProfileSyncFactory {
public:
- ProfileSyncFactoryMock() {}
+ ProfileSyncFactoryMock();
ProfileSyncFactoryMock(
browser_sync::AssociatorInterface* bookmark_model_associator,
browser_sync::ChangeProcessor* bookmark_change_processor);
+ virtual ~ProfileSyncFactoryMock();
MOCK_METHOD1(CreateProfileSyncService,
ProfileSyncService*(const std::string&));
diff --git a/chrome/browser/sync/profile_sync_service.cc b/chrome/browser/sync/profile_sync_service.cc
index a3da689..852ed4b 100644
--- a/chrome/browser/sync/profile_sync_service.cc
+++ b/chrome/browser/sync/profile_sync_service.cc
@@ -19,6 +19,7 @@
#include "base/string_util.h"
#include "base/task.h"
#include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_signin.h"
#include "chrome/browser/history/history_types.h"
#include "chrome/browser/platform_util.h"
#include "chrome/browser/prefs/pref_service.h"
@@ -31,6 +32,7 @@
#include "chrome/browser/sync/glue/session_data_type_controller.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/signin_manager.h"
+#include "chrome/browser/sync/sync_ui_util.h"
#include "chrome/browser/sync/token_migrator.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/net/gaia/gaia_constants.h"
@@ -39,6 +41,7 @@
#include "chrome/common/notification_type.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/time_format.h"
+#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "jingle/notifier/communicator/const_communicator.h"
#include "net/base/cookie_monster.h"
@@ -716,11 +719,7 @@ void ProfileSyncService::ShowLoginDialog(gfx::NativeWindow parent_window) {
}
wizard_.SetParent(parent_window);
- // This method will also be called if a passphrase is needed.
- if (observed_passphrase_required_)
- wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE);
- else
- wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
+ wizard_.Step(SyncSetupWizard::GAIA_LOGIN);
FOR_EACH_OBSERVER(Observer, observers_, OnStateChanged());
}
@@ -734,6 +733,31 @@ void ProfileSyncService::ShowConfigure(gfx::NativeWindow parent_window) {
wizard_.Step(SyncSetupWizard::CONFIGURE);
}
+void ProfileSyncService::PromptForExistingPassphrase(
+ gfx::NativeWindow parent_window) {
+ if (WizardIsVisible()) {
+ wizard_.Focus();
+ return;
+ }
+ wizard_.SetParent(parent_window);
+ wizard_.Step(SyncSetupWizard::ENTER_PASSPHRASE);
+}
+
+void ProfileSyncService::ShowPassphraseMigration(
+ gfx::NativeWindow parent_window) {
+ wizard_.SetParent(parent_window);
+ wizard_.Step(SyncSetupWizard::PASSPHRASE_MIGRATION);
+}
+
+void ProfileSyncService::SigninForPassphrase(TabContents* container) {
+ string16 prefilled_username = GetAuthenticatedUsername();
+ string16 login_message = sync_ui_util::GetLoginMessageForEncryption();
+ profile_->GetBrowserSignin()->RequestSignin(container,
+ prefilled_username,
+ login_message,
+ this);
+}
+
SyncBackendHost::StatusSummary ProfileSyncService::QuerySyncStatusSummary() {
if (backend_.get() && backend_initialized_)
return backend_->GetStatusSummary();
@@ -1138,6 +1162,27 @@ void ProfileSyncService::Observe(NotificationType type,
}
}
+// This is the delegate callback from BrowserSigin.
+void ProfileSyncService::OnLoginSuccess() {
+ // The reason for the browser signin was a non-explicit passphrase
+ // required signal. If this is the first time through the passphrase
+ // process, we want to show the migration UI and offer an explicit
+ // passphrase. Otherwise, we're done because the implicit is enough.
+
+ if (passphrase_required_for_decryption_) {
+ // NOT first time (decrypting something encrypted elsewhere).
+ // Do nothing.
+ } else {
+ ShowPassphraseMigration(NULL);
+ }
+}
+
+// This is the delegate callback from BrowserSigin.
+void ProfileSyncService::OnLoginFailure(const GoogleServiceAuthError& error) {
+ // Do nothing. The UI will already reflect the fact that the
+ // user is not signed in.
+}
+
void ProfileSyncService::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
}
diff --git a/chrome/browser/sync/profile_sync_service.h b/chrome/browser/sync/profile_sync_service.h
index 19583b6..62a8e07 100644
--- a/chrome/browser/sync/profile_sync_service.h
+++ b/chrome/browser/sync/profile_sync_service.h
@@ -15,6 +15,7 @@
#include "base/string16.h"
#include "base/time.h"
#include "base/timer.h"
+#include "chrome/browser/browser_signin.h"
#include "chrome/browser/prefs/pref_member.h"
#include "chrome/browser/sync/engine/syncapi.h"
#include "chrome/browser/sync/glue/data_type_controller.h"
@@ -37,6 +38,7 @@ class NotificationSource;
class NotificationType;
class Profile;
class ProfileSyncFactory;
+class TabContents;
class TokenMigrator;
// ProfileSyncService is the layer between browser subsystems like bookmarks,
@@ -83,6 +85,7 @@ class TokenMigrator;
//
class ProfileSyncService : public browser_sync::SyncFrontend,
public browser_sync::UnrecoverableErrorHandler,
+ public BrowserSignin::SigninDelegate,
public NotificationObserver {
public:
typedef ProfileSyncServiceObserver Observer;
@@ -223,6 +226,9 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
}
virtual void ShowLoginDialog(gfx::NativeWindow parent_window);
void ShowConfigure(gfx::NativeWindow parent_window);
+ void PromptForExistingPassphrase(gfx::NativeWindow parent_window);
+ void SigninForPassphrase(TabContents* container);
+ void ShowPassphraseMigration(gfx::NativeWindow parent_window);
// Pretty-printed strings for a given StatusSummary.
static std::string BuildSyncStatusSummaryText(
@@ -230,6 +236,10 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
// Returns true if the SyncBackendHost has told us it's ready to accept
// changes.
+ // [REMARK] - it is safe to call this function only from the ui thread.
+ // because the variable is not thread safe and should only be accessed from
+ // single thread. If we want multiple threads to access this(and there is
+ // currently no need to do so) we need to protect this with a lock.
// TODO(timsteele): What happens if the bookmark model is loaded, a change
// takes place, and the backend isn't initialized yet?
bool sync_initialized() const { return backend_initialized_; }
@@ -315,6 +325,10 @@ class ProfileSyncService : public browser_sync::SyncFrontend,
const NotificationSource& source,
const NotificationDetails& details);
+ // BrowserSignin::SigninDelegate interface.
+ virtual void OnLoginSuccess();
+ virtual void OnLoginFailure(const GoogleServiceAuthError& error);
+
// Changes which data types we're going to be syncing to |preferred_types|.
// If it is running, the DataTypeManager will be instructed to reconfigure
// the sync backend so that exactly these datatypes are actively synced. See
diff --git a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
index a7b929a..025c573 100644
--- a/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_autofill_unittest.cc
@@ -16,7 +16,7 @@
#include "base/task.h"
#include "base/time.h"
#include "base/utf_string_conversions.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "chrome/browser/autofill/autofill_common_test.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/sync/abstract_profile_sync_service_test.h"
@@ -28,10 +28,15 @@
#include "chrome/browser/sync/glue/autofill_data_type_controller.h"
#include "chrome/browser/sync/glue/autofill_model_associator.h"
#include "chrome/browser/sync/glue/autofill_model_associator2.h"
+#include "chrome/browser/sync/glue/autofill_profile_change_processor.h"
+#include "chrome/browser/sync/glue/autofill_profile_data_type_controller.h"
+#include "chrome/browser/sync/glue/autofill_profile_model_associator.h"
+#include "chrome/browser/sync/glue/data_type_controller.h"
#include "chrome/browser/sync/profile_sync_factory.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/browser/sync/protocol/autofill_specifics.pb.h"
+#include "chrome/browser/sync/syncable/autofill_migration.h"
#include "chrome/browser/sync/syncable/syncable.h"
#include "chrome/browser/sync/syncable/model_type.h"
#include "chrome/browser/sync/test_profile_sync_service.h"
@@ -47,9 +52,15 @@
using base::Time;
using base::WaitableEvent;
+using browser_sync::AutofillChangeProcessor;
using browser_sync::AutofillChangeProcessor2;
using browser_sync::AutofillDataTypeController;
+using browser_sync::AutofillModelAssociator;
using browser_sync::AutofillModelAssociator2;
+using browser_sync::AutofillProfileChangeProcessor;
+using browser_sync::AutofillProfileDataTypeController;
+using browser_sync::AutofillProfileModelAssociator;
+using browser_sync::DataTypeController;
using browser_sync::GROUP_DB;
using browser_sync::kAutofillTag;
using browser_sync::SyncBackendHostForProfileSyncTest;
@@ -99,9 +110,26 @@ class WebDatabaseMock : public WebDatabase {
MOCK_METHOD1(AddAutoFillProfile,
bool(const AutoFillProfile&)); // NOLINT
MOCK_METHOD1(RemoveAutoFillProfile,
- bool(int)); // NOLINT
+ bool(const string&)); // NOLINT
};
+class ProfileSyncServiceAutofillTest;
+
+template<class AutoFillProfile>
+syncable::ModelType GetModelType() {
+ return syncable::UNSPECIFIED;
+}
+
+template<>
+syncable::ModelType GetModelType<AutofillEntry>() {
+ return syncable::AUTOFILL;
+}
+
+template<>
+syncable::ModelType GetModelType<AutoFillProfile>() {
+ return syncable::AUTOFILL_PROFILE;
+}
+
class WebDataServiceFake : public WebDataService {
public:
explicit WebDataServiceFake(WebDatabase* web_database)
@@ -118,31 +146,113 @@ class WebDataServiceFake : public WebDataService {
WebDatabase* web_database_;
};
-class PersonalDataManagerMock: public PersonalDataManager {
- public:
- MOCK_CONST_METHOD0(IsDataLoaded, bool());
- MOCK_METHOD0(LoadProfiles, void());
- MOCK_METHOD0(LoadCreditCards, void());
- MOCK_METHOD0(Refresh, void());
-};
-
ACTION_P4(MakeAutofillSyncComponents, service, wd, pdm, dtc) {
EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB));
if (!BrowserThread::CurrentlyOn(BrowserThread::DB))
return ProfileSyncFactory::SyncComponents(NULL, NULL);
- AutofillModelAssociator2* model_associator =
- new AutofillModelAssociator2(service, wd, pdm);
- AutofillChangeProcessor2* change_processor =
- new AutofillChangeProcessor2(model_associator, wd, pdm, dtc);
+ AutofillModelAssociator* model_associator =
+ new AutofillModelAssociator(service, wd, pdm);
+ AutofillChangeProcessor* change_processor =
+ new AutofillChangeProcessor(model_associator, wd, pdm, dtc);
return ProfileSyncFactory::SyncComponents(model_associator,
change_processor);
}
+ACTION_P4(MakeAutofillProfileSyncComponents, service, wd, pdm, dtc) {
+ EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB));
+ if (!BrowserThread::CurrentlyOn(BrowserThread::DB))
+ return ProfileSyncFactory::SyncComponents(NULL, NULL);
+ AutofillProfileModelAssociator* model_associator =
+ new AutofillProfileModelAssociator(service, wd, pdm);
+ AutofillProfileChangeProcessor* change_processor =
+ new AutofillProfileChangeProcessor(model_associator, wd, pdm, dtc);
+ return ProfileSyncFactory::SyncComponents(model_associator,
+ change_processor);
+}
+
+class AbstractAutofillFactory {
+ public:
+ virtual AutofillDataTypeController* CreateDataTypeController(
+ ProfileSyncFactory *factory,
+ ProfileMock* profile,
+ ProfileSyncService* service) = 0;
+ virtual void SetExpectation(ProfileSyncFactoryMock* factory,
+ ProfileSyncService* service,
+ WebDatabase* wd,
+ PersonalDataManager* pdm,
+ DataTypeController* dtc) = 0;
+ virtual ~AbstractAutofillFactory() {}
+};
+
+class AutofillEntryFactory : public AbstractAutofillFactory {
+ public:
+ browser_sync::AutofillDataTypeController* CreateDataTypeController(
+ ProfileSyncFactory *factory,
+ ProfileMock* profile,
+ ProfileSyncService* service) {
+ return new AutofillDataTypeController(factory,
+ profile,
+ service);
+ }
+
+ void SetExpectation(ProfileSyncFactoryMock* factory,
+ ProfileSyncService* service,
+ WebDatabase* wd,
+ PersonalDataManager* pdm,
+ DataTypeController* dtc) {
+ EXPECT_CALL(*factory, CreateAutofillSyncComponents(_,_,_,_)).
+ WillOnce(MakeAutofillSyncComponents(service, wd, pdm, dtc));
+ }
+};
+
+class AutofillProfileFactory : public AbstractAutofillFactory {
+ public:
+ browser_sync::AutofillDataTypeController* CreateDataTypeController(
+ ProfileSyncFactory *factory,
+ ProfileMock* profile,
+ ProfileSyncService* service) {
+ return new AutofillProfileDataTypeController(factory,
+ profile,
+ service);
+ }
+
+ void SetExpectation(ProfileSyncFactoryMock* factory,
+ ProfileSyncService* service,
+ WebDatabase* wd,
+ PersonalDataManager* pdm,
+ DataTypeController* dtc) {
+ EXPECT_CALL(*factory, CreateAutofillProfileSyncComponents(_,_,_,_)).
+ WillOnce(MakeAutofillProfileSyncComponents(service, wd, pdm, dtc));
+ }
+};
+
+class PersonalDataManagerMock: public PersonalDataManager {
+ public:
+ MOCK_CONST_METHOD0(IsDataLoaded, bool());
+ MOCK_METHOD0(LoadProfiles, void());
+ MOCK_METHOD0(LoadCreditCards, void());
+ MOCK_METHOD0(Refresh, void());
+};
+template <class T> class AddAutofillTask;
+
class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
protected:
ProfileSyncServiceAutofillTest() : db_thread_(BrowserThread::DB) {
}
+ AutofillProfileFactory profile_factory_;
+ AutofillEntryFactory entry_factory_;
+
+ AbstractAutofillFactory* GetFactory(syncable::ModelType type) {
+ if (type == syncable::AUTOFILL) {
+ return &entry_factory_;
+ } else if (type == syncable::AUTOFILL_PROFILE) {
+ return &profile_factory_;
+ } else {
+ NOTREACHED();
+ return NULL;
+ }
+ }
virtual void SetUp() {
web_data_service_ = new WebDataServiceFake(&web_database_);
personal_data_manager_ = new PersonalDataManagerMock();
@@ -162,48 +272,49 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
MessageLoop::current()->RunAllPending();
}
- void StartSyncService(Task* task, bool will_fail_association) {
- if (!service_.get()) {
- service_.reset(
- new TestProfileSyncService(&factory_, &profile_, "test_user", false,
- task));
- AutofillDataTypeController* data_type_controller =
- new AutofillDataTypeController(&factory_,
- &profile_,
- service_.get());
-
- SyncBackendHostForProfileSyncTest::
- SetDefaultExpectationsForWorkerCreation(&profile_);
-
- EXPECT_CALL(factory_, CreateAutofillSyncComponents(_, _, _, _)).
- WillOnce(MakeAutofillSyncComponents(service_.get(),
- &web_database_,
- personal_data_manager_.get(),
- data_type_controller));
- EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
- WillOnce(ReturnNewDataTypeManager());
-
- EXPECT_CALL(profile_, GetWebDataService(_)).
- WillOnce(Return(web_data_service_.get()));
-
- EXPECT_CALL(profile_, GetPersonalDataManager()).
- WillRepeatedly(Return(personal_data_manager_.get()));
-
- EXPECT_CALL(*personal_data_manager_, IsDataLoaded()).
- WillRepeatedly(Return(true));
-
- // We need tokens to get the tests going
- token_service_.IssueAuthTokenForTest(
- GaiaConstants::kSyncService, "token");
-
- 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();
- }
+ void StartSyncService(Task* task,
+ bool will_fail_association,
+ syncable::ModelType type) {
+ AbstractAutofillFactory* factory = GetFactory(type);
+ service_.reset(
+ new TestProfileSyncService(&factory_, &profile_, "test_user", false,
+ task));
+ AutofillDataTypeController* data_type_controller =
+ factory->CreateDataTypeController(&factory_,
+ &profile_,
+ service_.get());
+ SyncBackendHostForProfileSyncTest::
+ SetDefaultExpectationsForWorkerCreation(&profile_);
+
+ factory->SetExpectation(&factory_,
+ service_.get(),
+ &web_database_,
+ personal_data_manager_.get(),
+ data_type_controller);
+
+ EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
+ WillOnce(ReturnNewDataTypeManager());
+
+ EXPECT_CALL(profile_, GetWebDataService(_)).
+ WillOnce(Return(web_data_service_.get()));
+
+ EXPECT_CALL(profile_, GetPersonalDataManager()).
+ WillRepeatedly(Return(personal_data_manager_.get()));
+
+ EXPECT_CALL(*personal_data_manager_, IsDataLoaded()).
+ WillRepeatedly(Return(true));
+
+ // We need tokens to get the tests going
+ token_service_.IssueAuthTokenForTest(
+ GaiaConstants::kSyncService, "token");
+
+ 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();
}
bool AddAutofillSyncNode(const AutofillEntry& entry) {
@@ -214,30 +325,27 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
return false;
sync_api::WriteNode node(&trans);
- std::string tag = AutofillModelAssociator2::KeyToTag(entry.key().name(),
+ std::string tag = AutofillModelAssociator::KeyToTag(entry.key().name(),
entry.key().value());
if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag))
return false;
- AutofillChangeProcessor2::WriteAutofillEntry(entry, &node);
+ AutofillChangeProcessor::WriteAutofillEntry(entry, &node);
return true;
}
- bool AddAutofillProfileSyncNode(const AutoFillProfile& profile) {
+ bool AddAutofillSyncNode(const AutoFillProfile& profile) {
sync_api::WriteTransaction trans(
service_->backend()->GetUserShareHandle());
sync_api::ReadNode autofill_root(&trans);
- if (!autofill_root.InitByTagLookup(browser_sync::kAutofillTag))
+ if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag))
return false;
sync_api::WriteNode node(&trans);
- std::string tag = browser_sync::AutofillModelAssociator2::ProfileLabelToTag(
- profile.Label());
- if (!node.InitUniqueByCreation(syncable::AUTOFILL, autofill_root, tag))
+ std::string tag = profile.guid();
+ if (!node.InitUniqueByCreation(syncable::AUTOFILL_PROFILE,
+ autofill_root, tag))
return false;
- AutofillChangeProcessor2::WriteAutofillProfile(profile, &node);
- sync_pb::AutofillSpecifics s(node.GetAutofillSpecifics());
- s.mutable_profile()->set_label(UTF16ToUTF8(profile.Label()));
- node.SetAutofillSpecifics(s);
+ AutofillProfileChangeProcessor::WriteAutofillProfile(profile, &node);
return true;
}
@@ -268,8 +376,8 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
entries->push_back(AutofillEntry(key, timestamps));
} else if (autofill.has_profile()) {
AutoFillProfile p;
- p.set_label(UTF8ToUTF16(autofill.profile().label()));
- AutofillModelAssociator2::OverwriteProfileWithServerData(&p,
+ p.set_guid(autofill.profile().guid());
+ AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
autofill.profile());
profiles->push_back(p);
}
@@ -278,6 +386,31 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
return true;
}
+ bool GetAutofillProfilesFromSyncDBUnderProfileNode(
+ std::vector<AutoFillProfile>* profiles) {
+ sync_api::ReadTransaction trans(service_->backend()->GetUserShareHandle());
+ sync_api::ReadNode autofill_root(&trans);
+ if (!autofill_root.InitByTagLookup(browser_sync::kAutofillProfileTag))
+ return false;
+
+ int64 child_id = autofill_root.GetFirstChildId();
+ while (child_id != sync_api::kInvalidId) {
+ sync_api::ReadNode child_node(&trans);
+ if (!child_node.InitByIdLookup(child_id))
+ return false;
+
+ const sync_pb::AutofillProfileSpecifics& autofill(
+ child_node.GetAutofillProfileSpecifics());
+ AutoFillProfile p;
+ p.set_guid(autofill.guid());
+ AutofillProfileModelAssociator::OverwriteProfileWithServerData(&p,
+ autofill);
+ profiles->push_back(p);
+ child_id = child_node.GetSuccessorId();
+ }
+ return true;
+ }
+
void SetIdleChangeProcessorExpectations() {
EXPECT_CALL(web_database_, RemoveFormElement(_, _)).Times(0);
EXPECT_CALL(web_database_, GetAutofillTimestamps(_, _, _)).Times(0);
@@ -303,7 +436,8 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
return MakeAutofillEntry(name, value, timestamp, -1);
}
- friend class AddAutofillEntriesTask;
+ friend class AddAutofillTask<AutofillEntry>;
+ friend class AddAutofillTask<AutoFillProfile>;
friend class FakeServerUpdater;
BrowserThread db_thread_;
@@ -315,34 +449,28 @@ class ProfileSyncServiceAutofillTest : public AbstractProfileSyncServiceTest {
scoped_refptr<PersonalDataManagerMock> personal_data_manager_;
};
-class AddAutofillEntriesTask : public Task {
+template <class T>
+class AddAutofillTask : public Task {
public:
- AddAutofillEntriesTask(ProfileSyncServiceAutofillTest* test,
- const std::vector<AutofillEntry>& entries,
- const std::vector<AutoFillProfile>& profiles)
- : test_(test), entries_(entries), profiles_(profiles), success_(false) {
+ AddAutofillTask(ProfileSyncServiceAutofillTest* test,
+ const std::vector<T>& entries)
+ : test_(test), entries_(entries), success_(false) {
}
virtual void Run() {
- if (!test_->CreateRoot(syncable::AUTOFILL))
+ if (!test_->CreateRoot(GetModelType<T>()))
return;
for (size_t i = 0; i < entries_.size(); ++i) {
if (!test_->AddAutofillSyncNode(entries_[i]))
return;
}
- for (size_t i = 0; i < profiles_.size(); ++i) {
- if (!test_->AddAutofillProfileSyncNode(profiles_[i]))
- return;
- }
success_ = true;
}
-
bool success() { return success_; }
private:
ProfileSyncServiceAutofillTest* test_;
- const std::vector<AutofillEntry>& entries_;
- const std::vector<AutoFillProfile>& profiles_;
+ const std::vector<T>& entries_;
bool success_;
};
@@ -392,7 +520,7 @@ class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> {
ASSERT_TRUE(dir.good());
// Create autofill protobuf
- std::string tag = AutofillModelAssociator2::KeyToTag(entry_.key().name(),
+ std::string tag = AutofillModelAssociator::KeyToTag(entry_.key().name(),
entry_.key().value());
sync_pb::AutofillSpecifics new_autofill;
new_autofill.set_name(UTF16ToUTF8(entry_.key().name()));
@@ -485,7 +613,7 @@ class FakeServerUpdater: public base::RefCountedThreadSafe<FakeServerUpdater> {
// waiting for the PersonalDataManager.
TEST_F(ProfileSyncServiceAutofillTest, FailModelAssociation) {
// Don't create the root autofill node so startup fails.
- StartSyncService(NULL, true);
+ StartSyncService(NULL, true, syncable::AUTOFILL);
EXPECT_TRUE(service_->unrecoverable_error_detected());
}
@@ -495,7 +623,7 @@ TEST_F(ProfileSyncServiceAutofillTest, EmptyNativeEmptySync) {
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
std::vector<AutoFillProfile> sync_profiles;
@@ -513,7 +641,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) {
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
std::vector<AutoFillProfile> sync_profiles;
@@ -523,18 +651,15 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeEntriesEmptySync) {
EXPECT_EQ(0U, sync_profiles.size());
}
-TEST_F(ProfileSyncServiceAutofillTest, HasMixedNativeEmptySync) {
- std::vector<AutofillEntry> entries;
- entries.push_back(MakeAutofillEntry("foo", "bar", 1));
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
- WillOnce(DoAll(SetArgumentPointee<0>(entries), Return(true)));
+TEST_F(ProfileSyncServiceAutofillTest, HasProfileEmptySync) {
std::vector<AutoFillProfile*> profiles;
std::vector<AutoFillProfile> expected_profiles;
// Owned by GetAutoFillProfiles caller.
AutoFillProfile* profile0 = new AutoFillProfile;
- autofill_test::SetProfileInfo(profile0,
- "Billing", "Marion", "Mitchell", "Morrison",
+ autofill_test::SetProfileInfoWithGuid(profile0,
+ "{54B3F9AA-335E-4f71-A27D-719C41564230}", "Billing",
+ "Mitchell", "Morrison",
"johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
"91601", "US", "12345678910", "01987654321");
profiles.push_back(profile0);
@@ -543,88 +668,15 @@ TEST_F(ProfileSyncServiceAutofillTest, HasMixedNativeEmptySync) {
WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true)));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
- CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ CreateRootTask task(this, syncable::AUTOFILL_PROFILE);
+ StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- std::vector<AutofillEntry> sync_entries;
std::vector<AutoFillProfile> sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
- ASSERT_EQ(1U, entries.size());
- EXPECT_TRUE(entries[0] == sync_entries[0]);
+ ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(&sync_profiles));
EXPECT_EQ(1U, sync_profiles.size());
EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[0]));
}
-bool ProfilesMatchExceptLabelImpl(AutoFillProfile p1, AutoFillProfile p2) {
- const AutoFillFieldType types[] = { NAME_FIRST,
- NAME_MIDDLE,
- NAME_LAST,
- EMAIL_ADDRESS,
- COMPANY_NAME,
- ADDRESS_HOME_LINE1,
- ADDRESS_HOME_LINE2,
- ADDRESS_HOME_CITY,
- ADDRESS_HOME_STATE,
- ADDRESS_HOME_ZIP,
- ADDRESS_HOME_COUNTRY,
- PHONE_HOME_NUMBER,
- PHONE_FAX_NUMBER };
- if (p1.Label() == p2.Label())
- return false;
-
- for (size_t index = 0; index < arraysize(types); ++index) {
- if (p1.GetFieldText(AutoFillType(types[index])) !=
- p2.GetFieldText(AutoFillType(types[index])))
- return false;
- }
- return true;
-}
-
-MATCHER_P(ProfileMatchesExceptLabel, profile, "") {
- return ProfilesMatchExceptLabelImpl(arg, profile);
-}
-
-TEST_F(ProfileSyncServiceAutofillTest, HasDuplicateProfileLabelsEmptySync) {
- std::vector<AutoFillProfile> expected_profiles;
- std::vector<AutoFillProfile*> profiles;
- AutoFillProfile* profile0 = new AutoFillProfile;
- autofill_test::SetProfileInfo(profile0,
- "Billing", "Marion", "Mitchell", "Morrison",
- "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
- "91601", "US", "12345678910", "01987654321");
- AutoFillProfile* profile1 = new AutoFillProfile;
- autofill_test::SetProfileInfo(profile1,
- "Billing", "Same", "Label", "Morrison",
- "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
- "91601", "US", "12345678910", "01987654321");
- profiles.push_back(profile0);
- profiles.push_back(profile1);
- expected_profiles.push_back(*profile0);
- expected_profiles.push_back(*profile1);
- AutoFillProfile relabelled_profile;
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
- EXPECT_CALL(*personal_data_manager_, Refresh());
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
- WillOnce(DoAll(SetArgumentPointee<0>(profiles), Return(true)));
- EXPECT_CALL(web_database_, UpdateAutoFillProfile(
- ProfileMatchesExceptLabel(expected_profiles[1]))).
- WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true)));
-
- SetIdleChangeProcessorExpectations();
- CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
- ASSERT_TRUE(task.success());
- std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles));
- EXPECT_EQ(0U, sync_entries.size());
- EXPECT_EQ(2U, sync_profiles.size());
- EXPECT_EQ(0, expected_profiles[0].Compare(sync_profiles[1]));
- EXPECT_TRUE(ProfilesMatchExceptLabelImpl(expected_profiles[1],
- sync_profiles[0]));
- EXPECT_EQ(sync_profiles[0].Label(), relabelled_profile.Label());
-}
-
TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) {
// There is buggy autofill code that allows duplicate name/value
// pairs to exist in the database with separate pair_ids.
@@ -638,7 +690,7 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) {
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> sync_entries;
std::vector<AutoFillProfile> sync_profiles;
@@ -649,46 +701,25 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeWithDuplicatesEmptySync) {
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) {
AutofillEntry native_entry(MakeAutofillEntry("native", "entry", 1));
AutofillEntry sync_entry(MakeAutofillEntry("sync", "entry", 2));
- AutoFillProfile sync_profile;
- autofill_test::SetProfileInfo(&sync_profile,
- "Billing", "Marion", "Mitchell", "Morrison",
- "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
- "91601", "US", "12345678910", "01987654321");
-
- AutoFillProfile* native_profile = new AutoFillProfile;
- autofill_test::SetProfileInfo(native_profile,
- "Work", "Josephine", "Alicia", "Saenz",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
std::vector<AutofillEntry> native_entries;
native_entries.push_back(native_entry);
- std::vector<AutoFillProfile*> native_profiles;
- native_profiles.push_back(native_profile);
-
- std::vector<AutoFillProfile> expected_profiles;
- expected_profiles.push_back(*native_profile);
- expected_profiles.push_back(sync_profile);
EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_entries), Return(true)));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
- WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
+
+ EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
+
std::vector<AutofillEntry> sync_entries;
sync_entries.push_back(sync_entry);
- std::vector<AutoFillProfile> sync_profiles;
- sync_profiles.push_back(sync_profile);
- AddAutofillEntriesTask task(this, sync_entries, sync_profiles);
- AutoFillProfile to_be_added(sync_profile);
+ AddAutofillTask<AutofillEntry> task(this, sync_entries);
+
EXPECT_CALL(web_database_, UpdateAutofillEntries(ElementsAre(sync_entry))).
WillOnce(Return(true));
- // TODO(dhollowa): Duplicate removal when contents match but GUIDs don't.
- // http://crbug.com/58813
- EXPECT_CALL(web_database_, AddAutoFillProfile(_)).
- WillOnce(Return(true));
+
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::set<AutofillEntry> expected_entries;
@@ -703,9 +734,6 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncNoMerge) {
new_sync_entries.end());
EXPECT_TRUE(expected_entries == new_sync_entries_set);
- EXPECT_EQ(2U, new_sync_profiles.size());
- EXPECT_EQ(0, expected_profiles[0].Compare(new_sync_profiles[0]));
- EXPECT_EQ(0, expected_profiles[1].Compare(new_sync_profiles[1]));
}
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) {
@@ -720,14 +748,13 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) {
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
sync_entries.push_back(sync_entry);
- AddAutofillEntriesTask task(this, sync_entries, sync_profiles);
+ AddAutofillTask<AutofillEntry> task(this, sync_entries);
EXPECT_CALL(web_database_, UpdateAutofillEntries(ElementsAre(merged_entry))).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
std::vector<AutofillEntry> new_sync_entries;
@@ -740,51 +767,89 @@ TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeEntry) {
TEST_F(ProfileSyncServiceAutofillTest, HasNativeHasSyncMergeProfile) {
AutoFillProfile sync_profile;
- autofill_test::SetProfileInfo(&sync_profile,
- "Billing", "Marion", "Mitchell", "Morrison",
+ 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;
- autofill_test::SetProfileInfo(native_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
+ 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;
native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true));
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- std::vector<AutofillEntry> sync_entries;
std::vector<AutoFillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
- AddAutofillEntriesTask task(this, sync_entries, sync_profiles);
+ AddAutofillTask<AutoFillProfile> task(this, sync_profiles);
- // TODO(dhollowa): Duplicate removal when contents match but GUIDs don't.
- // http://crbug.com/58813
EXPECT_CALL(web_database_, UpdateAutoFillProfile(_)).
WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- std::vector<AutofillEntry> new_sync_entries;
std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
+ ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
+ &new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0]));
}
+TEST_F(ProfileSyncServiceAutofillTest, MergeProfileWithDifferentGuid) {
+ 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");
+
+ string native_guid = "{EDC609ED-7EEE-4f27-B00C-423242A9C44B}";
+ 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;
+ native_profiles.push_back(native_profile);
+ EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
+ WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
+
+ std::vector<AutoFillProfile> sync_profiles;
+ sync_profiles.push_back(sync_profile);
+ AddAutofillTask<AutoFillProfile> task(this, sync_profiles);
+
+ EXPECT_CALL(web_database_, AddAutoFillProfile(_)).
+ WillOnce(Return(true));
+ EXPECT_CALL(web_database_, 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;
+ ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
+ &new_sync_profiles));
+ ASSERT_EQ(1U, new_sync_profiles.size());
+ EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[0]));
+ EXPECT_EQ(sync_profile.guid(), new_sync_profiles[0].guid());
+}
+
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
AutofillEntry added_entry(MakeAutofillEntry("added", "entry", 1));
@@ -809,89 +874,33 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddEntry) {
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfile) {
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
SetIdleChangeProcessorExpectations();
- CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ CreateRootTask task(this, syncable::AUTOFILL_PROFILE);
+ StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
AutoFillProfile added_profile;
- autofill_test::SetProfileInfo(&added_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
+ autofill_test::SetProfileInfoWithGuid(&added_profile,
+ "{D6ADA912-D374-4c0a-917D-F5C8EBE43011}", "Josephine", "Alicia", "Saenz",
"joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
"32801", "US", "19482937549", "13502849239");
- AutofillProfileChange change(AutofillProfileChange::ADD,
- added_profile.Label(), &added_profile, string16());
+ AutofillProfileChangeGUID change(AutofillProfileChangeGUID::ADD,
+ added_profile.guid(), &added_profile);
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
- notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
+ notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED_GUID,
Source<WebDataService>(web_data_service_.get()),
- Details<AutofillProfileChange>(&change));
+ Details<AutofillProfileChangeGUID>(&change));
- std::vector<AutofillEntry> new_sync_entries;
std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
+ ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
+ &new_sync_profiles));
ASSERT_EQ(1U, new_sync_profiles.size());
EXPECT_EQ(0, added_profile.Compare(new_sync_profiles[0]));
}
-TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeAddProfileConflict) {
- AutoFillProfile sync_profile;
- autofill_test::SetProfileInfo(&sync_profile,
- "Billing", "Marion", "Mitchell", "Morrison",
- "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
- "91601", "US", "12345678910", "01987654321");
-
- std::vector<AutofillEntry> sync_entries;
- std::vector<AutoFillProfile> sync_profiles;
- sync_profiles.push_back(sync_profile);
- AddAutofillEntriesTask task(this, sync_entries, sync_profiles);
-
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)).WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
- // TODO(dhollowa): Duplicate removal when contents match but GUIDs don't.
- // http://crbug.com/58813
- EXPECT_CALL(web_database_, AddAutoFillProfile(_)).
- WillOnce(Return(true));
- EXPECT_CALL(*personal_data_manager_, Refresh());
- SetIdleChangeProcessorExpectations();
- StartSyncService(&task, false);
- ASSERT_TRUE(task.success());
-
- AutoFillProfile added_profile;
- autofill_test::SetProfileInfo(&added_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
-
- AutofillProfileChange change(AutofillProfileChange::ADD,
- added_profile.Label(), &added_profile, string16());
-
- AutoFillProfile relabelled_profile;
- EXPECT_CALL(web_database_, UpdateAutoFillProfile(
- ProfileMatchesExceptLabel(added_profile))).
- WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true)));
- EXPECT_CALL(*personal_data_manager_, Refresh());
-
- scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
- notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
- Source<WebDataService>(web_data_service_.get()),
- Details<AutofillProfileChange>(&change));
-
- std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
- ASSERT_EQ(2U, new_sync_profiles.size());
- EXPECT_EQ(0, sync_profile.Compare(new_sync_profiles[1]));
- EXPECT_TRUE(ProfilesMatchExceptLabelImpl(added_profile,
- new_sync_profiles[0]));
- EXPECT_EQ(new_sync_profiles[0].Label(), relabelled_profile.Label());
-}
-
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1));
std::vector<AutofillEntry> original_entries;
@@ -902,7 +911,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
AutofillEntry updated_entry(MakeAutofillEntry("my", "entry", 1, 2));
@@ -928,147 +937,6 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateEntry) {
}
-TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfile) {
- AutoFillProfile* native_profile = new AutoFillProfile;
- autofill_test::SetProfileInfo(native_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
- std::vector<AutoFillProfile*> native_profiles;
- native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
- WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- EXPECT_CALL(*personal_data_manager_, Refresh());
- CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
- ASSERT_TRUE(task.success());
-
- AutoFillProfile update_profile;
- autofill_test::SetProfileInfo(&update_profile,
- "Billing", "Changin'", "Mah", "Namez",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
-
- AutofillProfileChange change(AutofillProfileChange::UPDATE,
- update_profile.Label(), &update_profile,
- ASCIIToUTF16("Billing"));
- scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
- notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
- Source<WebDataService>(web_data_service_.get()),
- Details<AutofillProfileChange>(&change));
-
- std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
- ASSERT_EQ(1U, new_sync_profiles.size());
- EXPECT_EQ(0, update_profile.Compare(new_sync_profiles[0]));
-}
-
-TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeUpdateProfileRelabel) {
- AutoFillProfile* native_profile = new AutoFillProfile;
- autofill_test::SetProfileInfo(native_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
- std::vector<AutoFillProfile*> native_profiles;
- native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
- WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- EXPECT_CALL(*personal_data_manager_, Refresh());
- CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
- ASSERT_TRUE(task.success());
-
- AutoFillProfile update_profile;
- autofill_test::SetProfileInfo(&update_profile,
- "TRYIN 2 FOOL U", "Josephine", "Alicia", "Saenz",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
-
- AutofillProfileChange change(AutofillProfileChange::UPDATE,
- update_profile.Label(), &update_profile,
- ASCIIToUTF16("Billing"));
- scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
- notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
- Source<WebDataService>(web_data_service_.get()),
- Details<AutofillProfileChange>(&change));
-
- std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
- ASSERT_EQ(1U, new_sync_profiles.size());
- EXPECT_EQ(0, update_profile.Compare(new_sync_profiles[0]));
-}
-
-TEST_F(ProfileSyncServiceAutofillTest,
- ProcessUserChangeUpdateProfileRelabelConflict) {
- std::vector<AutoFillProfile*> native_profiles;
- native_profiles.push_back(new AutoFillProfile);
- native_profiles.push_back(new AutoFillProfile);
- autofill_test::SetProfileInfo(native_profiles[0],
- "Billing", "Josephine", "Alicia", "Saenz",
- "joewayne@me.xyz", "Fox", "1212 Center.", "Bld. 5", "Orlando", "FL",
- "32801", "US", "19482937549", "13502849239");
- autofill_test::SetProfileInfo(native_profiles[1],
- "ExistingLabel", "Marion", "Mitchell", "Morrison",
- "johnwayne@me.xyz", "Fox", "123 Zoo St.", "unit 5", "Hollywood", "CA",
- "91601", "US", "12345678910", "01987654321");
- AutoFillProfile marion(*native_profiles[1]);
- AutoFillProfile josephine(*native_profiles[0]);
-
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true));
- EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
- WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- EXPECT_CALL(*personal_data_manager_, Refresh());
- CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
- ASSERT_TRUE(task.success());
- MessageLoop::current()->RunAllPending();
- Mock::VerifyAndClearExpectations(&web_database_);
- native_profiles.clear(); // Contents freed.
-
- // Update josephine twice with marion's label. The second time ought to be
- // idempotent, settling on the same name and not triggering a sync upload.
- for (int pass = 0; pass < 2; ++pass) {
- AutoFillProfile josephine_update(josephine);
- // TODO(dhollowa): Replace with |AutoFillProfile::set_guid|.
- // http://crbug.com/58813
- josephine_update.set_label(ASCIIToUTF16("ExistingLabel"));
-
- AutoFillProfile relabelled_profile;
- EXPECT_CALL(web_database_, UpdateAutoFillProfile(
- ProfileMatchesExceptLabel(josephine_update))).
- WillOnce(DoAll(SaveArg<0>(&relabelled_profile), Return(true)));
- EXPECT_CALL(*personal_data_manager_, Refresh());
-
- AutofillProfileChange change(AutofillProfileChange::UPDATE,
- josephine_update.Label(), &josephine_update,
- josephine.Label());
- scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
- notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
- Source<WebDataService>(web_data_service_.get()),
- Details<AutofillProfileChange>(&change));
- MessageLoop::current()->RunAllPending(); // Run the Refresh task.
- Mock::VerifyAndClearExpectations(&web_database_);
-
- std::vector<AutofillEntry> new_sync_entries;
- std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
- ASSERT_EQ(2U, new_sync_profiles.size());
- EXPECT_EQ(0, marion.Compare(new_sync_profiles[1]));
- EXPECT_TRUE(ProfilesMatchExceptLabelImpl(josephine_update,
- new_sync_profiles[0]));
- EXPECT_EQ(ASCIIToUTF16("ExistingLabel2"), new_sync_profiles[0].Label());
- EXPECT_EQ(ASCIIToUTF16("ExistingLabel2"), relabelled_profile.Label());
- josephine = relabelled_profile;
- }
-}
-
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) {
AutofillEntry original_entry(MakeAutofillEntry("my", "entry", 1));
std::vector<AutofillEntry> original_entries;
@@ -1079,7 +947,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) {
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
AutofillChangeList changes;
@@ -1099,42 +967,39 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveEntry) {
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeRemoveProfile) {
AutoFillProfile sync_profile;
- autofill_test::SetProfileInfo(&sync_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
+ 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;
- autofill_test::SetProfileInfo(native_profile,
- "Billing", "Josephine", "Alicia", "Saenz",
+ 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;
native_profiles.push_back(native_profile);
- EXPECT_CALL(web_database_, GetAllAutofillEntries(_)). WillOnce(Return(true));
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).
WillOnce(DoAll(SetArgumentPointee<0>(native_profiles), Return(true)));
- std::vector<AutofillEntry> sync_entries;
std::vector<AutoFillProfile> sync_profiles;
sync_profiles.push_back(sync_profile);
- AddAutofillEntriesTask task(this, sync_entries, sync_profiles);
+ AddAutofillTask<AutoFillProfile> task(this, sync_profiles);
EXPECT_CALL(*personal_data_manager_, Refresh());
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL_PROFILE);
ASSERT_TRUE(task.success());
- AutofillProfileChange change(AutofillProfileChange::REMOVE,
- sync_profile.Label(), NULL, string16());
+ AutofillProfileChangeGUID change(AutofillProfileChangeGUID::REMOVE,
+ sync_profile.guid(), NULL);
scoped_refptr<ThreadNotifier> notifier(new ThreadNotifier(&db_thread_));
- notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED,
+ notifier->Notify(NotificationType::AUTOFILL_PROFILE_CHANGED_GUID,
Source<WebDataService>(web_data_service_.get()),
- Details<AutofillProfileChange>(&change));
+ Details<AutofillProfileChangeGUID>(&change));
- std::vector<AutofillEntry> new_sync_entries;
std::vector<AutoFillProfile> new_sync_profiles;
- ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&new_sync_entries,
- &new_sync_profiles));
- ASSERT_EQ(0U, new_sync_entries.size());
+ ASSERT_TRUE(GetAutofillProfilesFromSyncDBUnderProfileNode(
+ &new_sync_profiles));
+ ASSERT_EQ(0U, new_sync_profiles.size());
}
TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) {
@@ -1142,7 +1007,7 @@ TEST_F(ProfileSyncServiceAutofillTest, ProcessUserChangeError) {
EXPECT_CALL(web_database_, GetAutoFillProfiles(_)).WillOnce(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh());
CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
// Inject an evil entry into the sync db to conflict with the same
@@ -1179,7 +1044,7 @@ TEST_F(ProfileSyncServiceAutofillTest, DISABLED_ServerChangeRace) {
WillRepeatedly(Return(true));
EXPECT_CALL(*personal_data_manager_, Refresh()).Times(3);
CreateRootTask task(this, syncable::AUTOFILL);
- StartSyncService(&task, false);
+ StartSyncService(&task, false, syncable::AUTOFILL);
ASSERT_TRUE(task.success());
// (true, false) means we have to reset after |Signal|, init to unsignaled.
diff --git a/chrome/browser/sync/profile_sync_service_harness.cc b/chrome/browser/sync/profile_sync_service_harness.cc
index d275147..14fba6e 100644
--- a/chrome/browser/sync/profile_sync_service_harness.cc
+++ b/chrome/browser/sync/profile_sync_service_harness.cc
@@ -2,12 +2,16 @@
// 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 <algorithm>
+#include <vector>
+
#include "base/message_loop.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/sync_backend_host.h"
-#include "chrome/browser/sync/profile_sync_service_harness.h"
#include "chrome/browser/sync/sessions/session_state.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/net/gaia/gaia_constants.h"
@@ -90,8 +94,7 @@ ProfileSyncServiceHarness::ProfileSyncServiceHarness(
: wait_state_(INITIAL_WAIT_STATE),
profile_(profile),
service_(NULL),
- last_timestamp_(0),
- min_timestamp_needed_(kMinTimestampNeededNone),
+ timestamp_match_partner_(NULL),
username_(username),
password_(password),
id_(id) {
@@ -263,11 +266,14 @@ bool ProfileSyncServiceHarness::RunStateChangeMachine() {
}
case WAITING_FOR_UPDATES: {
LogClientInfo("WAITING_FOR_UPDATES");
- if (!IsSynced() || GetUpdatedTimestamp() < min_timestamp_needed_) {
- // The client is not yet fully synced. Continue waiting until the client
- // is at the required minimum timestamp.
+ DCHECK(timestamp_match_partner_);
+ if (!MatchesOtherClient(timestamp_match_partner_)) {
+ // The client is not yet fully synced; keep waiting until we converge.
break;
}
+ timestamp_match_partner_->service()->RemoveObserver(this);
+ timestamp_match_partner_ = NULL;
+
SignalStateCompleteWithNextState(FULLY_SYNCED);
break;
}
@@ -353,7 +359,7 @@ bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
LogClientInfo("AwaitMutualSyncCycleCompletion");
if (!AwaitSyncCycleCompletion("Sync cycle completion on active client."))
return false;
- return partner->WaitUntilTimestampIsAtLeast(last_timestamp_,
+ return partner->WaitUntilTimestampMatches(this,
"Sync cycle completion on passive client.");
}
@@ -367,7 +373,7 @@ bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
partners.begin(); it != partners.end(); ++it) {
if ((this != *it) && ((*it)->wait_state_ != SYNC_DISABLED)) {
return_value = return_value &&
- (*it)->WaitUntilTimestampIsAtLeast(last_timestamp_,
+ (*it)->WaitUntilTimestampMatches(this,
"Sync cycle completion on partner client.");
}
}
@@ -388,20 +394,21 @@ bool ProfileSyncServiceHarness::AwaitQuiescence(
return return_value;
}
-bool ProfileSyncServiceHarness::WaitUntilTimestampIsAtLeast(
- int64 timestamp, const std::string& reason) {
- LogClientInfo("WaitUntilTimestampIsAtLeast");
+bool ProfileSyncServiceHarness::WaitUntilTimestampMatches(
+ ProfileSyncServiceHarness* partner, const std::string& reason) {
+ LogClientInfo("WaitUntilTimestampMatches");
if (wait_state_ == SYNC_DISABLED) {
LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
return false;
}
- min_timestamp_needed_ = timestamp;
- if (GetUpdatedTimestamp() < min_timestamp_needed_) {
- wait_state_ = WAITING_FOR_UPDATES;
- return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
- } else {
+ DCHECK(!timestamp_match_partner_);
+ if (MatchesOtherClient(partner))
return true;
- }
+
+ timestamp_match_partner_ = partner;
+ partner->service()->AddObserver(this);
+ wait_state_ = WAITING_FOR_UPDATES;
+ return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
}
bool ProfileSyncServiceHarness::AwaitStatusChangeWithTimeout(
@@ -424,8 +431,13 @@ bool ProfileSyncServiceHarness::AwaitStatusChangeWithTimeout(
timeout_milliseconds);
loop->Run();
loop->SetNestableTasksAllowed(did_allow_nestable_tasks);
- LogClientInfo("AwaitStatusChangeWithTimeout succeeded");
- return timeout_signal->Abort();
+ if (timeout_signal->Abort()) {
+ LogClientInfo("AwaitStatusChangeWithTimeout succeeded");
+ return true;
+ } else {
+ LogClientInfo("AwaitStatusChangeWithTimeout timed out");
+ return false;
+ }
}
ProfileSyncService::Status ProfileSyncServiceHarness::GetStatus() {
@@ -449,6 +461,27 @@ bool ProfileSyncServiceHarness::IsSynced() {
snap->unsynced_count == 0);
}
+bool ProfileSyncServiceHarness::MatchesOtherClient(
+ ProfileSyncServiceHarness* partner) {
+ if (!IsSynced())
+ return false;
+
+ // Only look for a match if we have at least one enabled datatype in
+ // common with the partner client.
+ syncable::ModelTypeSet types, other_types, intersection_types;
+ service()->GetPreferredDataTypes(&types);
+ partner->service()->GetPreferredDataTypes(&other_types);
+ std::set_intersection(types.begin(), types.end(), other_types.begin(),
+ other_types.end(),
+ inserter(intersection_types,
+ intersection_types.begin()));
+ if (intersection_types.empty()) {
+ return true;
+ }
+ return partner->IsSynced() &&
+ partner->GetUpdatedTimestamp() == GetUpdatedTimestamp();
+}
+
const SyncSessionSnapshot*
ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
DCHECK(service_ != NULL) << "Sync service has not yet been set up.";
@@ -539,9 +572,7 @@ void ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
int64 ProfileSyncServiceHarness::GetUpdatedTimestamp() {
const SyncSessionSnapshot* snap = GetLastSessionSnapshot();
DCHECK(snap != NULL) << "GetUpdatedTimestamp(): Sync snapshot is NULL.";
- DCHECK_LE(last_timestamp_, snap->max_local_timestamp);
- last_timestamp_ = snap->max_local_timestamp;
- return last_timestamp_;
+ return snap->max_local_timestamp;
}
void ProfileSyncServiceHarness::LogClientInfo(std::string message) {
diff --git a/chrome/browser/sync/profile_sync_service_harness.h b/chrome/browser/sync/profile_sync_service_harness.h
index e3047af..064afe1 100644
--- a/chrome/browser/sync/profile_sync_service_harness.h
+++ b/chrome/browser/sync/profile_sync_service_harness.h
@@ -59,8 +59,9 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
bool AwaitSyncCycleCompletion(const std::string& reason);
// Blocks the caller until this harness has observed that the sync engine
- // has "synced" up to at least the specified local timestamp.
- bool WaitUntilTimestampIsAtLeast(int64 timestamp, const std::string& reason);
+ // has downloaded all the changes seen by the |partner| harness's client.
+ bool WaitUntilTimestampMatches(
+ ProfileSyncServiceHarness* partner, const std::string& reason);
// Calling this acts as a barrier and blocks the caller until |this| and
// |partner| have both completed a sync cycle. When calling this method,
@@ -72,10 +73,9 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
bool AwaitMutualSyncCycleCompletion(ProfileSyncServiceHarness* partner);
// Blocks the caller until |this| completes its ongoing sync cycle and every
- // other client in |partners| has a timestamp that is greater than or equal to
- // the timestamp of |this|. Note: Use this method when exactly one client
- // makes local change(s), and more than one client is waiting to receive those
- // changes.
+ // other client in |partners| have achieved identical download progresses.
+ // Note: Use this method when exactly one client makes local change(s),
+ // and more than one client is waiting to receive those changes.
bool AwaitGroupSyncCycleCompletion(
std::vector<ProfileSyncServiceHarness*>& partners);
@@ -168,11 +168,14 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
// Returns true if the sync client has no unsynced items.
bool IsSynced();
+ // Returns true if this client has downloaded all the items that the
+ // other client has.
+ bool MatchesOtherClient(ProfileSyncServiceHarness* partner);
+
// Logs message with relevant info about client's sync state (if available).
void LogClientInfo(std::string message);
- // Updates |last_timestamp_| with the timestamp of the current sync session.
- // Returns the new value of |last_timestamp_|.
+ // Gets the current progress indicator of the current sync session.
int64 GetUpdatedTimestamp();
WaitState wait_state_;
@@ -180,15 +183,9 @@ class ProfileSyncServiceHarness : public ProfileSyncServiceObserver {
Profile* profile_;
ProfileSyncService* service_;
- // This value tracks the max sync timestamp (e.g. synced-to revision) inside
- // the sync engine. It gets updated when a sync cycle ends and the session
- // snapshot implies syncing is "done".
- int64 last_timestamp_;
-
- // The minimum value of the 'max_local_timestamp' member of a
- // SyncSessionSnapshot we need to wait to observe in OnStateChanged when told
- // to WaitUntilTimestampIsAtLeast(...).
- int64 min_timestamp_needed_;
+ // The harness of the client whose update progress marker we're expecting
+ // eventually match.
+ ProfileSyncServiceHarness* timestamp_match_partner_;
// Credentials used for GAIA authentication.
std::string username_;
diff --git a/chrome/browser/sync/profile_sync_service_mock.cc b/chrome/browser/sync/profile_sync_service_mock.cc
new file mode 100644
index 0000000..8bfdb10
--- /dev/null
+++ b/chrome/browser/sync/profile_sync_service_mock.cc
@@ -0,0 +1,9 @@
+// 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_mock.h"
+
+ProfileSyncServiceMock::ProfileSyncServiceMock() {}
+
+ProfileSyncServiceMock::~ProfileSyncServiceMock() {}
diff --git a/chrome/browser/sync/profile_sync_service_mock.h b/chrome/browser/sync/profile_sync_service_mock.h
index a338631..32bff66 100644
--- a/chrome/browser/sync/profile_sync_service_mock.h
+++ b/chrome/browser/sync/profile_sync_service_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.
@@ -17,8 +17,8 @@
class ProfileSyncServiceMock : public ProfileSyncService {
public:
- ProfileSyncServiceMock() {}
- virtual ~ProfileSyncServiceMock() {}
+ ProfileSyncServiceMock();
+ virtual ~ProfileSyncServiceMock();
MOCK_METHOD0(DisableForUser, void());
MOCK_METHOD0(OnBackendInitialized, void());
diff --git a/chrome/browser/sync/profile_sync_service_session_unittest.cc b/chrome/browser/sync/profile_sync_service_session_unittest.cc
index 6242f0e..7f90cd6 100644
--- a/chrome/browser/sync/profile_sync_service_session_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_session_unittest.cc
@@ -170,16 +170,9 @@ TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) {
bool has_nodes;
ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
ASSERT_TRUE(has_nodes);
- ASSERT_TRUE(model_associator_->ChromeModelHasUserCreatedNodes(&has_nodes));
- ASSERT_TRUE(has_nodes);
std::string machine_tag = model_associator_->GetCurrentMachineTag();
- int64 sync_id;
- ASSERT_TRUE(model_associator_->GetSyncIdForTaggedNode(&machine_tag,
- &sync_id));
- ASSERT_EQ(model_associator_->GetSyncIdFromChromeId(machine_tag), sync_id);
- scoped_ptr<const sync_pb::SessionSpecifics> sync_specifics(
- model_associator_->GetChromeNodeFromSyncId(sync_id));
- ASSERT_TRUE(sync_specifics != NULL);
+ int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
+ ASSERT_NE(sync_api::kInvalidId, sync_id);
// Check that we can get the correct session specifics back from the node.
sync_api::ReadTransaction trans(sync_service_->
@@ -188,8 +181,10 @@ TEST_F(ProfileSyncServiceSessionTest, WriteSessionToNode) {
ASSERT_TRUE(node.InitByClientTagLookup(syncable::SESSIONS,
machine_tag));
const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics());
- ASSERT_EQ(sync_specifics->session_tag(), specifics.session_tag());
ASSERT_EQ(machine_tag, specifics.session_tag());
+ ASSERT_TRUE(specifics.has_header());
+ const sync_pb::SessionHeader& header_s = specifics.header();
+ ASSERT_EQ(0, header_s.window_size());
}
// Test that we can fill this machine's session, write it to a node,
@@ -208,51 +203,35 @@ TEST_F(ProfileSyncServiceSessionTest, WriteFilledSessionToNode) {
AddTab(browser(), GURL("http://bar/1"));
NavigateAndCommitActiveTab(GURL("http://bar/2"));
- // Report a saved session, thus causing the ChangeProcessor to write to a
- // node.
- NotificationService::current()->Notify(
- NotificationType::SESSION_SERVICE_SAVED,
- Source<Profile>(profile()),
- NotificationService::NoDetails());
+ ASSERT_TRUE(model_associator_->SyncModelHasUserCreatedNodes(&has_nodes));
+ ASSERT_TRUE(has_nodes);
std::string machine_tag = model_associator_->GetCurrentMachineTag();
- int64 sync_id;
- ASSERT_TRUE(model_associator_->GetSyncIdForTaggedNode(&machine_tag,
- &sync_id));
- ASSERT_EQ(model_associator_->GetSyncIdFromChromeId(machine_tag), sync_id);
- scoped_ptr<const sync_pb::SessionSpecifics> sync_specifics(
- model_associator_->GetChromeNodeFromSyncId(sync_id));
- ASSERT_TRUE(sync_specifics != NULL);
+ int64 sync_id = model_associator_->GetSyncIdFromSessionTag(machine_tag);
+ ASSERT_NE(sync_api::kInvalidId, sync_id);
// Check that this machine's data is not included in the foreign windows.
- ScopedVector<ForeignSession> foreign_sessions;
- model_associator_->GetSessionData(&foreign_sessions.get());
+ std::vector<const ForeignSession*> foreign_sessions;
+ model_associator_->GetAllForeignSessions(&foreign_sessions);
ASSERT_EQ(foreign_sessions.size(), 0U);
- // Get the windows for this machine from the node and check that they were
+ // Get the tabs for this machine from the node and check that they were
// filled.
- sync_api::ReadTransaction trans(sync_service_->
- backend()->GetUserShareHandle());
- sync_api::ReadNode node(&trans);
- ASSERT_TRUE(node.InitByClientTagLookup(syncable::SESSIONS,
- machine_tag));
- model_associator_->AppendForeignSessionWithID(sync_id,
- &foreign_sessions.get(), &trans);
- ASSERT_EQ(foreign_sessions.size(), 1U);
- ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
- ASSERT_EQ(2U, foreign_sessions[0]->windows[0]->tabs.size());
- ASSERT_EQ(2U, foreign_sessions[0]->windows[0]->tabs[0]->navigations.size());
- ASSERT_EQ(GURL("http://bar/1"),
- foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].virtual_url());
- ASSERT_EQ(GURL("http://bar/2"),
- foreign_sessions[0]->windows[0]->tabs[0]->navigations[1].virtual_url());
- ASSERT_EQ(2U, foreign_sessions[0]->windows[0]->tabs[1]->navigations.size());
- ASSERT_EQ(GURL("http://foo/1"),
- foreign_sessions[0]->windows[0]->tabs[1]->navigations[0].virtual_url());
- ASSERT_EQ(GURL("http://foo/2"),
- foreign_sessions[0]->windows[0]->tabs[1]->navigations[1].virtual_url());
- const sync_pb::SessionSpecifics& specifics(node.GetSessionSpecifics());
- ASSERT_EQ(sync_specifics->session_tag(), specifics.session_tag());
- ASSERT_EQ(machine_tag, specifics.session_tag());
+ SessionModelAssociator::TabLinksMap tab_map = model_associator_->tab_map_;
+ ASSERT_EQ(2U, tab_map.size());
+ // Tabs are ordered by sessionid in tab_map, so should be able to traverse
+ // the tree based on order of tabs created
+ SessionModelAssociator::TabLinksMap::iterator iter = tab_map.begin();
+ ASSERT_EQ(2, iter->second.tab()->controller().entry_count());
+ ASSERT_EQ(GURL("http://foo/1"), iter->second.tab()->controller().
+ GetEntryAtIndex(0)->virtual_url());
+ ASSERT_EQ(GURL("http://foo/2"), iter->second.tab()->controller().
+ GetEntryAtIndex(1)->virtual_url());
+ iter++;
+ ASSERT_EQ(2, iter->second.tab()->controller().entry_count());
+ ASSERT_EQ(GURL("http://bar/1"), iter->second.tab()->controller().
+ GetEntryAtIndex(0)->virtual_url());
+ ASSERT_EQ(GURL("http://bar/2"), iter->second.tab()->controller().
+ GetEntryAtIndex(1)->virtual_url());
}
// Test that we fail on a failed model association.
@@ -273,13 +252,18 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
ASSERT_TRUE(has_nodes);
// Fill an instance of session specifics with a foreign session's data.
- sync_pb::SessionSpecifics specifics;
+ sync_pb::SessionSpecifics meta_specifics;
std::string machine_tag = "session_sync123";
- specifics.set_session_tag(machine_tag);
- sync_pb::SessionWindow* window = specifics.add_session_window();
- window->set_selected_tab_index(1);
- window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
- sync_pb::SessionTab* tab = window->add_session_tab();
+ meta_specifics.set_session_tag(machine_tag);
+ sync_pb::SessionHeader* header_s = meta_specifics.mutable_header();
+ sync_pb::SessionWindow* window_s = header_s->add_window();
+ window_s->add_tab(0);
+ window_s->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_NORMAL);
+ window_s->set_selected_tab_index(1);
+
+ sync_pb::SessionSpecifics tab_specifics;
+ tab_specifics.set_session_tag(machine_tag);
+ sync_pb::SessionTab* tab = tab_specifics.mutable_tab();
tab->set_tab_visual_index(13);
tab->set_current_navigation_index(3);
tab->set_pinned(true);
@@ -293,25 +277,15 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
// Update the server with the session specifics.
{
- sync_api::WriteTransaction trans(sync_service_->
- backend()->GetUserShareHandle());
- sync_api::ReadNode root(&trans);
- ASSERT_TRUE(root.InitByTagLookup(kSessionsTag));
- model_associator_->UpdateSyncModel(&specifics, &trans, &root);
- model_associator_->UpdateFromSyncModel(&trans);
+ model_associator_->AssociateForeignSpecifics(meta_specifics, 0);
+ model_associator_->AssociateForeignSpecifics(tab_specifics, 0);
}
- // Check that the foreign session was written to a node and retrieve the data.
- int64 sync_id;
- ASSERT_TRUE(model_associator_->GetSyncIdForTaggedNode(&machine_tag,
- &sync_id));
- ASSERT_EQ(model_associator_->GetSyncIdFromChromeId(machine_tag), sync_id);
- scoped_ptr<const sync_pb::SessionSpecifics> sync_specifics(
- model_associator_->GetChromeNodeFromSyncId(sync_id));
- ASSERT_TRUE(sync_specifics != NULL);
- ScopedVector<ForeignSession> foreign_sessions;
- model_associator_->GetSessionData(&foreign_sessions.get());
+ // Check that the foreign session was associated and retrieve the data.
+ std::vector<const ForeignSession*> foreign_sessions;
+ model_associator_->GetAllForeignSessions(&foreign_sessions);
ASSERT_EQ(1U, foreign_sessions.size());
+ ASSERT_EQ(machine_tag, foreign_sessions[0]->foreign_session_tag);
ASSERT_EQ(1U, foreign_sessions[0]->windows.size());
ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs.size());
ASSERT_EQ(1U, foreign_sessions[0]->windows[0]->tabs[0]->navigations.size());
@@ -334,14 +308,6 @@ TEST_F(ProfileSyncServiceSessionTest, WriteForeignSessionToNode) {
foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].transition());
ASSERT_EQ(GURL("http://foo/1"),
foreign_sessions[0]->windows[0]->tabs[0]->navigations[0].virtual_url());
- sync_api::WriteTransaction trans(sync_service_->
- backend()->GetUserShareHandle());
- sync_api::ReadNode node(&trans);
- ASSERT_TRUE(node.InitByClientTagLookup(syncable::SESSIONS,
- machine_tag));
- const sync_pb::SessionSpecifics& specifics_(node.GetSessionSpecifics());
- ASSERT_EQ(sync_specifics->session_tag(), specifics_.session_tag());
- ASSERT_EQ(machine_tag, specifics_.session_tag());
}
// Test the DataTypeController on update.
@@ -349,7 +315,7 @@ TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionUpdate) {
CreateRootTask task(this);
ASSERT_TRUE(StartSyncService(&task, false));
ASSERT_TRUE(task.success());
- int64 node_id = model_associator_->GetSyncIdFromChromeId(
+ int64 node_id = model_associator_->GetSyncIdFromSessionTag(
model_associator_->GetCurrentMachineTag());
scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord);
record->action = SyncManager::ChangeRecord::ACTION_UPDATE;
@@ -368,7 +334,7 @@ TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionAdd) {
ASSERT_TRUE(StartSyncService(&task, false));
ASSERT_TRUE(task.success());
- int64 node_id = model_associator_->GetSyncIdFromChromeId(
+ int64 node_id = model_associator_->GetSyncIdFromSessionTag(
model_associator_->GetCurrentMachineTag());
scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord);
record->action = SyncManager::ChangeRecord::ACTION_ADD;
@@ -387,7 +353,7 @@ TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) {
ASSERT_TRUE(StartSyncService(&task, false));
ASSERT_TRUE(task.success());
- int64 node_id = model_associator_->GetSyncIdFromChromeId(
+ int64 node_id = model_associator_->GetSyncIdFromSessionTag(
model_associator_->GetCurrentMachineTag());
scoped_ptr<SyncManager::ChangeRecord> record(new SyncManager::ChangeRecord);
record->action = SyncManager::ChangeRecord::ACTION_DELETE;
@@ -399,6 +365,64 @@ TEST_F(ProfileSyncServiceSessionTest, UpdatedSyncNodeActionDelete) {
}
ASSERT_TRUE(notified_of_update_);
}
+// Test the TabNodePool when it starts off empty.
+TEST_F(ProfileSyncServiceSessionTest, TabNodePoolEmpty) {
+ CreateRootTask task(this);
+ ASSERT_TRUE(StartSyncService(&task, false));
+ ASSERT_TRUE(task.success());
+
+ std::vector<int64> node_ids;
+ ASSERT_EQ(0U, model_associator_->tab_pool_.capacity());
+ ASSERT_TRUE(model_associator_->tab_pool_.empty());
+ ASSERT_TRUE(model_associator_->tab_pool_.full());
+ const size_t num_ids = 10;
+ for (size_t i = 0; i < num_ids; ++i) {
+ int64 id = model_associator_->tab_pool_.GetFreeTabNode();
+ ASSERT_GT(id, -1);
+ node_ids.push_back(id);
+ }
+ ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
+ ASSERT_TRUE(model_associator_->tab_pool_.empty());
+ ASSERT_FALSE(model_associator_->tab_pool_.full());
+ for (size_t i = 0; i < num_ids; ++i) {
+ model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
+ }
+ ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
+ ASSERT_FALSE(model_associator_->tab_pool_.empty());
+ ASSERT_TRUE(model_associator_->tab_pool_.full());
+}
+
+// Test the TabNodePool when it starts off with nodes
+TEST_F(ProfileSyncServiceSessionTest, TabNodePoolNonEmpty) {
+ CreateRootTask task(this);
+ ASSERT_TRUE(StartSyncService(&task, false));
+ ASSERT_TRUE(task.success());
+
+ const size_t num_starting_nodes = 3;
+ for (size_t i = 0; i < num_starting_nodes; ++i) {
+ model_associator_->tab_pool_.AddTabNode(i);
+ }
+
+ std::vector<int64> node_ids;
+ ASSERT_EQ(num_starting_nodes, model_associator_->tab_pool_.capacity());
+ ASSERT_FALSE(model_associator_->tab_pool_.empty());
+ ASSERT_TRUE(model_associator_->tab_pool_.full());
+ const size_t num_ids = 10;
+ for (size_t i = 0; i < num_ids; ++i) {
+ int64 id = model_associator_->tab_pool_.GetFreeTabNode();
+ ASSERT_GT(id, -1);
+ node_ids.push_back(id);
+ }
+ ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
+ ASSERT_TRUE(model_associator_->tab_pool_.empty());
+ ASSERT_FALSE(model_associator_->tab_pool_.full());
+ for (size_t i = 0; i < num_ids; ++i) {
+ model_associator_->tab_pool_.FreeTabNode(node_ids[i]);
+ }
+ ASSERT_EQ(num_ids, model_associator_->tab_pool_.capacity());
+ ASSERT_FALSE(model_associator_->tab_pool_.empty());
+ ASSERT_TRUE(model_associator_->tab_pool_.full());
+}
} // namespace browser_sync
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 17795c6..98b70c0 100644
--- a/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
+++ b/chrome/browser/sync/profile_sync_service_typed_url_unittest.cc
@@ -8,7 +8,7 @@
#include "base/ref_counted.h"
#include "base/string16.h"
-#include "base/thread.h"
+#include "base/threading/thread.h"
#include "base/time.h"
#include "chrome/browser/history/history_backend.h"
#include "chrome/browser/history/history_notifications.h"
diff --git a/chrome/browser/sync/profile_sync_test_util.h b/chrome/browser/sync/profile_sync_test_util.h
index f6e66ac..4e28efa 100644
--- a/chrome/browser/sync/profile_sync_test_util.h
+++ b/chrome/browser/sync/profile_sync_test_util.h
@@ -13,8 +13,8 @@
#include "base/ref_counted.h"
#include "base/scoped_ptr.h"
#include "base/task.h"
-#include "base/thread.h"
-#include "base/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/synchronization/waitable_event.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/webdata/web_database.h"
diff --git a/chrome/browser/sync/protocol/session_specifics.proto b/chrome/browser/sync/protocol/session_specifics.proto
index 050687d..5b00b35 100644
--- a/chrome/browser/sync/protocol/session_specifics.proto
+++ b/chrome/browser/sync/protocol/session_specifics.proto
@@ -13,14 +13,20 @@ package sync_pb;
import "sync.proto";
-// Properties of session sync objects.
message SessionSpecifics {
- // Unique id for the session.
+ // Unique id for the client.
optional string session_tag = 1;
+ optional SessionHeader header = 2;
+ optional SessionTab tab = 3;
+}
+// Properties of session sync objects.
+message SessionHeader {
// Each session is composed of windows.
- repeated SessionWindow session_window = 2;
+ repeated SessionWindow window = 2;
}
message SessionWindow {
+ // Unique (to the owner) id for this window.
+ optional int32 window_id = 1;
// Index of the selected tab in tabs; -1 if no tab is selected.
optional int32 selected_tab_index = 2 [default = -1];
// Type of the browser. Currently we only store browsers of type
@@ -30,22 +36,26 @@ message SessionWindow {
TYPE_POPUP = 2;
}
optional BrowserType browser_type = 3 [default = TYPE_NORMAL];
- // The tabs that compose a window.
- repeated SessionTab session_tab= 4;
+ // The tabs that compose a window (correspond to tab id's).
+ repeated int32 tab = 4;
}
message SessionTab {
+ // Unique (to the owner) id for this tab.
+ optional int32 tab_id = 1;
+ // The unique id for the window this tab belongs to.
+ optional int32 window_id = 2;
// Visual index of the tab within its window. There may be gaps in these
// values.
- optional int32 tab_visual_index = 2 [default = -1];
+ optional int32 tab_visual_index = 3 [default = -1];
// Identifies the index of the current navigation in navigations. For
// example, if this is 2 it means the current navigation is navigations[2].
- optional int32 current_navigation_index = 3 [default = -1];
+ optional int32 current_navigation_index = 4 [default = -1];
// True if the tab is pinned.
- optional bool pinned = 4 [default = false];
+ optional bool pinned = 5 [default = false];
// If non-empty, this tab is an app tab and this is the id of the extension.
- optional string extension_app_id = 5;
+ optional string extension_app_id = 6;
// Tabs are navigated, and the navigation data is here.
- repeated TabNavigation navigation = 6;
+ repeated TabNavigation navigation = 7;
}
message TabNavigation {
// The index in the NavigationController. If this is -1, it means this
diff --git a/chrome/browser/sync/resources/configure.html b/chrome/browser/sync/resources/configure.html
index 90a3491..b087169 100644
--- a/chrome/browser/sync/resources/configure.html
+++ b/chrome/browser/sync/resources/configure.html
@@ -83,13 +83,11 @@ form {
}
#sync-encryption-instructions {
- margin-bottom: 10px;
- line-height: 1.8em;
+ margin-bottom: 5px;
}
#sync-passphrase-warning {
- font-style: italic;
- line-height: 1.8em;
+ margin-bottom: 5px;
}
#encryption-tab-contents > .sync_item_show {
@@ -145,6 +143,26 @@ form {
margin-right: 10px;
margin-bottom: 10px;
}
+.sync-section {
+ background: #EEE;
+ margin: 5px 0px;
+ padding: 6px;
+}
+
+#explicit-message {
+ margin-bottom: 5px;
+}
+
+#change-passphrase {
+ margin: 10px 0;
+ background: #EEE;
+ padding: 8px;
+}
+
+#clear-data-button {
+ margin-top: 10px;
+}
+
html[dir='rtl'] .sync-footer {
text-align: left;
left: 0px;
@@ -254,9 +272,9 @@ html[os='mac'] input[type='submit'] {
args.syncApps;
document.getElementById("appsItem").className = "sync-item-show";
} else {
- document.getElementById("appsItem").className = "sync-item-hide";
- }
-
+ document.getElementById("appsItem").className = "sync-item-hide";
+ }
+
setCheckboxesToKeepEverythingSynced(args.keepEverythingSynced);
if (args.sessionsRegistered) {
document.getElementById("sessionsCheckbox").checked = args.syncSessions;
@@ -267,13 +285,18 @@ html[os='mac'] input[type='submit'] {
}
function setEncryptionCheckboxes(args) {
- document.getElementById("usePassphraseCheckbox").checked =
- args["usePassphrase"];
-
- // The passphrase, once set, cannot be unset.
if (args["usePassphrase"]) {
- document.getElementById("usePassphraseCheckbox").disabled = true;
+ document.getElementById("explicit-option").checked = true;
+
+ // 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";
+ } else {
+ document.getElementById("google-option").checked = true;
+ document.getElementById("change-passphrase").style.display = "none";
}
+ switchToMode("");
}
function setErrorState(args) {
@@ -298,6 +321,8 @@ 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.)
@@ -328,6 +353,10 @@ html[os='mac'] input[type='submit'] {
}
var f = document.getElementById("chooseDataTypesForm");
+ if (!checkPassphraseMatch()) {
+ return false;
+ }
+
var syncAll = f.keepEverythingSyncedRadio.checked;
// These values need to be kept in sync with where they are read in
// SyncSetupFlow::GetDataTypeChoiceData().
@@ -342,7 +371,8 @@ html[os='mac'] input[type='submit'] {
"syncTypedUrls": syncAll || f.typedUrlsCheckbox.checked,
"syncApps": syncAll || f.appsCheckbox.checked,
"syncSessions": syncAll || f.sessionsCheckbox.checked,
- "usePassphrase": document.getElementById("usePassphraseCheckbox").checked
+ "usePassphrase": (getRadioCheckedValue() == 'explicit'),
+ "passphrase": f.passphrase.value
});
chrome.send("Configure", [result]);
}
@@ -354,15 +384,73 @@ html[os='mac'] input[type='submit'] {
document.getElementById(currentTab + "-tab-contents").className =
"sync-config-tab-contents-inactive";
}
-
+
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";
+ }
+ }
+
+ function getRadioCheckedValue() {
+ var f = document.getElementById("chooseDataTypesForm");
+ for (var i = 0; i < f.option.length; ++i) {
+ if (f.option[i].checked) {
+ return f.option[i].value;
+ }
+ }
+ return undefined;
+ }
+
+ function onRadioChange() {
+ switchToMode(getRadioCheckedValue());
+ }
+
+ function checkPassphraseMatch() {
+ var emptyError = document.getElementById("emptyerror");
+ var mismatchError = document.getElementById("mismatcherror");
+ emptyError.style.display = "none";
+ mismatchError.style.display = "none";
+
+ if (getRadioCheckedValue() != "explicit") {
+ return true;
+ }
+ var f = document.getElementById("chooseDataTypesForm");
+ if (f.passphrase.value.length == 0) {
+ emptyError.style.display = "block";
+ return false;
+ }
+ if (f.confirmpassphrase.value.length > 0 &&
+ f.confirmpassphrase.value != f.passphrase.value) {
+ mismatchError.style.display = "block";
+ return false;
+ }
+ return true;
+ }
+
+ function sendValuesAndClose() {
+ var result = JSON.stringify({"option": getRadioCheckedValue(),
+ "passphrase": f.passphrase.value});
+ chrome.send("FirstPassphrase", [result]);
+ }
+
+ function goToDashboard() {
+ chrome.send("GoToDashboard", [""]);
+ chrome.send("DialogClose", [""]);
+ }
+
</script>
</head>
<body i18n-values=".style.fontFamily:fontfamily"
@@ -381,9 +469,9 @@ html[os='mac'] input[type='submit'] {
</div>
<div id="data-type-tab-contents" class="sync-config-tab-contents-inactive">
- <div class="sync-header"
+ <div class="sync-header"
i18n-content="choosedatatypesheader"></div>
- <div class="sync-choose_data_types_instructions"
+ <div class="sync-choose_data_types_instructions"
i18n-content="choosedatatypesinstructions"></div>
<div class="sync-select-customization">
<div class="sync-choice_radio">
@@ -395,7 +483,7 @@ html[os='mac'] input[type='submit'] {
</label>
</div>
<div id="chooseDataTypes" class="sync-choice_radio">
- <input id="chooseDataTypesRadio" type="radio" name="syncChooseDataTypes"
+ <input id="chooseDataTypesRadio" type="radio" name="syncChooseDataTypes"
onclick="setCheckboxesToKeepEverythingSynced(false)">
<label for="chooseDataTypesRadio" i18n-content="choosedatatypes" ></label>
<div id="chooseDataTypesBody">
@@ -403,49 +491,49 @@ html[os='mac'] input[type='submit'] {
<!-- Apps -->
<div class="sync-item-show" id="appsItem">
<input id="appsCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="appsCheckboxLabel" name="dataTypeLabel"
+ <label id="appsCheckboxLabel" name="dataTypeLabel"
for="appsCheckbox" i18n-content="apps"
i18n-values="title:apps"></label>
</div>
<!-- Autofill -->
<div class="sync-item-show" id="autofillItem">
<input id="autofillCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="autofillCheckboxLabel" name="dataTypeLabel"
+ <label id="autofillCheckboxLabel" name="dataTypeLabel"
for="autofillCheckbox" i18n-content="autofill"
i18n-values="title:autofill"></label>
</div>
<!-- Bookmarks -->
<div class="sync-item-show" id="bookmarksItem">
<input id="bookmarksCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="bookmarksCheckboxLabel" name="dataTypeLabel"
+ <label id="bookmarksCheckboxLabel" name="dataTypeLabel"
for="bookmarksCheckbox" i18n-content="bookmarks"
i18n-values="title:bookmarks"></label>
</div>
<!-- Extensions -->
<div class="sync-item-show" id="extensionsItem">
<input id="extensionsCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="extensionsCheckboxLabel" name="dataTypeLabel"
+ <label id="extensionsCheckboxLabel" name="dataTypeLabel"
for="extensionsCheckbox" i18n-content="extensions"
i18n-values="title:extensions"></label>
</div>
<!-- Omnibox -->
<div class="sync-item-show" id="omniboxItem">
<input id="typedUrlsCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="typedUrlsCheckboxLabel" name="dataTypeLabel"
+ <label id="typedUrlsCheckboxLabel" name="dataTypeLabel"
for="typedUrlsCheckbox" i18n-content="typedurls"
i18n-values="title:typedurls"></label>
</div>
<!-- Passwords -->
<div class="sync-item-show" id="passwordsItem">
<input id="passwordsCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="passwordsCheckboxLabel" name="dataTypeLabel"
+ <label id="passwordsCheckboxLabel" name="dataTypeLabel"
for="passwordsCheckbox" i18n-content="passwords"
i18n-values="title:passwords"></label>
</div>
<!-- Preferences -->
<div class="sync-item-show" id="preferencesItem">
<input id="preferencesCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="preferencesCheckboxLabel" name="dataTypeLabel"
+ <label id="preferencesCheckboxLabel" name="dataTypeLabel"
for="preferencesCheckbox" i18n-content="preferences"
i18n-values="title:preferences"></label>
</div>
@@ -458,7 +546,7 @@ html[os='mac'] input[type='submit'] {
<!-- Sessions -->
<div class="sync-item-show" id="sessionsItem">
<input id="sessionsCheckbox" name="dataTypeCheckbox" type="checkbox">
- <label id="sessionsCheckboxLabel" name="dataTypeLabel"
+ <label id="sessionsCheckboxLabel" name="dataTypeLabel"
for="sessionsCheckbox" i18n-content="foreignsessions"
il8n-values="title:sessions"></label>
</div>
@@ -475,21 +563,58 @@ html[os='mac'] input[type='submit'] {
</div>
<div id="encryption-tab-contents" class="sync-config-tab-contents-inactive">
- <div id="sync-encryption-instructions"
+ <div id="sync-encryption-instructions"
i18n-content="encryptionInstructions"></div>
-
- <div class="sync-item-show" id="usePassphrase">
- <input id="usePassphraseCheckbox" name="usePassphraseCheckbox"
- type="checkbox" />
- <label id="usePassphraseLabel" name="usePassphraseLabel"
- for="usePassphraseCheckbox" i18n-content="usePassphraseLabel">
- </label>
+
+ <div>
+ <input id="google-option" name="option" type="radio"
+ value="google" onchange="onRadioChange();">
+ <span i18n-content="googleOption"></span>
+ </input>
+ </div>
+ <div>
+ <input id="explicit-option" name="option" type="radio"
+ value="explicit" onchange="onRadioChange();">
+ <span i18n-content="explicitOption"></span>
+ </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" onkeyup="checkPassphraseMatch();"
+ onchange="checkPassphraseMatch();"/>
+ </div>
+ <div>
+ <div i18n-content="confirmLabel" id="confirmPassphraseLabel">
+ </div>
+ <input id="confirmpassphrase" name="confirmpassphrase" type="password"
+ label="confirmPassphraseLabel"
+ onkeyup="checkPassphraseMatch();"
+ onchange="checkPassphraseMatch();" />
+ </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="sync-passphrase-warning" i18n-content="passphraseWarning">
+
+ <div id="change-passphrase">
+ <div id="sync-passphrase-warning" i18n-content="passphraseWarning">
+ </div>
+ <div id="sync-reset-confirmation" i18n-content="cleardataconfirmation"
+ style="display: none;">
+ </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"
diff --git a/chrome/browser/sync/resources/encryption_login.html b/chrome/browser/sync/resources/encryption_login.html
new file mode 100644
index 0000000..7883cca
--- /dev/null
+++ b/chrome/browser/sync/resources/encryption_login.html
@@ -0,0 +1,6 @@
+<!-- This is intentionally an HTML fragment to be embedded
+ in the browser sign in HTML -->
+<div id="login_message">
+<h1>$1</h1>
+<p>$2</p>
+</div>
diff --git a/chrome/browser/sync/resources/firstpassphrase.html b/chrome/browser/sync/resources/firstpassphrase.html
new file mode 100644
index 0000000..4f82ef9
--- /dev/null
+++ b/chrome/browser/sync/resources/firstpassphrase.html
@@ -0,0 +1,204 @@
+<html i18n-values="dir:textdirection;">
+<head>
+<title></title>
+<style type="text/css">
+body {
+ line-height: 1.5em;
+ background: #FFFFFF;
+ font-size: 11pt;
+}
+html[os='mac'] body {
+ line-height: 1.5em;
+ background: #FFFFFF;
+}
+form {
+ -webkit-user-select: none;
+}
+.error {
+ color: red;
+ font-size: 10pt;
+ }
+.sync-header {
+ font-size: 1.2em;
+ font-weight: bold;
+ margin-bottom: 10px;
+}
+.sync-instructions {
+ margin-top: 10px;
+ margin-bottom: 10px;
+}
+.sync-footer {
+ position: fixed;
+ right: 0px;
+ bottom: 0px;
+ margin-right: 10px;
+ margin-bottom: 10px;
+}
+.sync-section {
+ background: #EEE;
+ margin: 5px;
+ padding: 10px;
+}
+html[dir='rtl'] .sync-footer {
+ text-align: left;
+ left: 0px;
+ bottom: 0px;
+ margin-left: 20px;
+}
+input[type='button'],
+input[type='submit'] {
+ min-width: 87px;
+ min-height: 26px;
+}
+html[os='mac'] input[type='button'],
+html[os='mac'] input[type='submit'] {
+ font-size: 12pt;
+}
+
+#passphrase {
+ margin-top: 5px;
+}
+
+#passphraseLabel,
+#confirmPassphraseLabel {
+ width: 145px;
+ float: left;
+ text-align: right;
+ margin-right: 4px;
+ padding-top: 3px;
+}
+
+</style>
+<script src="chrome://resources/js/cr.js"></script>
+<script>
+ var currentMode;
+
+ // Called once, when this html/js is loaded.
+ function setupDialog(args) {
+ // Allow platform specific rules
+ if (cr.isMac) {
+ document.documentElement.setAttribute('os', 'mac');
+ } else if (!cr.isWindows) {
+ document.documentElement.setAttribute('os', 'linux');
+ }
+
+ switchToMode("");
+ }
+
+ function switchToMode(mode) {
+ document.getElementById("section-explicit").style.display = "none";
+ document.getElementById("section-nothanks").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";
+ } else if (mode == "nothanks") {
+ document.getElementById("section-nothanks").style.display = "block";
+ }
+ }
+
+ function getRadioCheckedValue() {
+ var f = document.getElementById("form");
+ for (var i = 0; i < f.option.length; ++i) {
+ if (f.option[i].checked) {
+ return f.option[i].value;
+ }
+ }
+ return undefined;
+ }
+
+ function onRadioChange() {
+ switchToMode(getRadioCheckedValue());
+ }
+
+ function checkPassphraseMatch() {
+ var emptyError = document.getElementById("emptyerror");
+ var mismatchError = document.getElementById("mismatcherror");
+ emptyError.style.display = "none";
+ mismatchError.style.display = "none";
+
+ if (getRadioCheckedValue() != "explicit") {
+ return true;
+ }
+ var f = document.getElementById("form");
+ if (f.passphrase.value.length == 0) {
+ emptyError.style.display = "block";
+ return false;
+ }
+ if (f.confirmpassphrase.value.length > 0 &&
+ f.confirmpassphrase.value != f.passphrase.value) {
+ mismatchError.style.display = "block";
+ return false;
+ }
+ return true;
+ }
+
+ function sendValuesAndClose() {
+ var f = document.getElementById("form");
+ if (!checkPassphraseMatch()) {
+ return false;
+ }
+
+ var result = JSON.stringify({"option": getRadioCheckedValue(),
+ "passphrase": f.passphrase.value});
+ chrome.send("FirstPassphrase", [result]);
+ }
+</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" onchange="onRadioChange();">
+ <span i18n-content="googleOption"></span>
+ </input>
+ </div>
+ <div>
+ <input name="option" type="radio" value="explicit" onchange="onRadioChange();">
+ <span i18n-content="explicitOption"></span>
+ </input>
+ </div>
+ <div>
+ <input name="option" type="radio" value="nothanks" onchange="onRadioChange();">
+ <span i18n-content="nothanksOption"></span>
+ </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"></div>
+ <div>
+ <div i18n-content="passphraseLabel" id="passphraseLabel"></div>
+ <input id="passphrase" name="passphrase" label="passphraseLabel"
+ type="password" onkeyup="checkPassphraseMatch();"
+ onchange="checkPassphraseMatch();"/>
+ </div>
+ <div>
+ <div i18n-content="confirmLabel" id="confirmPassphraseLabel">
+ </div>
+ <input id="confirmpassphrase" name="confirmpassphrase" type="password"
+ label="confirmPassphraseLabel"
+ onkeyup="checkPassphraseMatch();"
+ onchange="checkPassphraseMatch();" />
+ </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-section" id="section-nothanks">
+ <div i18n-content="sectionNothanksMessage"></div>
+ </div>
+
+ <div class="sync-footer">
+ <input id="okButton" type="submit" i18n-values="value:ok" />
+ </div>
+</form>
+</body>
+</html>
diff --git a/chrome/browser/sync/resources/setup_flow.html b/chrome/browser/sync/resources/setup_flow.html
index a3de363..495828f 100644
--- a/chrome/browser/sync/resources/setup_flow.html
+++ b/chrome/browser/sync/resources/setup_flow.html
@@ -3,7 +3,8 @@
<title></title>
<script type="text/javascript">
function hideAllPages() {
- var pages = ['login', 'configure', 'passphrase', 'settingup', 'done'];
+ var pages = ['login', 'configure', 'passphrase',
+ 'settingup', 'done', 'firstpassphrase'];
for (var i = 0; i < pages.length; ++i) {
document.getElementById(pages[i]).style.display = 'none';
document.getElementById(pages[i]).tabIndex = -1;
@@ -21,6 +22,7 @@
function showPassphrase() { showPage('passphrase'); }
function showSettingUp() { showPage('settingup'); }
function showSetupDone() { showPage('done'); }
+ function showFirstPassphrase() { showPage('firstpassphrase'); }
// Called once, when this html/js is loaded.
function showTheRightIframe() {
@@ -45,5 +47,8 @@
<iframe id="done" frameborder="0" width="100%" scrolling="no" height="100%"
src="chrome://syncresources/setupdone" style="display:none"
tabindex="-1"></iframe>
+ <iframe id="firstpassphrase" frameborder="0" width="100%" scrolling="no"
+ height="100%" src="chrome://syncresources/firstpassphrase"
+ style="display:none" tabindex="-1"></iframe>
</body>
</html>
diff --git a/chrome/browser/sync/sync_setup_flow.cc b/chrome/browser/sync/sync_setup_flow.cc
index a6dd5ec..adb06ea 100644
--- a/chrome/browser/sync/sync_setup_flow.cc
+++ b/chrome/browser/sync/sync_setup_flow.cc
@@ -44,6 +44,10 @@ void FlowHandler::RegisterMessages() {
NewCallback(this, &FlowHandler::HandleConfigure));
dom_ui_->RegisterMessageCallback("Passphrase",
NewCallback(this, &FlowHandler::HandlePassphraseEntry));
+ dom_ui_->RegisterMessageCallback("FirstPassphrase",
+ NewCallback(this, &FlowHandler::HandleFirstPassphrase));
+ dom_ui_->RegisterMessageCallback("GoToDashboard",
+ NewCallback(this, &FlowHandler::HandleGoToDashboard));
}
static bool GetAuthData(const std::string& json,
@@ -76,6 +80,18 @@ bool GetPassphrase(const std::string& json, std::string* passphrase,
result->GetString("mode", mode);
}
+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));
@@ -145,6 +161,9 @@ static bool GetConfiguration(const std::string& json,
// 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;
}
@@ -204,6 +223,28 @@ void FlowHandler::HandlePassphraseEntry(const ListValue* args) {
flow_->OnPassphraseEntry(passphrase, mode);
}
+void FlowHandler::HandleFirstPassphrase(const ListValue* args) {
+ std::string json(dom_ui_util::GetJsonResponseFromFirstArgumentInList(args));
+
+ 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;
+ }
+
+ 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
@@ -258,6 +299,17 @@ void FlowHandler::ShowPassphraseEntry(const DictionaryValue& args) {
ExecuteJavascriptInIFrame(kPassphraseIFrameXPath, script);
}
+void FlowHandler::ShowFirstPassphrase(const DictionaryValue& args) {
+ if (dom_ui_)
+ dom_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);
+}
+
void FlowHandler::ShowSettingUp() {
if (dom_ui_)
dom_ui_->CallJavascriptFunction(L"showSettingUp");
@@ -306,7 +358,6 @@ SyncSetupFlow::SyncSetupFlow(SyncSetupWizard::State start_state,
login_start_time_(base::TimeTicks::Now()),
flow_handler_(new FlowHandler()),
owns_flow_handler_(true),
- configuration_pending_(false),
service_(service),
html_dialog_window_(NULL) {
flow_handler_->set_flow(this);
@@ -390,7 +441,8 @@ void SyncSetupFlow::OnDialogClosed(const std::string& json_retval) {
}
std::wstring SyncSetupFlow::GetDialogTitle() const {
- return l10n_util::GetString(IDS_SYNC_MY_BOOKMARKS_LABEL);
+ return UTF16ToWideHack(
+ l10n_util::GetStringUTF16(IDS_SYNC_MY_BOOKMARKS_LABEL));
}
bool SyncSetupFlow::IsDialogModal() const {
@@ -502,8 +554,6 @@ bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
return current_state_ == SyncSetupWizard::GAIA_LOGIN;
case SyncSetupWizard::CONFIGURE:
return current_state_ == SyncSetupWizard::GAIA_SUCCESS;
- case SyncSetupWizard::CREATE_PASSPHRASE:
- return current_state_ == SyncSetupWizard::CONFIGURE;
case SyncSetupWizard::ENTER_PASSPHRASE:
return current_state_ == SyncSetupWizard::CONFIGURE ||
current_state_ == SyncSetupWizard::SETTING_UP;
@@ -511,8 +561,8 @@ bool SyncSetupFlow::ShouldAdvance(SyncSetupWizard::State state) {
return current_state_ == SyncSetupWizard::CONFIGURE;
case SyncSetupWizard::SETTING_UP:
return current_state_ == SyncSetupWizard::CONFIGURE ||
- current_state_ == SyncSetupWizard::CREATE_PASSPHRASE ||
- current_state_ == SyncSetupWizard::ENTER_PASSPHRASE;
+ current_state_ == SyncSetupWizard::ENTER_PASSPHRASE ||
+ current_state_ == SyncSetupWizard::PASSPHRASE_MIGRATION;
case SyncSetupWizard::FATAL_ERROR:
return true; // You can always hit the panic button.
case SyncSetupWizard::DONE_FIRST_TIME:
@@ -553,16 +603,16 @@ void SyncSetupFlow::Advance(SyncSetupWizard::State advance_state) {
flow_handler_->ShowConfigure(args);
break;
}
- case SyncSetupWizard::CREATE_PASSPHRASE: {
+ case SyncSetupWizard::ENTER_PASSPHRASE: {
DictionaryValue args;
- args.SetString("mode", "new");
+ SyncSetupFlow::GetArgsForEnterPassphrase(service_, &args);
flow_handler_->ShowPassphraseEntry(args);
break;
}
- case SyncSetupWizard::ENTER_PASSPHRASE: {
+ case SyncSetupWizard::PASSPHRASE_MIGRATION: {
DictionaryValue args;
- SyncSetupFlow::GetArgsForEnterPassphrase(service_, &args);
- flow_handler_->ShowPassphraseEntry(args);
+ args.SetString("iframeToShow", "firstpassphrase");
+ flow_handler_->ShowFirstPassphrase(args);
break;
}
case SyncSetupWizard::SETUP_ABORTED_BY_PENDING_CLEAR: {
@@ -629,6 +679,9 @@ SyncSetupFlow* SyncSetupFlow::Run(ProfileSyncService* service,
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);
@@ -663,56 +716,47 @@ void SyncSetupFlow::OnUserSubmittedAuth(const std::string& username,
}
void SyncSetupFlow::OnUserConfigured(const SyncConfiguration& configuration) {
- // Store the configuration in case we need more information.
- configuration_ = configuration;
- configuration_pending_ = true;
-
- // If the user is activating secondary passphrase for the first time,
- // we need to prompt them to enter one.
- if (configuration.use_secondary_passphrase &&
- !service_->IsUsingSecondaryPassphrase()) {
- // TODO(tim): If we could download the Nigori node first before any other
- // types, we could do that prior to showing the configure page so that we
- // could pre-populate the 'Use an encryption passphrase' checkbox.
- // http://crbug.com/60182
- Advance(SyncSetupWizard::CREATE_PASSPHRASE);
- return;
- }
-
- OnConfigurationComplete();
-}
-
-void SyncSetupFlow::OnConfigurationComplete() {
- if (!configuration_pending_)
- return;
-
// 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);
+ !configuration.use_secondary_passphrase ||
+ configuration.secondary_passphrase.length() > 0);
- if (configuration_.use_secondary_passphrase &&
+ if (configuration.use_secondary_passphrase &&
!service_->IsUsingSecondaryPassphrase()) {
- service_->SetPassphrase(configuration_.secondary_passphrase, true);
+ service_->SetPassphrase(configuration.secondary_passphrase, true);
}
- service_->OnUserChoseDatatypes(configuration_.sync_everything,
- configuration_.data_types);
-
- configuration_pending_ = false;
+ service_->OnUserChoseDatatypes(configuration.sync_everything,
+ configuration.data_types);
}
void SyncSetupFlow::OnPassphraseEntry(const std::string& passphrase,
const std::string& mode) {
- if (current_state_ == SyncSetupWizard::ENTER_PASSPHRASE) {
- service_->SetPassphrase(passphrase, mode == std::string("enter"));
- Advance(SyncSetupWizard::SETTING_UP);
- } else if (configuration_pending_) {
- DCHECK_EQ(SyncSetupWizard::CREATE_PASSPHRASE, current_state_);
- configuration_.secondary_passphrase = passphrase;
- OnConfigurationComplete();
+ Advance(SyncSetupWizard::SETTING_UP);
+ service_->SetPassphrase(passphrase, true);
+}
+
+void SyncSetupFlow::OnFirstPassphraseEntry(const std::string& option,
+ const std::string& passphrase) {
+ Advance(SyncSetupWizard::SETTING_UP);
+ if (option == "explicit") {
+ service_->SetPassphrase(passphrase, true);
+ } else if (option == "nothanks") {
+ // User opted out of encrypted sync, need to turn off encrypted
+ // data types.
+ syncable::ModelTypeSet registered_types;
+ service_->GetRegisteredDataTypes(&registered_types);
+ registered_types.erase(syncable::PASSWORDS);
+ service_->OnUserChoseDatatypes(false, registered_types);
+ } else if (option == "google") {
+ // Implicit passphrase already set up.
+ Advance(SyncSetupWizard::DONE);
}
}
+
+void SyncSetupFlow::OnGoToDashboard() {
+ BrowserList::GetLastActive()->OpenPrivacyDashboardTabAndActivate();
+}
diff --git a/chrome/browser/sync/sync_setup_flow.h b/chrome/browser/sync/sync_setup_flow.h
index 8bfc443..f6c0233 100644
--- a/chrome/browser/sync/sync_setup_flow.h
+++ b/chrome/browser/sync/sync_setup_flow.h
@@ -110,7 +110,10 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
void OnPassphraseEntry(const std::string& passphrase,
const std::string& mode);
- void OnConfigurationComplete();
+ void OnFirstPassphraseEntry(const std::string& option,
+ const std::string& passphrase);
+
+ void OnGoToDashboard();
private:
FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, InitialStepLogin);
@@ -124,6 +127,7 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest,
DiscreteRunChooseDataTypesAbortedByPendingClear);
FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, EnterPassphraseRequired);
+ FRIEND_TEST_ALL_PREFIXES(SyncSetupWizardTest, PassphraseMigration);
// Use static Run method to get an instance.
SyncSetupFlow(SyncSetupWizard::State start_state,
@@ -149,11 +153,6 @@ class SyncSetupFlow : public HtmlDialogUIDelegate {
FlowHandler* flow_handler_;
mutable bool owns_flow_handler_;
- // The current configuration, held pending until all the information has
- // been populated (possibly using multiple dialog states).
- SyncConfiguration configuration_;
- bool configuration_pending_;
-
// We need this to write the sentinel "setup completed" pref.
ProfileSyncService* service_;
@@ -198,6 +197,8 @@ class FlowHandler : public DOMMessageHandler {
void HandleSubmitAuth(const ListValue* args);
void HandleConfigure(const ListValue* args);
void HandlePassphraseEntry(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);
@@ -205,6 +206,7 @@ class FlowHandler : public DOMMessageHandler {
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);
diff --git a/chrome/browser/sync/sync_setup_wizard.cc b/chrome/browser/sync/sync_setup_wizard.cc
index e80afe0..6e9d254 100644
--- a/chrome/browser/sync/sync_setup_wizard.cc
+++ b/chrome/browser/sync/sync_setup_wizard.cc
@@ -79,6 +79,7 @@ void SyncResourcesSource::StartDataRequest(const std::string& path_raw,
const char kSyncGaiaLoginPath[] = "gaialogin";
const char kSyncConfigurePath[] = "configure";
const char kSyncPassphrasePath[] = "passphrase";
+ const char kSyncFirstPassphrasePath[] = "firstpassphrase";
const char kSyncSettingUpPath[] = "settingup";
const char kSyncSetupDonePath[] = "setupdone";
@@ -149,23 +150,48 @@ void SyncResourcesSource::StartDataRequest(const std::string& path_raw,
GetStringFUTF16(IDS_SYNC_ENCRYPTION_INSTRUCTIONS,
GetStringUTF16(IDS_PRODUCT_NAME)));
AddString(dict, "encryptAllLabel", IDS_SYNC_ENCRYPT_ALL_LABEL);
- AddString(dict, "usePassphraseLabel", IDS_SYNC_PASSPHRASE_CHECKBOX_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);
// 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, "newPassphraseTitle", IDS_SYNC_NEW_PASSPHRASE_TITLE);
- AddString(dict, "newPassphraseBody", IDS_SYNC_NEW_PASSPHRASE_BODY);
AddString(dict, "enterPassphraseTitle", IDS_SYNC_ENTER_PASSPHRASE_TITLE);
AddString(dict, "enterPassphraseBody", IDS_SYNC_ENTER_PASSPHRASE_BODY);
- AddString(dict, "gaiaPassphraseTitle", IDS_SYNC_GAIA_PASSPHRASE_TITLE);
- AddString(dict, "gaiaPassphraseBody", IDS_SYNC_GAIA_PASSPHRASE_BODY);
AddString(dict, "passphraseLabel", IDS_SYNC_PASSPHRASE_LABEL);
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, "nothanksOption", IDS_SYNC_PASSPHRASE_OPT_CANCEL);
+ AddString(dict, "sectionGoogleMessage", IDS_SYNC_PASSPHRASE_MSG_GOOGLE);
+ AddString(dict, "sectionExplicitMessage", IDS_SYNC_PASSPHRASE_MSG_EXPLICIT);
+ AddString(dict, "sectionNothanksMessage", IDS_SYNC_PASSPHRASE_MSG_CANCEL);
+ 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, "ok", IDS_OK);
+ AddString(dict, "cancel", IDS_CANCEL);
} else if (path_raw == kSyncSettingUpPath) {
html_resource_id = IDR_SYNC_SETTING_UP_HTML;
@@ -279,9 +305,9 @@ SyncSetupWizard::State SyncSetupWizard::GetEndStateForDiscreteRun(
State result = FATAL_ERROR;
if (start_state == GAIA_LOGIN) {
result = GAIA_SUCCESS;
- } else if (start_state == ENTER_PASSPHRASE) {
- result = DONE;
- } else if (start_state == CONFIGURE) {
+ } else if (start_state == ENTER_PASSPHRASE ||
+ start_state == CONFIGURE ||
+ start_state == PASSPHRASE_MIGRATION) {
result = DONE;
}
DCHECK_NE(FATAL_ERROR, result) <<
diff --git a/chrome/browser/sync/sync_setup_wizard.h b/chrome/browser/sync/sync_setup_wizard.h
index dad3fa9..76b6fae 100644
--- a/chrome/browser/sync/sync_setup_wizard.h
+++ b/chrome/browser/sync/sync_setup_wizard.h
@@ -29,10 +29,11 @@ class SyncSetupWizard {
// Encryption --
// Choose what to encrypt and whether to use a passphrase.
CONFIGURE,
- // Show the screen that lets you enter a new passphrase
- CREATE_PASSPHRASE,
// Show the screen that prompts for your passphrase
ENTER_PASSPHRASE,
+ // Show the passphrase "first time" screen for migrating users, where all
+ // is explained and they choose between google password/custom passphrase.
+ PASSPHRASE_MIGRATION,
// The panic switch. Something went terribly wrong during setup and we
// can't recover.
FATAL_ERROR,
diff --git a/chrome/browser/sync/sync_setup_wizard_unittest.cc b/chrome/browser/sync/sync_setup_wizard_unittest.cc
index 46a0d69..b0da0d7 100644
--- a/chrome/browser/sync/sync_setup_wizard_unittest.cc
+++ b/chrome/browser/sync/sync_setup_wizard_unittest.cc
@@ -312,6 +312,7 @@ TEST_F(SyncSetupWizardTest, InitialStepLogin) {
TEST_F(SyncSetupWizardTest, ChooseDataTypesSetsPrefs) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
+ wizard_->Step(SyncSetupWizard::GAIA_SUCCESS);
wizard_->Step(SyncSetupWizard::CONFIGURE);
ListValue data_type_choices_value;
@@ -359,6 +360,22 @@ TEST_F(SyncSetupWizardTest, EnterPassphraseRequired) {
EXPECT_EQ("myPassphrase", service_->passphrase_);
}
+TEST_F(SyncSetupWizardTest, PassphraseMigration) {
+ SKIP_TEST_ON_MACOSX();
+ wizard_->Step(SyncSetupWizard::PASSPHRASE_MIGRATION);
+ ListValue value;
+ value.Append(new StringValue("{\"option\":\"explicit\","
+ "\"passphrase\":\"myPassphrase\"}"));
+ test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value);
+ EXPECT_EQ("myPassphrase", service_->passphrase_);
+
+ ListValue value2;
+ value2.Append(new StringValue("{\"option\":\"nothanks\","
+ "\"passphrase\":\"myPassphrase\"}"));
+ test_window_->flow()->flow_handler_->HandleFirstPassphrase(&value2);
+ EXPECT_EQ(service_->chosen_data_types_.count(syncable::PASSWORDS), 0U);
+}
+
TEST_F(SyncSetupWizardTest, DialogCancelled) {
SKIP_TEST_ON_MACOSX();
wizard_->Step(SyncSetupWizard::GAIA_LOGIN);
diff --git a/chrome/browser/sync/sync_ui_util.cc b/chrome/browser/sync/sync_ui_util.cc
index 27fdb9a..d49ada1 100644
--- a/chrome/browser/sync/sync_ui_util.cc
+++ b/chrome/browser/sync/sync_ui_util.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/sync/sync_ui_util.h"
#include "app/l10n_util.h"
+#include "app/resource_bundle.h"
#include "base/i18n/number_formatting.h"
#include "base/i18n/time_formatting.h"
#include "base/string_util.h"
@@ -13,6 +14,7 @@
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/ui/options/options_window.h"
#include "chrome/common/net/gaia/google_service_auth_error.h"
+#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
@@ -102,8 +104,28 @@ MessageType GetStatusInfo(ProfileSyncService* service,
l10n_util::GetStringUTF16(IDS_SYNC_AUTHENTICATING_LABEL));
}
result_type = PRE_SYNCED;
- } else if (auth_error.state() != AuthError::NONE ||
- service->observed_passphrase_required()) {
+ } else if (service->observed_passphrase_required()) {
+ if (service->passphrase_required_for_decryption()) {
+ // NOT first machine.
+ // Show a link and present as an error ("needs attention").
+ if (status_label && link_label) {
+ status_label->assign(string16());
+ link_label->assign(
+ l10n_util::GetStringUTF16(IDS_SYNC_CONFIGURE_ENCRYPTION));
+ }
+ result_type = SYNC_ERROR;
+ } else {
+ // First machine. Show as a promotion.
+ if (status_label && link_label) {
+ status_label->assign(
+ l10n_util::GetStringFUTF16(IDS_SYNC_NTP_PASSWORD_PROMO,
+ l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
+ link_label->assign(
+ l10n_util::GetStringUTF16(IDS_SYNC_NTP_PASSWORD_ENABLE));
+ }
+ result_type = SYNC_PROMO;
+ }
+ } else if (auth_error.state() != AuthError::NONE) {
if (status_label && link_label) {
GetStatusLabelsForAuthError(auth_error, service,
status_label, link_label);
@@ -156,6 +178,20 @@ MessageType GetStatusInfo(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) {
@@ -322,6 +358,12 @@ void ConstructAboutInformation(ProfileSyncService* service,
std::string location_str;
loc.Write(true, true, &location_str);
strings->SetString("unrecoverable_error_location", location_str);
+ } else if (!service->sync_initialized()) {
+ strings->SetString("summary", "Sync not yet initialized");
+ } else if (service->backend() == NULL) {
+ strings->SetString("summary",
+ "Unrecoverable error detected. Backend is null when it shouldnt be");
+ NOTREACHED();
} else {
browser_sync::ModelSafeRoutingInfo routes;
service->backend()->GetModelSafeRoutingInfo(&routes);
diff --git a/chrome/browser/sync/sync_ui_util.h b/chrome/browser/sync/sync_ui_util.h
index 8bcd85a..5a168c4 100644
--- a/chrome/browser/sync/sync_ui_util.h
+++ b/chrome/browser/sync/sync_ui_util.h
@@ -24,11 +24,15 @@ enum MessageType {
PRE_SYNCED, // User has not set up sync.
SYNCED, // We are synced and authenticated to a gmail account.
SYNC_ERROR, // A sync error (such as invalid credentials) has occurred.
+ SYNC_PROMO, // A situation has occurred which should be brought to the user's
+ // attention, but not as an error.
};
// 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,
@@ -60,4 +64,3 @@ void AddIntSyncDetail(ListValue* details,
int64 stat_value);
} // namespace sync_ui_util
#endif // CHROME_BROWSER_SYNC_SYNC_UI_UTIL_H_
-
diff --git a/chrome/browser/sync/syncable/syncable_unittest.cc b/chrome/browser/sync/syncable/syncable_unittest.cc
index 32a4cc3..6c887b2 100644
--- a/chrome/browser/sync/syncable/syncable_unittest.cc
+++ b/chrome/browser/sync/syncable/syncable_unittest.cc
@@ -23,10 +23,10 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
-#include "base/platform_thread.h"
#include "base/scoped_ptr.h"
#include "base/scoped_temp_dir.h"
#include "base/string_util.h"
+#include "base/threading/platform_thread.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"
@@ -386,7 +386,7 @@ TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
dir_->PurgeEntriesWithTypeIn(to_purge);
Directory::SaveChangesSnapshot snapshot1;
- AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
+ base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
dir_->TakeSnapshotForSaveChanges(&snapshot1);
EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge);
@@ -415,7 +415,7 @@ TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
// Fake SaveChanges() and make sure we got what we expected.
{
Directory::SaveChangesSnapshot snapshot;
- AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
+ base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
dir_->TakeSnapshotForSaveChanges(&snapshot);
// Make sure there's an entry for each new metahandle. Make sure all
// entries are marked dirty.
@@ -448,7 +448,7 @@ TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
// Fake SaveChanges() and make sure we got what we expected.
{
Directory::SaveChangesSnapshot snapshot;
- AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
+ base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
dir_->TakeSnapshotForSaveChanges(&snapshot);
// Make sure there's an entry for each new metahandle. Make sure all
// entries are marked dirty.
@@ -588,7 +588,7 @@ TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
// Fake SaveChanges() and make sure we got what we expected.
{
Directory::SaveChangesSnapshot snapshot;
- AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
+ base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
dir_->TakeSnapshotForSaveChanges(&snapshot);
// Make sure there are no dirty_metahandles.
EXPECT_EQ(0u, snapshot.dirty_metas.size());
@@ -614,7 +614,7 @@ TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
// Fake SaveChanges() and make sure we got what we expected.
{
Directory::SaveChangesSnapshot snapshot;
- AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
+ base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex);
dir_->TakeSnapshotForSaveChanges(&snapshot);
// Make sure there's an entry for each changed metahandle. Make sure all
// entries are marked dirty.
@@ -1356,7 +1356,7 @@ TEST(SyncableDirectoryManager, TestFileRelease) {
ASSERT_TRUE(file_util::Delete(dm.GetSyncDataDatabasePath(), true));
}
-class ThreadOpenTestDelegate : public PlatformThread::Delegate {
+class ThreadOpenTestDelegate : public base::PlatformThread::Delegate {
public:
explicit ThreadOpenTestDelegate(DirectoryManager* dm)
: directory_manager_(dm) {}
@@ -1373,10 +1373,10 @@ class ThreadOpenTestDelegate : public PlatformThread::Delegate {
TEST(SyncableDirectoryManager, ThreadOpenTest) {
DirectoryManager dm(FilePath(FILE_PATH_LITERAL(".")));
- PlatformThreadHandle thread_handle;
+ base::PlatformThreadHandle thread_handle;
ThreadOpenTestDelegate test_delegate(&dm);
- ASSERT_TRUE(PlatformThread::Create(0, &test_delegate, &thread_handle));
- PlatformThread::Join(thread_handle);
+ ASSERT_TRUE(base::PlatformThread::Create(0, &test_delegate, &thread_handle));
+ base::PlatformThread::Join(thread_handle);
{
ScopedDirLookup dir(&dm, "Open");
ASSERT_TRUE(dir.good());
@@ -1389,13 +1389,13 @@ TEST(SyncableDirectoryManager, ThreadOpenTest) {
struct Step {
Step() : condvar(&mutex), number(0) {}
- Lock mutex;
- ConditionVariable condvar;
+ base::Lock mutex;
+ base::ConditionVariable condvar;
int number;
int64 metahandle;
};
-class ThreadBugDelegate : public PlatformThread::Delegate {
+class ThreadBugDelegate : public base::PlatformThread::Delegate {
public:
// a role is 0 or 1, meaning this thread does the odd or event steps.
ThreadBugDelegate(int role, Step* step, DirectoryManager* dirman)
@@ -1409,7 +1409,7 @@ class ThreadBugDelegate : public PlatformThread::Delegate {
// PlatformThread::Delegate methods:
virtual void ThreadMain() {
const std::string dirname = "ThreadBug1";
- AutoLock scoped_lock(step_->mutex);
+ base::AutoLock scoped_lock(step_->mutex);
while (step_->number < 3) {
while (step_->number % 2 != role_) {
@@ -1457,14 +1457,16 @@ TEST(SyncableDirectoryManager, ThreadBug1) {
ThreadBugDelegate thread_delegate_1(0, &step, &dirman);
ThreadBugDelegate thread_delegate_2(1, &step, &dirman);
- PlatformThreadHandle thread_handle_1;
- PlatformThreadHandle thread_handle_2;
+ base::PlatformThreadHandle thread_handle_1;
+ base::PlatformThreadHandle thread_handle_2;
- ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
- ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
+ ASSERT_TRUE(
+ base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
+ ASSERT_TRUE(
+ base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
- PlatformThread::Join(thread_handle_1);
- PlatformThread::Join(thread_handle_2);
+ base::PlatformThread::Join(thread_handle_1);
+ base::PlatformThread::Join(thread_handle_2);
}
@@ -1480,7 +1482,7 @@ class DirectoryKernelStalenessBugDelegate : public ThreadBugDelegate {
virtual void ThreadMain() {
const char test_bytes[] = "test data";
const std::string dirname = "DirectoryKernelStalenessBug";
- AutoLock scoped_lock(step_->mutex);
+ base::AutoLock scoped_lock(step_->mutex);
const Id jeff_id = TestIdFactory::FromNumber(100);
while (step_->number < 4) {
@@ -1552,17 +1554,19 @@ TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) {
DirectoryKernelStalenessBugDelegate thread_delegate_1(0, &step, &dirman);
DirectoryKernelStalenessBugDelegate thread_delegate_2(1, &step, &dirman);
- PlatformThreadHandle thread_handle_1;
- PlatformThreadHandle thread_handle_2;
+ base::PlatformThreadHandle thread_handle_1;
+ base::PlatformThreadHandle thread_handle_2;
- ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
- ASSERT_TRUE(PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
+ ASSERT_TRUE(
+ base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1));
+ ASSERT_TRUE(
+ base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2));
- PlatformThread::Join(thread_handle_1);
- PlatformThread::Join(thread_handle_2);
+ base::PlatformThread::Join(thread_handle_1);
+ base::PlatformThread::Join(thread_handle_2);
}
-class StressTransactionsDelegate : public PlatformThread::Delegate {
+class StressTransactionsDelegate : public base::PlatformThread::Delegate {
public:
StressTransactionsDelegate(DirectoryManager* dm,
const std::string& dirname,
@@ -1588,7 +1592,7 @@ class StressTransactionsDelegate : public PlatformThread::Delegate {
if (rand_action < 4 && !path_name.empty()) {
ReadTransaction trans(dir, __FILE__, __LINE__);
CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name));
- PlatformThread::Sleep(rand() % 10);
+ base::PlatformThread::Sleep(rand() % 10);
} else {
std::string unique_name = StringPrintf("%d.%d", thread_number_,
entry_count++);
@@ -1596,7 +1600,7 @@ class StressTransactionsDelegate : public PlatformThread::Delegate {
WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__);
MutableEntry e(&trans, CREATE, trans.root_id(), path_name);
CHECK(e.good());
- PlatformThread::Sleep(rand() % 20);
+ base::PlatformThread::Sleep(rand() % 20);
e.Put(IS_UNSYNCED, true);
if (e.Put(ID, TestIdFactory::FromNumber(rand())) &&
e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) {
@@ -1616,18 +1620,18 @@ TEST(SyncableDirectory, StressTransactions) {
dirman.Open(dirname);
const int kThreadCount = 7;
- PlatformThreadHandle threads[kThreadCount];
+ base::PlatformThreadHandle threads[kThreadCount];
scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount];
for (int i = 0; i < kThreadCount; ++i) {
thread_delegates[i].reset(
new StressTransactionsDelegate(&dirman, dirname, i));
- ASSERT_TRUE(
- PlatformThread::Create(0, thread_delegates[i].get(), &threads[i]));
+ ASSERT_TRUE(base::PlatformThread::Create(
+ 0, thread_delegates[i].get(), &threads[i]));
}
for (int i = 0; i < kThreadCount; ++i) {
- PlatformThread::Join(threads[i]);
+ base::PlatformThread::Join(threads[i]);
}
dirman.Close(dirname);
diff --git a/chrome/browser/sync/test_profile_sync_service.h b/chrome/browser/sync/test_profile_sync_service.h
index f5da3d6..2615b24 100644
--- a/chrome/browser/sync/test_profile_sync_service.h
+++ b/chrome/browser/sync/test_profile_sync_service.h
@@ -112,6 +112,12 @@ class SyncBackendHostForProfileSyncTest : public SyncBackendHost {
}
}
+ virtual void ConfigureDataTypes(const syncable::ModelTypeSet& types,
+ CancelableTask* ready_task) {
+ SetAutofillMigrationState(syncable::MIGRATED);
+ SyncBackendHost::ConfigureDataTypes(types, ready_task);
+ }
+
virtual void HandleInitializationCompletedOnFrontendLoop() {
set_syncapi_initialized(); // Need to do this asap so task below works.
diff --git a/chrome/browser/sync/tools/sync_listen_notifications.cc b/chrome/browser/sync/tools/sync_listen_notifications.cc
index d15ded3..0f66c27 100644
--- a/chrome/browser/sync/tools/sync_listen_notifications.cc
+++ b/chrome/browser/sync/tools/sync_listen_notifications.cc
@@ -299,7 +299,6 @@ int main(int argc, char* argv[]) {
xmpp_client_settings.set_server(addr);
net::CertVerifier cert_verifier;
-
MessageLoopForIO message_loop;
// Connect and listen.
diff --git a/chrome/browser/sync/util/channel.h b/chrome/browser/sync/util/channel.h
index a21f6ea..88ddfc4 100644
--- a/chrome/browser/sync/util/channel.h
+++ b/chrome/browser/sync/util/channel.h
@@ -52,7 +52,7 @@
#include "base/lock.h"
#include "base/observer_list.h"
-#include "base/platform_thread.h"
+#include "base/threading/platform_thread.h"
namespace browser_sync {
@@ -106,7 +106,7 @@ class Channel {
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_ != PlatformThread::CurrentId());
+ bool need_lock = (locking_thread_ != base::PlatformThread::CurrentId());
if (need_lock)
event_handlers_mutex_.Acquire();
@@ -121,7 +121,7 @@ class Channel {
// This may result in an observer trying to remove itself, so keep track
// of the thread we're locked on.
- locking_thread_ = PlatformThread::CurrentId();
+ locking_thread_ = base::PlatformThread::CurrentId();
ChannelObserverList::Iterator it(event_handlers_);
EventHandler* obs;
@@ -136,7 +136,7 @@ class Channel {
private:
Lock event_handlers_mutex_;
- PlatformThreadId locking_thread_;
+ base::PlatformThreadId locking_thread_;
ObserverList<EventHandler> event_handlers_;
};
diff --git a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc
index d5f88d4..b85256e 100644
--- a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc
+++ b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc
@@ -6,7 +6,7 @@
#include "base/file_path.h"
#include "base/string_util.h"
-#include "base/waitable_event.h"
+#include "base/synchronization/waitable_event.h"
#include "base/values.h"
#include "chrome/browser/browser_thread.h"
#include "chrome/browser/extensions/extension_bookmarks_module.h"