Commit af3a19de authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

notifications: adds ability to block new notifications

Specifically, blocking only happens while in CCT and only if a
parameter is supplied along with the intent. The ability to do this
will be restricted to certain apps.

BUG=1149582
TEST=CustomTabActivityTest and NotificationPermissionContextTest

Change-Id: Ieb6d76ff2326406872764973ff31d81d8e088449
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2538359
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Reviewed-by: default avatarPeter Conn <peconn@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828341}
parent 0186d8a4
......@@ -519,4 +519,11 @@ public abstract class BrowserServicesIntentDataProvider {
public boolean shouldHideCctVisits() {
return false;
}
/**
* Returns true if new notification requests from cct should be blocked.
*/
public boolean shouldBlockNewNotificationRequests() {
return false;
}
}
......@@ -34,6 +34,7 @@ import org.chromium.chrome.browser.LaunchIntentDispatcher;
import org.chromium.chrome.browser.autofill_assistant.AutofillAssistantFacade;
import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider;
import org.chromium.chrome.browser.browserservices.BrowserServicesIntentDataProvider.CustomTabsUiType;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityNavigationController;
import org.chromium.chrome.browser.customtabs.content.CustomTabActivityTabProvider;
import org.chromium.chrome.browser.customtabs.features.CustomTabNavigationBarController;
import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
......@@ -100,12 +101,8 @@ public class CustomTabActivity extends BaseCustomTabActivity {
getStartupTabPreloader().setTabCreatedCallback(new Callback<Tab>() {
@Override
public void onResult(Tab tab) {
if (mIntentDataProvider.shouldHideOmniboxSuggestionsForCctVisits()) {
tab.setAddApi2TransitionToFutureNavigations(true);
}
if (mIntentDataProvider.shouldHideCctVisits()) {
tab.setHideFutureNavigations(true);
}
CustomTabActivityNavigationController.applyExperimentsToNewTab(
tab, mIntentDataProvider);
}
});
......@@ -197,6 +194,7 @@ public class CustomTabActivity extends BaseCustomTabActivity {
if (tab != null) {
tab.setAddApi2TransitionToFutureNavigations(false);
tab.setHideFutureNavigations(false);
tab.setShouldBlockNewNotificationRequests(false);
}
mConnection.notifyOpenInBrowser(mSession, webContents);
}
......
......@@ -157,6 +157,12 @@ public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvid
private static final String EXTRA_TRANSLATE_LANGUAGE =
"androidx.browser.customtabs.extra.TRANSLATE_LANGUAGE";
/**
* Extra that, if set, results in blocking new notification requests while in CCT.
*/
public static final String EXTRA_BLOCK_NEW_NOTIFICATION_REQUESTS_IN_CCT =
"androidx.browser.customtabs.extra.BLOCK_NEW_NOTIFICATION_REQUESTS_IN_CCT";
/**
* Extra that, if set, results in hiding omnibox suggestions for visits from cct. The value is
* a boolean, and is only considered if the feature kSuggestVisitsWithPageTransitionFromApi2 is
......@@ -938,4 +944,14 @@ public class CustomTabIntentDataProvider extends BrowserServicesIntentDataProvid
if (!GSAState.isGsaPackageName(clientPackageName)) return false;
return IntentUtils.safeGetBooleanExtra(mIntent, EXTRA_HIDE_VISITS_FROM_CCT, false);
}
@Override
public boolean shouldBlockNewNotificationRequests() {
// Only 1p apps are allowed to hide visits.
String clientPackageName =
CustomTabsConnection.getInstance().getClientPackageNameForSession(getSession());
if (!GSAState.isGsaPackageName(clientPackageName)) return false;
return IntentUtils.safeGetBooleanExtra(
mIntent, EXTRA_BLOCK_NEW_NOTIFICATION_REQUESTS_IN_CCT, false);
}
}
......@@ -181,15 +181,28 @@ public class CustomTabActivityNavigationController implements StartStopWithNativ
params.setTransitionType(IntentHandler.getTransitionTypeFromIntent(
mIntentDataProvider.getIntent(), transition));
if (mIntentDataProvider.shouldHideOmniboxSuggestionsForCctVisits()) {
applyExperimentsToNewTab(tab, mIntentDataProvider);
tab.loadUrl(params);
}
/**
* Configures various experiments in tab based on provider. This is intended to be called when a
* new tab is created.
*/
public static void applyExperimentsToNewTab(
Tab tab, BrowserServicesIntentDataProvider provider) {
if (provider.shouldHideOmniboxSuggestionsForCctVisits()) {
tab.setAddApi2TransitionToFutureNavigations(true);
}
if (mIntentDataProvider.shouldHideCctVisits()) {
if (provider.shouldHideCctVisits()) {
tab.setHideFutureNavigations(true);
}
tab.loadUrl(params);
if (provider.shouldBlockNewNotificationRequests()) {
tab.setShouldBlockNewNotificationRequests(true);
}
}
/**
......
......@@ -1152,6 +1152,19 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
return mPendingLoadParams;
}
@Override
public void setShouldBlockNewNotificationRequests(boolean value) {
if (mNativeTabAndroid != 0) {
TabImplJni.get().setShouldBlockNewNotificationRequests(mNativeTabAndroid, value);
}
}
@Override
public boolean getShouldBlockNewNotificationRequests() {
return mNativeTabAndroid != 0
&& TabImplJni.get().getShouldBlockNewNotificationRequests(mNativeTabAndroid);
}
/**
* Performs any subclass-specific tasks when the Tab crashes.
*/
......@@ -1556,5 +1569,7 @@ public class TabImpl implements Tab, TabObscuringHandler.Observer {
boolean getAddApi2TransitionToFutureNavigations(long nativeTabAndroid);
void setHideFutureNavigations(long nativeTabAndroid, boolean hide);
boolean getHideFutureNavigations(long nativeTabAndroid);
void setShouldBlockNewNotificationRequests(long nativeTabAndroid, boolean value);
boolean getShouldBlockNewNotificationRequests(long nativeTabAndroid);
}
}
......@@ -2532,6 +2532,40 @@ public class CustomTabActivityTest {
.getHideFutureNavigations());
}
@Test
@SmallTest
public void testShouldBlockNewNotificationRequestsDefaultsToFalse() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
Assert.assertFalse(mCustomTabActivityTestRule.getActivity()
.getActivityTab()
.getShouldBlockNewNotificationRequests());
}
@Test
@SmallTest
public void testShouldBlockNewNotificationRequests() throws Exception {
Context context = InstrumentationRegistry.getInstrumentation()
.getTargetContext()
.getApplicationContext();
Intent intent = CustomTabsTestUtils.createMinimalCustomTabIntent(context, mTestPage);
intent.setData(Uri.parse(mTestPage));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(
CustomTabIntentDataProvider.EXTRA_BLOCK_NEW_NOTIFICATION_REQUESTS_IN_CCT, true);
CustomTabsSessionToken token = CustomTabsSessionToken.getSessionTokenFromIntent(intent);
CustomTabsConnection connection = CustomTabsConnection.getInstance();
connection.newSession(token);
connection.overridePackageNameForSessionForTesting(
token, "com.google.android.googlequicksearchbox");
mCustomTabActivityTestRule.startCustomTabActivityWithIntent(intent);
Assert.assertTrue(mCustomTabActivityTestRule.getActivity()
.getActivityTab()
.getShouldBlockNewNotificationRequests());
}
/**
* The following test that history only has a single final page after speculation,
* whether it was a hit or a miss.
......
......@@ -28,6 +28,7 @@
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/history/history_tab_helper.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/notifications/notification_permission_context.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
#include "chrome/browser/profiles/profile_manager.h"
......@@ -264,6 +265,7 @@ void TabAndroid::InitWebContents(
AttachTabHelpers(web_contents_.get());
PropagateHideFutureNavigationsToHistoryTabHelper();
PropagateBlockNewNotificationRequestsToWebContents();
SetWindowSessionID(session_window_id_);
......@@ -477,6 +479,15 @@ void TabAndroid::SetHideFutureNavigations(JNIEnv* env, jboolean hide) {
PropagateHideFutureNavigationsToHistoryTabHelper();
}
void TabAndroid::SetShouldBlockNewNotificationRequests(JNIEnv* env,
jboolean value) {
if (should_block_new_notification_requests_ == value)
return;
should_block_new_notification_requests_ = value;
PropagateBlockNewNotificationRequestsToWebContents();
}
void TabAndroid::SetDevToolsAgentHost(
scoped_refptr<content::DevToolsAgentHost> host) {
devtools_host_ = std::move(host);
......@@ -490,6 +501,13 @@ void TabAndroid::PropagateHideFutureNavigationsToHistoryTabHelper() {
history_tab_helper->set_hide_all_navigations(hide_future_navigations_);
}
void TabAndroid::PropagateBlockNewNotificationRequestsToWebContents() {
if (!web_contents())
return;
NotificationPermissionContext::SetBlockNewNotificationRequests(
web_contents(), should_block_new_notification_requests_);
}
base::android::ScopedJavaLocalRef<jobject> JNI_TabImpl_FromWebContents(
JNIEnv* env,
const JavaParamRef<jobject>& jweb_contents) {
......
......@@ -120,6 +120,9 @@ class TabAndroid : public base::SupportsUserData {
bool hide_future_navigations() const { return hide_future_navigations_; }
bool should_block_new_notification_requests() const {
return should_block_new_notification_requests_;
}
// Observers -----------------------------------------------------------------
// Adds/Removes an Observer.
......@@ -180,6 +183,10 @@ class TabAndroid : public base::SupportsUserData {
jboolean GetHideFutureNavigations(JNIEnv* env) {
return hide_future_navigations_;
}
void SetShouldBlockNewNotificationRequests(JNIEnv* env, jboolean value);
jboolean GetShouldBlockNewNotificationRequests(JNIEnv* env) {
return should_block_new_notification_requests_;
}
scoped_refptr<content::DevToolsAgentHost> GetDevToolsAgentHost();
......@@ -190,6 +197,9 @@ class TabAndroid : public base::SupportsUserData {
// with |web_contents_|.
void PropagateHideFutureNavigationsToHistoryTabHelper();
// Calls SetBlockNewNotificationRequests() on NotificationPermissionContext.
void PropagateBlockNewNotificationRequestsToWebContents();
JavaObjectWeakGlobalRef weak_java_tab_;
// Identifier of the window the tab is in.
......@@ -204,6 +214,7 @@ class TabAndroid : public base::SupportsUserData {
std::unique_ptr<browser_sync::SyncedTabDelegateAndroid> synced_tab_delegate_;
bool should_add_api2_transition_to_future_navigations_ = false;
bool hide_future_navigations_ = false;
bool should_block_new_notification_requests_ = false;
base::ObserverList<Observer> observers_;
......
......@@ -20,6 +20,7 @@
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/permissions/permission_request_id.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents_user_data.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
......@@ -33,6 +34,35 @@
#include "ui/message_center/public/cpp/notifier_id.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
namespace {
class NotificationTabHelper
: public content::WebContentsUserData<NotificationTabHelper> {
public:
~NotificationTabHelper() override = default;
void set_should_block_new_notification_requests(bool value) {
should_block_new_notification_requests_ = value;
}
bool should_block_new_notification_requests() {
return should_block_new_notification_requests_;
}
private:
explicit NotificationTabHelper(content::WebContents* contents) {}
friend class content::WebContentsUserData<NotificationTabHelper>;
bool should_block_new_notification_requests_ = false;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
WEB_CONTENTS_USER_DATA_KEY_IMPL(NotificationTabHelper)
} // namespace
// static
void NotificationPermissionContext::UpdatePermission(
content::BrowserContext* browser_context,
......@@ -60,6 +90,15 @@ NotificationPermissionContext::NotificationPermissionContext(
NotificationPermissionContext::~NotificationPermissionContext() {}
// static
void NotificationPermissionContext::SetBlockNewNotificationRequests(
content::WebContents* web_contents,
bool value) {
NotificationTabHelper::CreateForWebContents(web_contents);
NotificationTabHelper::FromWebContents(web_contents)
->set_should_block_new_notification_requests(value);
}
ContentSetting NotificationPermissionContext::GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
......@@ -158,6 +197,12 @@ void NotificationPermissionContext::DecidePermission(
return;
}
auto* tab_helper = NotificationTabHelper::FromWebContents(web_contents);
if (tab_helper && tab_helper->should_block_new_notification_requests()) {
std::move(callback).Run(CONTENT_SETTING_BLOCK);
return;
}
permissions::PermissionContextBase::DecidePermission(
web_contents, id, requesting_origin, embedding_origin, user_gesture,
std::move(callback));
......
......@@ -94,6 +94,10 @@ class NotificationPermissionContext
content::BrowserContext* browser_context);
~NotificationPermissionContext() override;
static void SetBlockNewNotificationRequests(
content::WebContents* web_contents,
bool value);
// PermissionContextBase implementation.
ContentSetting GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
......
......@@ -499,6 +499,53 @@ TEST_F(NotificationPermissionContextTest, GetNotificationsSettings) {
EXPECT_EQ(CONTENT_SETTING_ASK, settings[4].GetContentSetting());
}
TEST_F(NotificationPermissionContextTest, BlockNewNotificationRequests) {
GURL url("https://example.com");
NavigateAndCommit(url);
NotificationPermissionContext context(profile());
const permissions::PermissionRequestID id(
web_contents()->GetMainFrame()->GetProcess()->GetID(),
web_contents()->GetMainFrame()->GetRoutingID(), 1);
ContentSetting result = CONTENT_SETTING_DEFAULT;
context.RequestPermission(web_contents(), id, url, true /* user_gesture */,
base::BindOnce(&StoreContentSetting, &result));
EXPECT_NE(result, CONTENT_SETTING_BLOCK);
NotificationPermissionContext::SetBlockNewNotificationRequests(web_contents(),
true);
context.RequestPermission(web_contents(), id, url, true /* user_gesture */,
base::BindOnce(&StoreContentSetting, &result));
EXPECT_EQ(result, CONTENT_SETTING_BLOCK);
NotificationPermissionContext::SetBlockNewNotificationRequests(web_contents(),
false);
result = CONTENT_SETTING_DEFAULT;
context.RequestPermission(web_contents(), id, url, true /* user_gesture */,
base::BindOnce(&StoreContentSetting, &result));
EXPECT_NE(result, CONTENT_SETTING_BLOCK);
}
TEST_F(NotificationPermissionContextTest,
BlockNewNotificationRequestsDoesNothingIfGranted) {
GURL url("https://example.com");
NavigateAndCommit(url);
NotificationPermissionContext::UpdatePermission(profile(), url,
CONTENT_SETTING_ALLOW);
NotificationPermissionContext context(profile());
const permissions::PermissionRequestID id(
web_contents()->GetMainFrame()->GetProcess()->GetID(),
web_contents()->GetMainFrame()->GetRoutingID(), 1);
NotificationPermissionContext::SetBlockNewNotificationRequests(web_contents(),
true);
ContentSetting result = CONTENT_SETTING_DEFAULT;
context.RequestPermission(web_contents(), id, url, true /* user_gesture */,
base::BindOnce(&StoreContentSetting, &result));
EXPECT_EQ(result, CONTENT_SETTING_ALLOW);
}
#if BUILDFLAG(ENABLE_EXTENSIONS)
TEST_F(NotificationPermissionContextTest, ExtensionPermissionAskByDefault) {
// Verifies that notification permission is not granted to extensions by
......
......@@ -279,4 +279,10 @@ public interface Tab extends TabLifecycle {
*/
public void setHideFutureNavigations(boolean hide);
public boolean getHideFutureNavigations();
/**
* If true, new notification requests are blocked.
*/
public void setShouldBlockNewNotificationRequests(boolean value);
public boolean getShouldBlockNewNotificationRequests();
}
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