summaryrefslogtreecommitdiff
path: root/chromium/components/send_tab_to_self
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-02-13 16:23:34 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-02-14 10:37:21 +0000
commit38a9a29f4f9436cace7f0e7abf9c586057df8a4e (patch)
treec4e8c458dc595bc0ddb435708fa2229edfd00bd4 /chromium/components/send_tab_to_self
parente684a3455bcc29a6e3e66a004e352dea4e1141e7 (diff)
downloadqtwebengine-chromium-38a9a29f4f9436cace7f0e7abf9c586057df8a4e.tar.gz
BASELINE: Update Chromium to 73.0.3683.37
Change-Id: I08c9af2948b645f671e5d933aca1f7a90ea372f2 Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/send_tab_to_self')
-rw-r--r--chromium/components/send_tab_to_self/BUILD.gn42
-rw-r--r--chromium/components/send_tab_to_self/DEPS5
-rw-r--r--chromium/components/send_tab_to_self/OWNERS6
-rw-r--r--chromium/components/send_tab_to_self/README.md8
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc184
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_bridge.h84
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc139
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_entry.cc98
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_entry.h74
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc100
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_model.cc23
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_model.h55
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_model_observer.h30
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_service.cc38
-rw-r--r--chromium/components/send_tab_to_self/send_tab_to_self_service.h45
15 files changed, 931 insertions, 0 deletions
diff --git a/chromium/components/send_tab_to_self/BUILD.gn b/chromium/components/send_tab_to_self/BUILD.gn
new file mode 100644
index 00000000000..b6bca20fcde
--- /dev/null
+++ b/chromium/components/send_tab_to_self/BUILD.gn
@@ -0,0 +1,42 @@
+# Copyright 2018 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.
+
+static_library("send_tab_to_self") {
+ sources = [
+ "send_tab_to_self_bridge.cc",
+ "send_tab_to_self_bridge.h",
+ "send_tab_to_self_entry.cc",
+ "send_tab_to_self_entry.h",
+ "send_tab_to_self_model.cc",
+ "send_tab_to_self_model.h",
+ "send_tab_to_self_model_observer.h",
+ "send_tab_to_self_service.cc",
+ "send_tab_to_self_service.h",
+ ]
+ deps = [
+ "//base",
+ "//components/keyed_service/core",
+ "//components/sync",
+ "//components/version_info",
+ "//url",
+ ]
+}
+
+source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "send_tab_to_self_bridge_unittest.cc",
+ "send_tab_to_self_entry_unittest.cc",
+ ]
+ deps = [
+ ":send_tab_to_self",
+ "//base",
+ "//base/test:test_support",
+ "//components/sync",
+ "//components/sync:test_support_driver",
+ "//components/sync:test_support_model",
+ "//testing/gtest",
+ "//url",
+ ]
+}
diff --git a/chromium/components/send_tab_to_self/DEPS b/chromium/components/send_tab_to_self/DEPS
new file mode 100644
index 00000000000..4ccca580c24
--- /dev/null
+++ b/chromium/components/send_tab_to_self/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+components/keyed_service/core",
+ "+components/sync",
+ "+components/version_info",
+]
diff --git a/chromium/components/send_tab_to_self/OWNERS b/chromium/components/send_tab_to_self/OWNERS
new file mode 100644
index 00000000000..a49acc9eff2
--- /dev/null
+++ b/chromium/components/send_tab_to_self/OWNERS
@@ -0,0 +1,6 @@
+hansberry@chromium.org
+jeffreycohen@chromium.org
+sebsg@chromium.org
+tgupta@chromium.org
+
+# COMPONENT: UI>Browser>Sharing
diff --git a/chromium/components/send_tab_to_self/README.md b/chromium/components/send_tab_to_self/README.md
new file mode 100644
index 00000000000..8307f790b82
--- /dev/null
+++ b/chromium/components/send_tab_to_self/README.md
@@ -0,0 +1,8 @@
+Send Tab To Self
+
+Send Tab To Self is a component providing necessary APIs for sending tabs
+between devices in Chrome.
+
+This feature will initially be developed for Android and Desktop, but with the
+goal of moving to supporting all platforms.
+
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc
new file mode 100644
index 00000000000..549d5134413
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.cc
@@ -0,0 +1,184 @@
+// Copyright 2018 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 "components/send_tab_to_self/send_tab_to_self_bridge.h"
+
+#include "base/bind.h"
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/optional.h"
+#include "base/time/clock.h"
+#include "components/sync/device_info/device_info.h"
+#include "components/sync/device_info/local_device_info_provider.h"
+#include "components/sync/model/entity_change.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/metadata_change_list.h"
+#include "components/sync/model/model_type_change_processor.h"
+#include "components/sync/model/mutable_data_batch.h"
+#include "components/sync/model_impl/in_memory_metadata_change_list.h"
+#include "components/sync/protocol/model_type_state.pb.h"
+
+namespace send_tab_to_self {
+
+SendTabToSelfBridge::SendTabToSelfBridge(
+ std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+ syncer::LocalDeviceInfoProvider* local_device_info_provider,
+ base::Clock* clock)
+ : ModelTypeSyncBridge(std::move(change_processor)),
+ local_device_info_provider_(local_device_info_provider),
+ clock_(clock) {
+ DCHECK(local_device_info_provider_);
+ DCHECK(clock_);
+ auto batch = std::make_unique<syncer::MetadataBatch>();
+ this->change_processor()->ModelReadyToSync(std::move(batch));
+ // TODO(jeffreycohen): Create a local store and read meta data from it.
+}
+
+SendTabToSelfBridge::~SendTabToSelfBridge() {
+}
+
+std::unique_ptr<syncer::MetadataChangeList>
+SendTabToSelfBridge::CreateMetadataChangeList() {
+ return std::make_unique<syncer::InMemoryMetadataChangeList>();
+}
+
+base::Optional<syncer::ModelError> SendTabToSelfBridge::MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_data) {
+ return ApplySyncChanges(std::move(metadata_change_list),
+ std::move(entity_data));
+}
+
+base::Optional<syncer::ModelError> SendTabToSelfBridge::ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) {
+ for (syncer::EntityChange& change : entity_changes) {
+ if (change.type() == syncer::EntityChange::ACTION_DELETE) {
+ entries_.erase(change.storage_key());
+ } else {
+ const sync_pb::SendTabToSelfSpecifics& specifics =
+ change.data().specifics.send_tab_to_self();
+ // TODO(jeffreycohen): FromProto expects a valid entry. External data
+ // needs to be sanitized before it's passed through.
+ std::unique_ptr<SendTabToSelfEntry> entry =
+ SendTabToSelfEntry::FromProto(specifics, clock_->Now());
+ // This entry is new. Add it to the model.
+ entries_[entry->GetGUID()] = std::move(entry);
+ }
+ }
+ if (!entity_changes.empty()) {
+ NotifySendTabToSelfModelChanged();
+ }
+ return base::nullopt;
+}
+
+void SendTabToSelfBridge::GetData(StorageKeyList storage_keys,
+ DataCallback callback) {
+ syncer::InMemoryMetadataChangeList metadata_change_list;
+
+ for (const std::string& guid : storage_keys) {
+ const SendTabToSelfEntry* entry = GetEntryByGUID(guid);
+ if (!entry) {
+ continue;
+ }
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics =
+ entry->AsProto();
+ auto entity_data = std::make_unique<syncer::EntityData>();
+ *entity_data->specifics.mutable_send_tab_to_self() = *specifics;
+ entity_data->non_unique_name = specifics->url();
+
+ change_processor()->Put(specifics->guid(), std::move(entity_data),
+ &metadata_change_list);
+ }
+}
+
+void SendTabToSelfBridge::GetAllDataForDebugging(DataCallback callback) {
+ // TODO(jeffreycohen): Add once we have a batch model.
+ NOTIMPLEMENTED();
+}
+
+std::string SendTabToSelfBridge::GetClientTag(
+ const syncer::EntityData& entity_data) {
+ return GetStorageKey(entity_data);
+}
+
+std::string SendTabToSelfBridge::GetStorageKey(
+ const syncer::EntityData& entity_data) {
+ return entity_data.specifics.send_tab_to_self().guid();
+}
+
+std::vector<std::string> SendTabToSelfBridge::GetAllGuids() const {
+ std::vector<std::string> keys;
+ for (const auto& it : entries_) {
+ DCHECK_EQ(it.first, it.second->GetGUID());
+ keys.push_back(it.first);
+ }
+ return keys;
+}
+
+void SendTabToSelfBridge::DeleteAllEntries() {
+ if (!change_processor()->IsTrackingMetadata()) {
+ DCHECK_EQ(0ul, entries_.size());
+ return;
+ }
+ syncer::InMemoryMetadataChangeList metadata_change_list;
+
+ for (const auto& key : GetAllGuids()) {
+ change_processor()->Delete(key, &metadata_change_list);
+ }
+ entries_.clear();
+}
+
+const SendTabToSelfEntry* SendTabToSelfBridge::GetEntryByGUID(
+ const std::string& guid) const {
+ auto it = entries_.find(guid);
+ if (it == entries_.end()) {
+ return nullptr;
+ }
+ return it->second.get();
+}
+
+const SendTabToSelfEntry* SendTabToSelfBridge::AddEntry(
+ const GURL& url,
+ const std::string& title,
+ base::Time navigation_time) {
+ if (!change_processor()->IsTrackingMetadata()) {
+ return nullptr;
+ }
+
+ std::string guid = base::GenerateGUID();
+
+ // TODO(jeffreycohen) Handle the fact that local device info may not be
+ // guaranteed in production code as it may take some time to get initialized.
+ DCHECK(local_device_info_provider_->GetLocalDeviceInfo());
+
+ // Assure that we don't have a guid collision.
+ DCHECK_EQ(GetEntryByGUID(guid), nullptr);
+ std::string trimmed_title = base::CollapseWhitespaceASCII(title, false);
+ std::string device_name =
+ local_device_info_provider_->GetLocalDeviceInfo()->client_name();
+
+ auto entry = std::make_unique<SendTabToSelfEntry>(
+ guid, url, trimmed_title, clock_->Now(), navigation_time, device_name);
+
+ syncer::InMemoryMetadataChangeList metadata_change_list;
+
+ // This entry is new. Add it to the store and model.
+ auto entity_data = std::make_unique<syncer::EntityData>();
+ *entity_data->specifics.mutable_send_tab_to_self() = *entry->AsProto();
+ entity_data->non_unique_name = entry->GetURL().spec();
+ entity_data->creation_time = entry->GetSharedTime();
+
+ change_processor()->Put(guid, std::move(entity_data), &metadata_change_list);
+
+ return entries_.emplace(guid, std::move(entry)).first->second.get();
+}
+
+void SendTabToSelfBridge::NotifySendTabToSelfModelChanged() {
+ for (SendTabToSelfModelObserver& observer : observers_)
+ observer.SendTabToSelfModelChanged();
+}
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h
new file mode 100644
index 00000000000..9cfc7e23475
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_bridge.h
@@ -0,0 +1,84 @@
+// Copyright 2018 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 COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_BRIDGE_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_BRIDGE_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+#include "components/send_tab_to_self/send_tab_to_self_entry.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
+#include "components/sync/base/model_type.h"
+#include "components/sync/model/model_type_sync_bridge.h"
+
+namespace syncer {
+class LocalDeviceInfoProvider;
+class ModelTypeChangeProcessor;
+} // namespace syncer
+
+namespace base {
+class Clock;
+} // namespace base
+
+namespace send_tab_to_self {
+
+// Interface for a persistence layer for send tab to self.
+// All interface methods have to be called on main thread.
+class SendTabToSelfBridge : public syncer::ModelTypeSyncBridge,
+ public SendTabToSelfModel {
+ public:
+ // |local_device_info_provider| must not be null and must outlive this object.
+ // |clock| must not be null and must outlive this object.
+ SendTabToSelfBridge(
+ std::unique_ptr<syncer::ModelTypeChangeProcessor> change_processor,
+ syncer::LocalDeviceInfoProvider* local_device_info_provider,
+ base::Clock* clock);
+ ~SendTabToSelfBridge() override;
+
+ // syncer::ModelTypeSyncBridge overrides.
+ std::unique_ptr<syncer::MetadataChangeList> CreateMetadataChangeList()
+ override;
+ base::Optional<syncer::ModelError> MergeSyncData(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_data) override;
+ base::Optional<syncer::ModelError> ApplySyncChanges(
+ std::unique_ptr<syncer::MetadataChangeList> metadata_change_list,
+ syncer::EntityChangeList entity_changes) override;
+ void GetData(StorageKeyList storage_keys, DataCallback callback) override;
+ void GetAllDataForDebugging(DataCallback callback) override;
+ std::string GetClientTag(const syncer::EntityData& entity_data) override;
+ std::string GetStorageKey(const syncer::EntityData& entity_data) override;
+
+ // SendTabToSelfModel overrides.
+ std::vector<std::string> GetAllGuids() const override;
+ void DeleteAllEntries() override;
+ const SendTabToSelfEntry* GetEntryByGUID(
+ const std::string& guid) const override;
+ const SendTabToSelfEntry* AddEntry(const GURL& url,
+ const std::string& title,
+ base::Time navigation_time) override;
+
+ private:
+ using SendTabToSelfEntries =
+ std::map<std::string, std::unique_ptr<SendTabToSelfEntry>>;
+
+ // Notify all observers of a change;
+ void NotifySendTabToSelfModelChanged();
+
+ // |entries_| is keyed by GUIDs.
+ SendTabToSelfEntries entries_;
+ const syncer::LocalDeviceInfoProvider* const local_device_info_provider_;
+ const base::Clock* const clock_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendTabToSelfBridge);
+};
+
+} // namespace send_tab_to_self
+
+#endif // COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_BRIDGE_H_
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc b/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
new file mode 100644
index 00000000000..ac447adfdc6
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 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 "components/send_tab_to_self/send_tab_to_self_bridge.h"
+
+#include <map>
+#include <set>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/simple_test_clock.h"
+#include "components/sync/device_info/local_device_info_provider_mock.h"
+#include "components/sync/model/metadata_batch.h"
+#include "components/sync/model/mock_model_type_change_processor.h"
+#include "components/sync/model_impl/in_memory_metadata_change_list.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+using testing::_;
+
+class SendTabToSelfBridgeTest : public testing::Test {
+ protected:
+ SendTabToSelfBridgeTest()
+ : bridge_(mock_processor_.CreateForwardingProcessor(),
+ &provider_,
+ &clock_) {
+ provider_.Initialize("cache_guid", "machine");
+ ON_CALL(mock_processor_, IsTrackingMetadata())
+ .WillByDefault(testing::Return(true));
+ }
+
+ base::Time AdvanceAndGetTime() {
+ clock_.Advance(base::TimeDelta::FromMilliseconds(10));
+ return clock_.Now();
+ }
+
+ syncer::EntityDataPtr MakeEntityData(const SendTabToSelfEntry& entry) {
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics =
+ entry.AsProto();
+
+ auto entity_data = std::make_unique<syncer::EntityData>();
+
+ *(entity_data->specifics.mutable_send_tab_to_self()) = *specifics;
+ entity_data->non_unique_name = entry.GetURL().spec();
+ // The client_tag_hash field is unused by the send_tab_to_self_bridge, but
+ // is required for a valid entity_data.
+ entity_data->client_tag_hash = "someclienttaghash";
+ return entity_data->PassToPtr();
+ }
+
+ // For Model Tests.
+ void AddSampleEntries() {
+ // Adds timer to avoid having two entries with the same shared timestamp.
+ bridge_.AddEntry(GURL("http://a.com"), "a", AdvanceAndGetTime());
+ bridge_.AddEntry(GURL("http://b.com"), "b", AdvanceAndGetTime());
+ bridge_.AddEntry(GURL("http://c.com"), "c", AdvanceAndGetTime());
+ bridge_.AddEntry(GURL("http://d.com"), "d", AdvanceAndGetTime());
+ }
+
+ base::SimpleTestClock clock_;
+
+ // In memory model type store needs to be able to post tasks.
+ base::test::ScopedTaskEnvironment task_environment_;
+
+ syncer::LocalDeviceInfoProviderMock provider_;
+
+ syncer::MockModelTypeChangeProcessor mock_processor_;
+
+ SendTabToSelfBridge bridge_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SendTabToSelfBridgeTest);
+};
+
+TEST_F(SendTabToSelfBridgeTest, CheckEmpties) {
+ EXPECT_EQ(0ul, bridge_.GetAllGuids().size());
+ AddSampleEntries();
+ EXPECT_EQ(4ul, bridge_.GetAllGuids().size());
+}
+
+TEST_F(SendTabToSelfBridgeTest, SyncAddOneEntry) {
+ syncer::EntityChangeList remote_input;
+
+ SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
+ AdvanceAndGetTime(), AdvanceAndGetTime(), "device");
+
+ remote_input.push_back(
+ syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry)));
+ auto metadata_change_list =
+ std::make_unique<syncer::InMemoryMetadataChangeList>();
+ bridge_.MergeSyncData(std::move(metadata_change_list), remote_input);
+ EXPECT_EQ(1ul, bridge_.GetAllGuids().size());
+}
+
+TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneAdd) {
+ SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
+ AdvanceAndGetTime(), AdvanceAndGetTime(), "device");
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics = entry.AsProto();
+
+ syncer::EntityChangeList add_changes;
+
+ add_changes.push_back(
+ syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry)));
+ auto metadata_change_list =
+ std::make_unique<syncer::InMemoryMetadataChangeList>();
+ bridge_.ApplySyncChanges(std::move(metadata_change_list), add_changes);
+ EXPECT_EQ(1ul, bridge_.GetAllGuids().size());
+}
+
+// Tests that the send tab to self entry is correctly removed.
+TEST_F(SendTabToSelfBridgeTest, ApplySyncChangesOneDeletion) {
+ SendTabToSelfEntry entry("guid1", GURL("http://www.example.com/"), "title",
+ AdvanceAndGetTime(), AdvanceAndGetTime(), "device");
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> specifics = entry.AsProto();
+
+ syncer::EntityChangeList add_changes;
+
+ add_changes.push_back(
+ syncer::EntityChange::CreateAdd("guid1", MakeEntityData(entry)));
+ auto metadata_change_list =
+ std::make_unique<syncer::InMemoryMetadataChangeList>();
+ bridge_.ApplySyncChanges(std::move(metadata_change_list), add_changes);
+ EXPECT_EQ(1ul, bridge_.GetAllGuids().size());
+ syncer::EntityChangeList delete_changes;
+ delete_changes.push_back(syncer::EntityChange::CreateDelete("guid1"));
+ bridge_.ApplySyncChanges(std::move(metadata_change_list), delete_changes);
+ EXPECT_EQ(0ul, bridge_.GetAllGuids().size());
+}
+
+} // namespace
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc b/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc
new file mode 100644
index 00000000000..6fc811f4568
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_entry.cc
@@ -0,0 +1,98 @@
+// Copyright 2018 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 "components/send_tab_to_self/send_tab_to_self_entry.h"
+
+#include "base/guid.h"
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "components/sync/protocol/send_tab_to_self_specifics.pb.h"
+
+namespace send_tab_to_self {
+
+SendTabToSelfEntry::SendTabToSelfEntry(const std::string& guid,
+ const GURL& url,
+ const std::string& title,
+ base::Time shared_time,
+ base::Time original_navigation_time,
+ const std::string& device_name)
+ : guid_(guid),
+ url_(url),
+ title_(title),
+ device_name_(device_name),
+ shared_time_(shared_time),
+ original_navigation_time_(original_navigation_time) {
+ DCHECK(!guid_.empty());
+ DCHECK(url_.is_valid());
+}
+
+SendTabToSelfEntry::~SendTabToSelfEntry() {}
+
+const std::string& SendTabToSelfEntry::GetGUID() const {
+ return guid_;
+}
+
+const GURL& SendTabToSelfEntry::GetURL() const {
+ return url_;
+}
+
+const std::string& SendTabToSelfEntry::GetTitle() const {
+ return title_;
+}
+
+base::Time SendTabToSelfEntry::GetSharedTime() const {
+ return shared_time_;
+}
+
+base::Time SendTabToSelfEntry::GetOriginalNavigationTime() const {
+ return original_navigation_time_;
+}
+
+const std::string& SendTabToSelfEntry::GetDeviceName() const {
+ return device_name_;
+}
+
+std::unique_ptr<sync_pb::SendTabToSelfSpecifics> SendTabToSelfEntry::AsProto()
+ const {
+ auto pb_entry = std::make_unique<sync_pb::SendTabToSelfSpecifics>();
+
+ pb_entry->set_guid(GetGUID());
+ pb_entry->set_title(GetTitle());
+ pb_entry->set_url(GetURL().spec());
+ pb_entry->set_shared_time_usec(
+ GetSharedTime().ToDeltaSinceWindowsEpoch().InMicroseconds());
+ pb_entry->set_navigation_time_usec(
+ GetOriginalNavigationTime().ToDeltaSinceWindowsEpoch().InMicroseconds());
+ pb_entry->set_device_name(GetDeviceName());
+
+ return pb_entry;
+}
+
+std::unique_ptr<SendTabToSelfEntry> SendTabToSelfEntry::FromProto(
+ const sync_pb::SendTabToSelfSpecifics& pb_entry,
+ base::Time now) {
+ std::string guid(pb_entry.guid());
+ DCHECK(!guid.empty());
+
+ GURL url(pb_entry.url());
+ DCHECK(url.is_valid());
+
+ base::Time shared_time = base::Time::FromDeltaSinceWindowsEpoch(
+ base::TimeDelta::FromMicroseconds(pb_entry.shared_time_usec()));
+ if (shared_time > now) {
+ shared_time = now;
+ }
+
+ base::Time navigation_time;
+ if (pb_entry.has_navigation_time_usec()) {
+ navigation_time = base::Time::FromDeltaSinceWindowsEpoch(
+ base::TimeDelta::FromMicroseconds(pb_entry.navigation_time_usec()));
+ }
+
+ return std::make_unique<SendTabToSelfEntry>(guid, url, pb_entry.title(),
+ shared_time, navigation_time,
+ pb_entry.device_name());
+}
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_entry.h b/chromium/components/send_tab_to_self/send_tab_to_self_entry.h
new file mode 100644
index 00000000000..ab0b98e7ee8
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_entry.h
@@ -0,0 +1,74 @@
+// Copyright 2018 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 COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_ENTRY_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_ENTRY_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "base/time/time.h"
+#include "url/gurl.h"
+
+namespace sync_pb {
+class SendTabToSelfSpecifics;
+}
+
+namespace send_tab_to_self {
+
+// A tab that is being shared. The URL is a unique identifier for an entry, as
+// such it should not be empty and is the only thing considered when comparing
+// entries.
+class SendTabToSelfEntry {
+ public:
+ // Creates a SendTabToSelf entry. |url| and |title| are the main fields of the
+ // entry.
+ // |now| is used to fill the |creation_time_us_| and all the update timestamp
+ // fields.
+ SendTabToSelfEntry(const std::string& guid,
+ const GURL& url,
+ const std::string& title,
+ base::Time shared_time,
+ base::Time original_navigation_time,
+ const std::string& device_name);
+ ~SendTabToSelfEntry();
+
+ // The unique random id for the entry.
+ const std::string& GetGUID() const;
+ // The URL of the page the user would like to send to themselves.
+ const GURL& GetURL() const;
+ // The title of the entry. Might be empty.
+ const std::string& GetTitle() const;
+ // The time that the tab was shared.
+ base::Time GetSharedTime() const;
+ // The time that the tab was navigated to.
+ base::Time GetOriginalNavigationTime() const;
+
+ // The name of the device that originated the sent tab.
+ const std::string& GetDeviceName() const;
+
+ // Returns a protobuf encoding the content of this SendTabToSelfEntry for
+ // sync.
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> AsProto() const;
+
+ // Creates a SendTabToSelfEntry from the protobuf format.
+ // If creation time is not set, it will be set to |now|.
+ static std::unique_ptr<SendTabToSelfEntry> FromProto(
+ const sync_pb::SendTabToSelfSpecifics& pb_entry,
+ base::Time now);
+
+ private:
+ std::string guid_;
+ GURL url_;
+ std::string title_;
+ std::string device_name_;
+ base::Time shared_time_;
+ base::Time original_navigation_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendTabToSelfEntry);
+};
+
+} // namespace send_tab_to_self
+
+#endif // COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_ENTRY_H_
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc b/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
new file mode 100644
index 00000000000..ba55592a0bd
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_entry_unittest.cc
@@ -0,0 +1,100 @@
+// Copyright 2019 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 "components/send_tab_to_self/send_tab_to_self_entry.h"
+
+#include <memory>
+
+#include "base/test/simple_test_tick_clock.h"
+#include "components/sync/protocol/send_tab_to_self_specifics.pb.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace send_tab_to_self {
+
+namespace {
+
+bool IsEqualForTesting(const SendTabToSelfEntry& a,
+ const SendTabToSelfEntry& b) {
+ return a.GetGUID() == b.GetGUID() && a.GetURL() == b.GetURL() &&
+ a.GetTitle() == b.GetTitle() &&
+ a.GetDeviceName() == b.GetDeviceName() &&
+ a.GetSharedTime() == b.GetSharedTime() &&
+ a.GetOriginalNavigationTime() == b.GetOriginalNavigationTime();
+}
+
+TEST(SendTabToSelfEntry, CompareEntries) {
+ const SendTabToSelfEntry e1("1", GURL("http://example.com"), "bar",
+ base::Time::FromTimeT(10),
+ base::Time::FromTimeT(10), "device");
+ const SendTabToSelfEntry e2("1", GURL("http://example.com"), "bar",
+ base::Time::FromTimeT(10),
+ base::Time::FromTimeT(10), "device");
+
+ EXPECT_TRUE(IsEqualForTesting(e1, e2));
+ const SendTabToSelfEntry e3("2", GURL("http://example.org"), "bar",
+ base::Time::FromTimeT(10),
+ base::Time::FromTimeT(10), "device");
+
+ EXPECT_FALSE(IsEqualForTesting(e1, e3));
+}
+
+TEST(SendTabToSelfEntry, SharedTime) {
+ SendTabToSelfEntry e("1", GURL("http://example.com"), "bar",
+ base::Time::FromTimeT(10), base::Time::FromTimeT(10),
+ "device");
+ EXPECT_EQ("bar", e.GetTitle());
+ // Getters return Base::Time values.
+ EXPECT_EQ(e.GetSharedTime(), base::Time::FromTimeT(10));
+}
+
+// Tests that the send tab to self entry is correctly encoded to
+// sync_pb::SendTabToSelfSpecifics.
+TEST(SendTabToSelfEntry, AsProto) {
+ SendTabToSelfEntry entry("1", GURL("http://example.com"), "bar",
+ base::Time::FromTimeT(10), base::Time::FromTimeT(10),
+ "device");
+ base::Time shared_time = entry.GetSharedTime();
+ base::Time navigation_time = entry.GetSharedTime();
+
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> pb_entry(entry.AsProto());
+ EXPECT_EQ(pb_entry->url(), "http://example.com/");
+ EXPECT_EQ(pb_entry->title(), "bar");
+ EXPECT_EQ(pb_entry->shared_time_usec(),
+ shared_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+
+ EXPECT_EQ(pb_entry->navigation_time_usec(),
+ navigation_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
+ EXPECT_EQ(pb_entry->device_name(), "device");
+}
+
+// Tests that the send tab to self entry is correctly parsed from
+// sync_pb::SendTabToSelfSpecifics.
+TEST(SendTabToSelfEntry, FromProto) {
+ std::unique_ptr<sync_pb::SendTabToSelfSpecifics> pb_entry =
+ std::make_unique<sync_pb::SendTabToSelfSpecifics>();
+ pb_entry->set_guid("1");
+ pb_entry->set_url("http://example.com/");
+ pb_entry->set_title("title");
+ pb_entry->set_device_name("device");
+ pb_entry->set_shared_time_usec(1);
+ pb_entry->set_navigation_time_usec(1);
+
+ std::unique_ptr<SendTabToSelfEntry> entry(
+ SendTabToSelfEntry::FromProto(*pb_entry, base::Time::FromTimeT(10)));
+
+ EXPECT_EQ(entry->GetGUID(), "1");
+ EXPECT_EQ(entry->GetURL().spec(), "http://example.com/");
+ EXPECT_EQ(entry->GetTitle(), "title");
+ EXPECT_EQ(entry->GetDeviceName(), "device");
+ EXPECT_EQ(entry->GetSharedTime().ToDeltaSinceWindowsEpoch().InMicroseconds(),
+ 1);
+ EXPECT_EQ(entry->GetOriginalNavigationTime()
+ .ToDeltaSinceWindowsEpoch()
+ .InMicroseconds(),
+ 1);
+}
+
+} // namespace
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_model.cc b/chromium/components/send_tab_to_self/send_tab_to_self_model.cc
new file mode 100644
index 00000000000..c2e4c883afa
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_model.cc
@@ -0,0 +1,23 @@
+// Copyright 2018 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 "components/send_tab_to_self/send_tab_to_self_model.h"
+
+namespace send_tab_to_self {
+
+SendTabToSelfModel::SendTabToSelfModel() {}
+
+SendTabToSelfModel::~SendTabToSelfModel() {}
+
+// Observer methods.
+void SendTabToSelfModel::AddObserver(SendTabToSelfModelObserver* observer) {
+ DCHECK(observer);
+ observers_.AddObserver(observer);
+}
+
+void SendTabToSelfModel::RemoveObserver(SendTabToSelfModelObserver* observer) {
+ observers_.RemoveObserver(observer);
+}
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_model.h b/chromium/components/send_tab_to_self/send_tab_to_self_model.h
new file mode 100644
index 00000000000..6ae0a69dc53
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_model.h
@@ -0,0 +1,55 @@
+// Copyright 2018 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 COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODEL_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODEL_H_
+
+#include <vector>
+
+#include "base/observer_list.h"
+#include "components/send_tab_to_self/send_tab_to_self_entry.h"
+#include "components/send_tab_to_self/send_tab_to_self_model_observer.h"
+
+namespace send_tab_to_self {
+
+// The send tab to self model contains a list of entries of shared urls.
+// This object should only be accessed from one thread, which is usually the
+// main thread.
+class SendTabToSelfModel {
+ public:
+ SendTabToSelfModel();
+ virtual ~SendTabToSelfModel();
+
+ // Returns a vector of entry IDs in the model.
+ virtual std::vector<std::string> GetAllGuids() const = 0;
+
+ // Delete all entries.
+ virtual void DeleteAllEntries() = 0;
+
+ // Returns a specific entry. Returns null if the entry does not exist.
+ virtual const SendTabToSelfEntry* GetEntryByGUID(
+ const std::string& guid) const = 0;
+
+ // Adds |url| at the top of the entries. The entry title will be a
+ // trimmed copy of |title|.
+ virtual const SendTabToSelfEntry* AddEntry(const GURL& url,
+ const std::string& title,
+ base::Time navigation_time) = 0;
+
+ // Observer registration methods. The model will remove all observers upon
+ // destruction automatically.
+ void AddObserver(SendTabToSelfModelObserver* observer);
+ void RemoveObserver(SendTabToSelfModelObserver* observer);
+
+ protected:
+ // The observers.
+ base::ObserverList<SendTabToSelfModelObserver>::Unchecked observers_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SendTabToSelfModel);
+};
+
+} // namespace send_tab_to_self
+
+#endif // COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODEL_H
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_model_observer.h b/chromium/components/send_tab_to_self/send_tab_to_self_model_observer.h
new file mode 100644
index 00000000000..54c471c9fbf
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_model_observer.h
@@ -0,0 +1,30 @@
+// Copyright 2019 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 COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODEL_OBSERVER_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODEL_OBSERVER_H_
+
+namespace send_tab_to_self {
+
+// Observer for the Send Tab To Self model. In the observer methods care should
+// be taken to not modify the model.
+class SendTabToSelfModelObserver {
+ public:
+ SendTabToSelfModelObserver() {}
+ virtual ~SendTabToSelfModelObserver() {}
+
+ // Invoked when the model has finished loading. Until this method is called it
+ // is unsafe to use the model.
+ virtual void SendTabToSelfModelLoaded() = 0;
+
+ // Invoked when elements of the model are added, removed, or updated.
+ virtual void SendTabToSelfModelChanged() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SendTabToSelfModelObserver);
+};
+
+} // namespace send_tab_to_self
+
+#endif // COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_MODEL_OBSERVER_H_
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_service.cc b/chromium/components/send_tab_to_self/send_tab_to_self_service.cc
new file mode 100644
index 00000000000..49916b60bf5
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_service.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 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 "components/send_tab_to_self/send_tab_to_self_service.h"
+
+#include "base/bind.h"
+#include "base/time/default_clock.h"
+#include "components/send_tab_to_self/send_tab_to_self_bridge.h"
+#include "components/send_tab_to_self/send_tab_to_self_model.h"
+#include "components/sync/base/report_unrecoverable_error.h"
+#include "components/sync/device_info/local_device_info_provider.h"
+#include "components/sync/model_impl/client_tag_based_model_type_processor.h"
+
+namespace send_tab_to_self {
+
+SendTabToSelfService::SendTabToSelfService(
+ version_info::Channel channel,
+ syncer::LocalDeviceInfoProvider* local_device_info_provider) {
+ bridge_ = std::make_unique<send_tab_to_self::SendTabToSelfBridge>(
+ std::make_unique<syncer::ClientTagBasedModelTypeProcessor>(
+ syncer::SEND_TAB_TO_SELF,
+ base::BindRepeating(&syncer::ReportUnrecoverableError, channel)),
+ local_device_info_provider, base::DefaultClock::GetInstance());
+}
+
+SendTabToSelfService::~SendTabToSelfService() = default;
+
+SendTabToSelfModel* SendTabToSelfService::GetSendTabToSelfModel() {
+ return bridge_.get();
+}
+
+base::WeakPtr<syncer::ModelTypeControllerDelegate>
+SendTabToSelfService::GetControllerDelegate() {
+ return bridge_->change_processor()->GetControllerDelegate();
+}
+
+} // namespace send_tab_to_self
diff --git a/chromium/components/send_tab_to_self/send_tab_to_self_service.h b/chromium/components/send_tab_to_self/send_tab_to_self_service.h
new file mode 100644
index 00000000000..44941a2933a
--- /dev/null
+++ b/chromium/components/send_tab_to_self/send_tab_to_self_service.h
@@ -0,0 +1,45 @@
+// Copyright 2019 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 COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_SERVICE_H_
+#define COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/memory/weak_ptr.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/version_info/channel.h"
+
+namespace syncer {
+class ModelTypeControllerDelegate;
+class LocalDeviceInfoProvider;
+} // namespace syncer
+
+namespace send_tab_to_self {
+class SendTabToSelfBridge;
+class SendTabToSelfModel;
+
+// KeyedService responsible for send tab to self sync.
+class SendTabToSelfService : public KeyedService {
+ public:
+ SendTabToSelfService(
+ version_info::Channel channel,
+ syncer::LocalDeviceInfoProvider* local_device_info_provider);
+ ~SendTabToSelfService() override;
+
+ SendTabToSelfModel* GetSendTabToSelfModel();
+
+ // For ProfileSyncService to initialize the controller.
+ base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate();
+
+ private:
+ std::unique_ptr<SendTabToSelfBridge> bridge_;
+
+ DISALLOW_COPY_AND_ASSIGN(SendTabToSelfService);
+};
+
+} // namespace send_tab_to_self
+
+#endif // COMPONENTS_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_SERVICE_H_