Commit bf218ae9 authored by Tina Wang's avatar Tina Wang Committed by Commit Bot

[SendTabToSelf] Add additional requirements for displaying SendTab option on Desktop

Add conditions which decides whether the "Send To My Devices" option
should be shown on the desktop Chrome menu.

Requirements are:
- Flag enabled
- Sync requirements:
  - User sync type enabled
  - 2 or more devices are synced
- Context requirements:
  - Is Http/Https
  - Not in a native page
  - Not in incognito mode

Bug: 926279
Change-Id: Ib93c99cdacc2bdb66960ec880b29978b8808f3f4
Reviewed-on: https://chromium-review.googlesource.com/c/1406170
Commit-Queue: Tina Wang <tinazwang@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarSebastien Seguin-Gagnon <sebsg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#630626}
parent 59c2fd09
...@@ -3106,6 +3106,8 @@ jumbo_split_static_library("browser") { ...@@ -3106,6 +3106,8 @@ jumbo_split_static_library("browser") {
"search/search_suggest/search_suggest_service_factory.cc", "search/search_suggest/search_suggest_service_factory.cc",
"search/search_suggest/search_suggest_service_factory.h", "search/search_suggest/search_suggest_service_factory.h",
"search/search_suggest/search_suggest_service_observer.h", "search/search_suggest/search_suggest_service_observer.h",
"send_tab_to_self/send_tab_to_self_util.cc",
"send_tab_to_self/send_tab_to_self_util.h",
"serial/chrome_serial_delegate.cc", "serial/chrome_serial_delegate.cc",
"serial/chrome_serial_delegate.h", "serial/chrome_serial_delegate.h",
"serial/serial_chooser_context.cc", "serial/serial_chooser_context.cc",
......
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#include "chrome/browser/renderer_context_menu/spelling_menu_observer.h" #include "chrome/browser/renderer_context_menu/spelling_menu_observer.h"
#include "chrome/browser/search/search.h" #include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h" #include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include "chrome/browser/spellchecker/spellcheck_service.h" #include "chrome/browser/spellchecker/spellcheck_service.h"
#include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/translate/translate_service.h" #include "chrome/browser/translate/translate_service.h"
...@@ -99,7 +100,6 @@ ...@@ -99,7 +100,6 @@
#include "components/spellcheck/common/spellcheck_common.h" #include "components/spellcheck/common/spellcheck_common.h"
#include "components/spellcheck/spellcheck_buildflags.h" #include "components/spellcheck/spellcheck_buildflags.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "components/translate/core/browser/translate_download_manager.h" #include "components/translate/core/browser/translate_download_manager.h"
#include "components/translate/core/browser/translate_manager.h" #include "components/translate/core/browser/translate_manager.h"
#include "components/translate/core/browser/translate_prefs.h" #include "components/translate/core/browser/translate_prefs.h"
...@@ -1358,7 +1358,8 @@ void RenderViewContextMenu::AppendPageItems() { ...@@ -1358,7 +1358,8 @@ void RenderViewContextMenu::AppendPageItems() {
IDS_CONTENT_CONTEXT_SAVEPAGEAS); IDS_CONTENT_CONTEXT_SAVEPAGEAS);
menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT); menu_model_.AddItemWithStringId(IDC_PRINT, IDS_CONTENT_CONTEXT_PRINT);
AppendMediaRouterItem(); AppendMediaRouterItem();
if (base::FeatureList::IsEnabled(switches::kSyncSendTabToSelf)) {
if (send_tab_to_self::ShouldOfferFeature(GetBrowser())) {
menu_model_.AddItemWithStringId(IDC_SEND_TO_MY_DEVICES, menu_model_.AddItemWithStringId(IDC_SEND_TO_MY_DEVICES,
IDS_CONTENT_CONTEXT_SEND_TO_MY_DEVICES); IDS_CONTENT_CONTEXT_SEND_TO_MY_DEVICES);
} }
......
file://components/send_tab_to_self/OWNERS
# COMPONENT: UI>Browser>Sharing
// 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 "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/device_info_sync_service_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/sync/device_info/device_info.h"
#include "components/sync/device_info/device_info_sync_service.h"
#include "components/sync/device_info/device_info_tracker.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "content/public/common/url_constants.h"
#include "url/gurl.h"
namespace send_tab_to_self {
bool IsFlagEnabled() {
return base::FeatureList::IsEnabled(switches::kSyncSendTabToSelf);
}
bool IsUserSyncTypeEnabled(Profile* profile) {
browser_sync::ProfileSyncService* profile_sync_service =
ProfileSyncServiceFactory::GetForProfile(profile);
return profile_sync_service &&
(profile_sync_service->GetUserSettings()->IsSyncEverythingEnabled() ||
profile_sync_service->GetUserSettings()->GetChosenDataTypes().Has(
syncer::SEND_TAB_TO_SELF));
}
bool IsSyncingOnMultipleDevices(Profile* profile) {
syncer::DeviceInfoSyncService* device_sync_service =
DeviceInfoSyncServiceFactory::GetForProfile(profile);
return device_sync_service &&
device_sync_service->GetDeviceInfoTracker()->CountActiveDevices() > 1;
}
bool IsContentRequirementsMet(GURL& url, Profile* profile) {
bool is_http_or_https = url.SchemeIsHTTPOrHTTPS();
bool is_native_page = url.SchemeIs(content::kChromeUIScheme);
bool is_incognito_mode =
profile->GetProfileType() == Profile::INCOGNITO_PROFILE;
return is_http_or_https && !is_native_page && !is_incognito_mode;
}
bool ShouldOfferFeature(Browser* browser) {
if (!browser) {
return false;
}
Profile* profile = browser->profile();
content::WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
if (!profile || !web_contents) {
return false;
}
GURL url = web_contents->GetURL();
return IsFlagEnabled() && IsUserSyncTypeEnabled(profile) &&
IsSyncingOnMultipleDevices(profile) &&
IsContentRequirementsMet(url, profile);
}
} // namespace send_tab_to_self
// 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 CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_UTIL_H_
#define CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_UTIL_H_
class Browser;
class GURL;
class Profile;
namespace send_tab_to_self {
// The send tab to self util contains a list of helper functions.
// Returns true if the 'send tab to self' flag is enabled.
bool IsFlagEnabled();
// Returns true if the SendTabToSelf sync datatype is enabled.
bool IsUserSyncTypeEnabled(Profile* profile);
// Returns true if the user syncing on two or more devices.
bool IsSyncingOnMultipleDevices(Profile* profile);
// Returns true if the tab and web content requirements are met:
// User is viewing an HTTP or HTTPS page.
// User is not on a native page.
// User is not in Incongnito mode.
bool IsContentRequirementsMet(GURL& gurl, Profile* profile);
// Returns true if all conditions are true and shows the option onto the menu
bool ShouldOfferFeature(Browser* browser);
} // namespace send_tab_to_self
#endif // CHROME_BROWSER_SEND_TAB_TO_SELF_SEND_TAB_TO_SELF_UTIL_H_
// 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 "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/sync/device_info_sync_service_factory.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chrome/browser/sync/profile_sync_test_util.h"
#include "chrome/test/base/browser_with_test_window_test.h"
#include "components/browser_sync/profile_sync_service.h"
#include "components/sync/device_info/device_info.h"
#include "components/sync/device_info/device_info_sync_bridge.h"
#include "components/sync/device_info/device_info_sync_service.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace send_tab_to_self {
namespace {
// Mock DeviceInfoTracker class for setting active devices
class TestDeviceInfoTracker : public syncer::DeviceInfoTracker {
public:
TestDeviceInfoTracker() = default;
~TestDeviceInfoTracker() override = default;
void SetActiveDevices(int devices) { active_devices_ = devices; }
// DeviceInfoTracker implementation
bool IsSyncing() const override { return false; }
std::unique_ptr<syncer::DeviceInfo> GetDeviceInfo(
const std::string& client_id) const override {
return std::unique_ptr<syncer::DeviceInfo>();
}
std::vector<std::unique_ptr<syncer::DeviceInfo>> GetAllDeviceInfo()
const override {
return std::vector<std::unique_ptr<syncer::DeviceInfo>>();
}
void AddObserver(Observer* observer) override {}
void RemoveObserver(Observer* observer) override {}
int CountActiveDevices() const override { return active_devices_; }
protected:
int active_devices_;
};
// Mock DeviceInfoSyncService to host mocked DeviceInfoTracker
class TestDeviceInfoSyncService : public syncer::DeviceInfoSyncService {
public:
TestDeviceInfoSyncService() = default;
~TestDeviceInfoSyncService() override = default;
TestDeviceInfoTracker* GetMockDeviceInfoTracker() { return &tracker_; }
void SetTrackerActiveDevices(int devices) {
tracker_.SetActiveDevices(devices);
}
// DeviceInfoSyncService implementation
syncer::LocalDeviceInfoProvider* GetLocalDeviceInfoProvider() override {
return nullptr;
}
syncer::DeviceInfoTracker* GetDeviceInfoTracker() override {
return &tracker_;
}
base::WeakPtr<syncer::ModelTypeControllerDelegate> GetControllerDelegate()
override {
return nullptr;
}
void InitLocalCacheGuid(const std::string& cache_guid,
const std::string& session_name) override {}
void ClearLocalCacheGuid() override {}
protected:
TestDeviceInfoTracker tracker_;
};
std::unique_ptr<KeyedService> BuildMockDeviceInfoSyncService(
content::BrowserContext* context) {
return std::make_unique<TestDeviceInfoSyncService>();
}
class SendTabToSelfUtilTest : public BrowserWithTestWindowTest {
public:
SendTabToSelfUtilTest() = default;
~SendTabToSelfUtilTest() override = default;
void SetUp() override {
BrowserWithTestWindowTest::SetUp();
mock_profile_sync_service_ =
static_cast<browser_sync::ProfileSyncServiceMock*>(
ProfileSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), base::BindRepeating(&BuildMockProfileSyncService)));
mock_device_sync_service_ = static_cast<TestDeviceInfoSyncService*>(
DeviceInfoSyncServiceFactory::GetInstance()->SetTestingFactoryAndUse(
profile(), base::BindRepeating(&BuildMockDeviceInfoSyncService)));
incognito_profile_ = profile()->GetOffTheRecordProfile();
url_ = GURL("https://www.google.com");
}
// Set up all test conditions to let ShouldOfferFeature() return true
void SetUpAllTrueEnv() {
scoped_feature_list_.InitAndEnableFeature(switches::kSyncSendTabToSelf);
syncer::ModelTypeSet enabled_modeltype(syncer::SEND_TAB_TO_SELF);
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(testing::Return(enabled_modeltype));
mock_device_sync_service_->SetTrackerActiveDevices(2);
AddTab(browser(), url_);
NavigateAndCommitActiveTab(url_);
}
// Set up a environment in which the feature flag is disabled
void SetUpFeatureDisabledEnv() {
scoped_feature_list_.InitAndDisableFeature(switches::kSyncSendTabToSelf);
syncer::ModelTypeSet enabled_modeltype(syncer::SEND_TAB_TO_SELF);
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(testing::Return(enabled_modeltype));
mock_device_sync_service_->SetTrackerActiveDevices(2);
AddTab(browser(), url_);
NavigateAndCommitActiveTab(url_);
}
protected:
browser_sync::ProfileSyncServiceMock* mock_profile_sync_service_;
TestDeviceInfoSyncService* mock_device_sync_service_;
base::test::ScopedFeatureList scoped_feature_list_;
Profile* incognito_profile_;
GURL url_;
};
TEST_F(SendTabToSelfUtilTest, IsFlagEnabled_True) {
scoped_feature_list_.InitAndEnableFeature(switches::kSyncSendTabToSelf);
EXPECT_TRUE(IsFlagEnabled());
}
TEST_F(SendTabToSelfUtilTest, IsFlagEnabled_False) {
scoped_feature_list_.InitAndDisableFeature(switches::kSyncSendTabToSelf);
EXPECT_FALSE(IsFlagEnabled());
}
TEST_F(SendTabToSelfUtilTest, IsUserSyncTypeEnabled_True) {
syncer::ModelTypeSet enabled_modeltype(syncer::SEND_TAB_TO_SELF);
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(testing::Return(enabled_modeltype));
EXPECT_TRUE(IsUserSyncTypeEnabled(profile()));
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(testing::Return(syncer::ModelTypeSet::All()));
EXPECT_TRUE(IsUserSyncTypeEnabled(profile()));
}
TEST_F(SendTabToSelfUtilTest, IsUserSyncTypeEnabled_False) {
syncer::ModelTypeSet disabled_modeltype;
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(testing::Return(disabled_modeltype));
EXPECT_FALSE(IsUserSyncTypeEnabled(profile()));
}
TEST_F(SendTabToSelfUtilTest, IsSyncingOnMultipleDevices_True) {
mock_device_sync_service_->SetTrackerActiveDevices(2);
EXPECT_TRUE(IsSyncingOnMultipleDevices(profile()));
}
TEST_F(SendTabToSelfUtilTest, IsSyncingOnMultipleDevices_False) {
mock_device_sync_service_->SetTrackerActiveDevices(0);
EXPECT_FALSE(IsSyncingOnMultipleDevices(profile()));
}
TEST_F(SendTabToSelfUtilTest, ContentRequirementsMet) {
EXPECT_TRUE(IsContentRequirementsMet(url_, profile()));
}
TEST_F(SendTabToSelfUtilTest, NotHTTPOrHTTPS) {
url_ = GURL("192.168.0.0");
EXPECT_FALSE(IsContentRequirementsMet(url_, profile()));
}
TEST_F(SendTabToSelfUtilTest, NativePage) {
url_ = GURL("chrome://flags");
EXPECT_FALSE(IsContentRequirementsMet(url_, profile()));
}
TEST_F(SendTabToSelfUtilTest, IncognitoMode) {
EXPECT_FALSE(IsContentRequirementsMet(url_, incognito_profile_));
}
TEST_F(SendTabToSelfUtilTest, ShouldOfferFeature_True) {
SetUpAllTrueEnv();
EXPECT_TRUE(ShouldOfferFeature(browser()));
}
TEST_F(SendTabToSelfUtilTest, ShouldOfferFeature_IsFlagEnabled_False) {
SetUpFeatureDisabledEnv();
EXPECT_FALSE(ShouldOfferFeature(browser()));
}
TEST_F(SendTabToSelfUtilTest, ShouldOfferFeature_IsUserSyncTypeEnabled_False) {
SetUpAllTrueEnv();
syncer::ModelTypeSet disabled_modeltype;
EXPECT_CALL(*mock_profile_sync_service_->GetUserSettingsMock(),
GetChosenDataTypes())
.WillRepeatedly(testing::Return(disabled_modeltype));
EXPECT_FALSE(ShouldOfferFeature(browser()));
}
TEST_F(SendTabToSelfUtilTest,
ShouldOfferFeature_IsSyncingOnMultipleDevices_False) {
SetUpAllTrueEnv();
mock_device_sync_service_->SetTrackerActiveDevices(0);
EXPECT_FALSE(ShouldOfferFeature(browser()));
}
TEST_F(SendTabToSelfUtilTest,
ShouldOfferFeature_IsContentRequirementsMet_False) {
SetUpAllTrueEnv();
url_ = GURL("192.168.0.0");
NavigateAndCommitActiveTab(url_);
EXPECT_FALSE(ShouldOfferFeature(browser()));
}
} // namespace
} // namespace send_tab_to_self
...@@ -6,13 +6,14 @@ ...@@ -6,13 +6,14 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "chrome/browser/browser_features.h" #include "chrome/browser/browser_features.h"
#include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h" #include "chrome/browser/ui/tabs/existing_tab_group_sub_menu_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h" #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_features.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
TabMenuModel::TabMenuModel(ui::SimpleMenuModel::Delegate* delegate, TabMenuModel::TabMenuModel(ui::SimpleMenuModel::Delegate* delegate,
...@@ -81,7 +82,10 @@ void TabMenuModel::Build(TabStripModel* tab_strip, int index) { ...@@ -81,7 +82,10 @@ void TabMenuModel::Build(TabStripModel* tab_strip, int index) {
: l10n_util::GetPluralStringFUTF16( : l10n_util::GetPluralStringFUTF16(
IDS_TAB_CXMENU_AUDIO_UNMUTE_TAB, num_affected_tabs)); IDS_TAB_CXMENU_AUDIO_UNMUTE_TAB, num_affected_tabs));
} }
if (base::FeatureList::IsEnabled(switches::kSyncSendTabToSelf)) {
Browser* browser =
chrome::FindBrowserWithWebContents(tab_strip->GetWebContentsAt(index));
if (send_tab_to_self::ShouldOfferFeature(browser)) {
AddItemWithStringId(TabStripModel::CommandSendToMyDevices, AddItemWithStringId(TabStripModel::CommandSendToMyDevices,
IDS_TAB_CXMENU_SEND_TO_MY_DEVICES); IDS_TAB_CXMENU_SEND_TO_MY_DEVICES);
} }
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search/search.h" #include "chrome/browser/search/search.h"
#include "chrome/browser/send_tab_to_self/send_tab_to_self_util.h"
#include "chrome/browser/ui/bookmarks/bookmark_utils.h" #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/browser_commands.h"
...@@ -55,7 +56,6 @@ ...@@ -55,7 +56,6 @@
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/signin/core/browser/signin_metrics.h" #include "components/signin/core/browser/signin_metrics.h"
#include "components/strings/grit/components_strings.h" #include "components/strings/grit/components_strings.h"
#include "components/sync/driver/sync_driver_switches.h"
#include "components/vector_icons/vector_icons.h" #include "components/vector_icons/vector_icons.h"
#include "components/zoom/zoom_controller.h" #include "components/zoom/zoom_controller.h"
#include "components/zoom/zoom_event_manager.h" #include "components/zoom/zoom_event_manager.h"
...@@ -765,7 +765,7 @@ void AppMenuModel::Build() { ...@@ -765,7 +765,7 @@ void AppMenuModel::Build() {
AddItemWithStringId(IDC_FIND, IDS_FIND); AddItemWithStringId(IDC_FIND, IDS_FIND);
if (base::FeatureList::IsEnabled(switches::kSyncSendTabToSelf)) { if (send_tab_to_self::ShouldOfferFeature(browser_)) {
AddItemWithStringId(IDC_SEND_TO_MY_DEVICES, IDS_SEND_TO_MY_DEVICES); AddItemWithStringId(IDC_SEND_TO_MY_DEVICES, IDS_SEND_TO_MY_DEVICES);
} }
......
...@@ -3329,6 +3329,7 @@ test("unit_tests") { ...@@ -3329,6 +3329,7 @@ test("unit_tests") {
"../browser/search/search_suggest/search_suggest_loader_impl_unittest.cc", "../browser/search/search_suggest/search_suggest_loader_impl_unittest.cc",
"../browser/search/search_suggest/search_suggest_service_unittest.cc", "../browser/search/search_suggest/search_suggest_service_unittest.cc",
"../browser/search/search_unittest.cc", "../browser/search/search_unittest.cc",
"../browser/send_tab_to_self/send_tab_to_self_util_unittest.cc",
"../browser/serial/serial_chooser_context_unittest.cc", "../browser/serial/serial_chooser_context_unittest.cc",
"../browser/sessions/tab_restore_service_unittest.cc", "../browser/sessions/tab_restore_service_unittest.cc",
"../browser/signin/signin_promo_unittest.cc", "../browser/signin/signin_promo_unittest.cc",
......
...@@ -2,5 +2,6 @@ hansberry@chromium.org ...@@ -2,5 +2,6 @@ hansberry@chromium.org
jeffreycohen@chromium.org jeffreycohen@chromium.org
sebsg@chromium.org sebsg@chromium.org
tgupta@chromium.org tgupta@chromium.org
tinazwang@chromium.org
# COMPONENT: UI>Browser>Sharing # COMPONENT: UI>Browser>Sharing
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment