Commit 7895d362 authored by Mugdha Lakhani's avatar Mugdha Lakhani Committed by Commit Bot

[Background Fetch] Surface background-fetch permission.

"background-fetch" permission was added to the Permissions API with this
commit:
https://github.com/w3c/permissions/pull/183/commits/096436eb59caaa5b244b0514d84f287392cb069b

Chrome uses Automatic Downloads for this permission, so as not to add yet
another user facing permission to the UI. This CL:
1. Adds a background_fetch content setting, because that's needed
   by PermissionContext and allows us to decouple from Automatic
   Downloads content setting.
2. Adds a BackgroundFetch PermissionContext mapped to this content
   setting.
3. Makes this PermissionContext decide whether background fetch() is
   permitted, which we do by:
   a. Querying DownloadRequestLimiter when there' a top level frame.
   b. Querying the Automatic Downloads content setting in other cases.

For context and details, please see this document:
https://docs.google.com/document/d/1rPYSlbzScw_6PLUJ3m96ZLIkxXVAWyBSL75-VDJEMso/edit?usp=sharing

Bug: 886896
Change-Id: Id0fcc64d4242290f3782c61ef11babe5717b409f
Reviewed-on: https://chromium-review.googlesource.com/c/1233714
Commit-Queue: Mugdha Lakhani <nator@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Reviewed-by: default avatarRayan Kanso <rayankans@chromium.org>
Reviewed-by: default avatarTobias Sargeant <tobiasjs@chromium.org>
Reviewed-by: default avatarRaymes Khoury <raymes@chromium.org>
Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#597209}
parent d07ad50c
......@@ -325,6 +325,7 @@ int AwPermissionManager::RequestPermissions(
case PermissionType::CLIPBOARD_READ:
case PermissionType::CLIPBOARD_WRITE:
case PermissionType::PAYMENT_HANDLER:
case PermissionType::BACKGROUND_FETCH:
NOTIMPLEMENTED() << "RequestPermissions is not implemented for "
<< static_cast<int>(permissions[i]);
pending_request_raw->SetPermissionStatus(permissions[i],
......@@ -516,6 +517,7 @@ void AwPermissionManager::CancelPermissionRequest(int request_id) {
case PermissionType::CLIPBOARD_READ:
case PermissionType::CLIPBOARD_WRITE:
case PermissionType::PAYMENT_HANDLER:
case PermissionType::BACKGROUND_FETCH:
NOTIMPLEMENTED() << "CancelPermission not implemented for "
<< static_cast<int>(permission);
break;
......
......@@ -124,6 +124,8 @@ jumbo_split_static_library("browser") {
"background_fetch/background_fetch_delegate_impl.h",
"background_fetch/background_fetch_download_client.cc",
"background_fetch/background_fetch_download_client.h",
"background_fetch/background_fetch_permission_context.cc",
"background_fetch/background_fetch_permission_context.h",
"background_sync/background_sync_controller_factory.cc",
"background_sync/background_sync_controller_factory.h",
"background_sync/background_sync_controller_impl.cc",
......
// 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 "chrome/browser/background_fetch/background_fetch_permission_context.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/download/download_request_limiter.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/web_contents.h"
BackgroundFetchPermissionContext::BackgroundFetchPermissionContext(
Profile* profile)
: PermissionContextBase(profile,
CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH,
blink::mojom::FeaturePolicyFeature::kNotFound) {}
bool BackgroundFetchPermissionContext::IsRestrictedToSecureOrigins() const {
return true;
}
ContentSetting BackgroundFetchPermissionContext::GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (render_frame_host && !render_frame_host->GetParent()) {
DownloadRequestLimiter* limiter =
g_browser_process->download_request_limiter();
DCHECK(limiter);
auto status = limiter->GetDownloadStatus(
content::WebContents::FromRenderFrameHost(render_frame_host));
switch (status) {
case DownloadRequestLimiter::DownloadStatus::ALLOW_ONE_DOWNLOAD:
case DownloadRequestLimiter::DownloadStatus::ALLOW_ALL_DOWNLOADS:
return CONTENT_SETTING_ALLOW;
case DownloadRequestLimiter::DownloadStatus::PROMPT_BEFORE_DOWNLOAD:
return CONTENT_SETTING_ASK;
case DownloadRequestLimiter::DownloadStatus::DOWNLOADS_NOT_ALLOWED:
return CONTENT_SETTING_BLOCK;
}
NOTREACHED();
}
// |render_frame_host| is either a nullptr, which means we're being called
// from a worker context, or it's not a top level frame. In either case, use
// content settings.
auto* host_content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile());
DCHECK(host_content_settings_map);
// The set of valid settings for automatic downloads is defined as
// {CONTENT_SETTING_ALLOW, CONTENT_SETTING_ASK, CONTENT_SETTING_BLOCK}.
return host_content_settings_map->GetContentSetting(
requesting_origin, requesting_origin,
CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
std::string() /* resource_identifier */);
}
void BackgroundFetchPermissionContext::DecidePermission(
content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
const BrowserPermissionCallback& callback) {
// The user should never be prompted to authorize Background Fetch
// from BackgroundFetchPermissionContext.
// BackgroundFetchDelegateImpl invokes CanDownload() on DownloadRequestLimiter
// to prompt the user.
NOTREACHED();
}
void BackgroundFetchPermissionContext::NotifyPermissionSet(
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
const BrowserPermissionCallback& callback,
bool persist,
ContentSetting content_setting) {
DCHECK(!persist);
PermissionContextBase::NotifyPermissionSet(
id, requesting_origin, embedding_origin, std::move(callback), persist,
content_setting);
}
// 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 CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_PERMISSION_CONTEXT_H_
#define CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_PERMISSION_CONTEXT_H_
#include "base/macros.h"
#include "chrome/browser/permissions/permission_context_base.h"
#include "components/content_settings/core/common/content_settings.h"
class GURL;
class Profile;
// Manages user permissions for Background Fetch. Background Fetch permission
// is currently dynamic and relies on either the download status from
// DownloadRequestLimiter, or the Automatic Downloads content setting
// This is why it isn't persisted.
class BackgroundFetchPermissionContext : public PermissionContextBase {
public:
explicit BackgroundFetchPermissionContext(Profile* profile);
~BackgroundFetchPermissionContext() override = default;
private:
// PermissionContextBase implementation.
bool IsRestrictedToSecureOrigins() const override;
ContentSetting GetPermissionStatusInternal(
content::RenderFrameHost* render_frame_host,
const GURL& requesting_origin,
const GURL& embedding_origin) const override;
void DecidePermission(content::WebContents* web_contents,
const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
const BrowserPermissionCallback& callback) override;
void NotifyPermissionSet(const PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
const BrowserPermissionCallback& callback,
bool persist,
ContentSetting content_setting) override;
DISALLOW_COPY_AND_ASSIGN(BackgroundFetchPermissionContext);
};
#endif // CHROME_BROWSER_BACKGROUND_FETCH_BACKGROUND_FETCH_PERMISSION_CONTEXT_H_
// 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 "chrome/browser/background_fetch/background_fetch_permission_context.h"
#include <string>
#include "base/macros.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "chrome/test/base/testing_profile.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/web_contents_tester.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class BackgroundFetchPermissionContextTest
: public ChromeRenderViewHostTestHarness {
protected:
BackgroundFetchPermissionContextTest() = default;
~BackgroundFetchPermissionContextTest() override = default;
ContentSetting GetPermissonStatus(
const GURL& url,
BackgroundFetchPermissionContext* permission_context,
bool with_frame) {
content::RenderFrameHost* render_frame_host = nullptr;
if (with_frame) {
content::WebContentsTester::For(web_contents())->NavigateAndCommit(url);
render_frame_host = web_contents()->GetMainFrame();
}
auto permission_result = permission_context->GetPermissionStatus(
render_frame_host, url /* requesting_origin */,
url /* embedding_origin */);
return permission_result.content_setting;
}
void SetContentSetting(const GURL& url,
ContentSettingsType content_type,
ContentSetting setting) {
auto* host_content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile());
ASSERT_TRUE(host_content_settings_map);
host_content_settings_map->SetContentSettingDefaultScope(
url /* primary_url*/, url /* secondary_url*/, content_type,
std::string() /* resource_identifier */, setting);
}
private:
DISALLOW_COPY_AND_ASSIGN(BackgroundFetchPermissionContextTest);
};
// Test that Background Fetch permission is "allow" by default, when queried
// from a top level frame.
TEST_F(BackgroundFetchPermissionContextTest, TestOutcomeAllowWithFrame) {
GURL url("https://example.com");
BackgroundFetchPermissionContext permission_context(profile());
EXPECT_EQ(GetPermissonStatus(url, &permission_context, /*with_frame =*/true),
CONTENT_SETTING_ALLOW);
}
// Test that Background Fetch permission is "allow" when queried from a worker
// context, if the Automatic Downloads content setting is set to
// CONTENT_SETTING_ALLOW.
TEST_F(BackgroundFetchPermissionContextTest, TestOutcomeAllowWithoutFrame) {
GURL url("https://example.com");
SetContentSetting(url, CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
CONTENT_SETTING_ALLOW);
BackgroundFetchPermissionContext permission_context(profile());
EXPECT_EQ(GetPermissonStatus(url, &permission_context, /*with_frame =*/false),
CONTENT_SETTING_ALLOW);
}
// Test that Background Fetch permission is "deny" when queried from a worker
// context, if the Automatic Downloads content settings is set to
// CONTENT_SETTING_BLOCK.
TEST_F(BackgroundFetchPermissionContextTest, TestOutcomeDenyWithoutFrame) {
GURL url("https://example.com");
SetContentSetting(url, CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
CONTENT_SETTING_BLOCK);
BackgroundFetchPermissionContext permission_context(profile());
EXPECT_EQ(GetPermissonStatus(url, &permission_context, /*with_frame =*/false),
CONTENT_SETTING_BLOCK);
}
// Test that Background Fetch permission is "prompt" when queried from a worker
// context, if the Automatic Downloads content setting is CONTENT_SETTING_ASK.
TEST_F(BackgroundFetchPermissionContextTest, TestOutcomePromptWithoutFrame) {
auto* host_content_settings_map =
HostContentSettingsMapFactory::GetForProfile(profile());
ASSERT_TRUE(host_content_settings_map);
GURL url("https://example.com");
SetContentSetting(url, CONTENT_SETTINGS_TYPE_AUTOMATIC_DOWNLOADS,
CONTENT_SETTING_ASK);
BackgroundFetchPermissionContext permission_context(profile());
EXPECT_EQ(GetPermissonStatus(url, &permission_context, /*with_frame =*/false),
CONTENT_SETTING_ASK);
}
} // namespace
......@@ -11,6 +11,7 @@
#include "base/feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/accessibility/accessibility_permission_context.h"
#include "chrome/browser/background_fetch/background_fetch_permission_context.h"
#include "chrome/browser/background_sync/background_sync_permission_context.h"
#include "chrome/browser/clipboard/clipboard_read_permission_context.h"
#include "chrome/browser/clipboard/clipboard_write_permission_context.h"
......@@ -123,6 +124,8 @@ ContentSettingsType PermissionTypeToContentSetting(PermissionType permission) {
return CONTENT_SETTINGS_TYPE_CLIPBOARD_WRITE;
case PermissionType::PAYMENT_HANDLER:
return CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER;
case PermissionType::BACKGROUND_FETCH:
return CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH;
case PermissionType::NUM:
// This will hit the NOTREACHED below.
break;
......@@ -307,6 +310,8 @@ PermissionManager::PermissionManager(Profile* profile) : profile_(profile) {
std::make_unique<ClipboardWritePermissionContext>(profile);
permission_contexts_[CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER] =
std::make_unique<payments::PaymentHandlerPermissionContext>(profile);
permission_contexts_[CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH] =
std::make_unique<BackgroundFetchPermissionContext>(profile);
}
PermissionManager::~PermissionManager() {
......
......@@ -50,6 +50,8 @@ std::string PermissionUtil::GetPermissionString(
return "ClipboardWrite";
case CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER:
return "PaymentHandler";
case CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH:
return "BackgroundFetch";
default:
break;
}
......@@ -122,6 +124,8 @@ bool PermissionUtil::GetPermissionType(ContentSettingsType type,
*out = PermissionType::CLIPBOARD_READ;
} else if (type == CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER) {
*out = PermissionType::PAYMENT_HANDLER;
} else if (type == CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH) {
*out = PermissionType::BACKGROUND_FETCH;
} else {
return false;
}
......@@ -145,6 +149,7 @@ bool PermissionUtil::IsPermission(ContentSettingsType type) {
case CONTENT_SETTINGS_TYPE_ACCESSIBILITY_EVENTS:
case CONTENT_SETTINGS_TYPE_CLIPBOARD_READ:
case CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER:
case CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH:
return true;
default:
return false;
......
......@@ -98,6 +98,7 @@ const ContentSettingsTypeNameEntry kContentSettingsTypeGroupNames[] = {
{CONTENT_SETTINGS_TYPE_ACCESSIBILITY_EVENTS, nullptr},
{CONTENT_SETTINGS_TYPE_CLIPBOARD_WRITE, nullptr},
{CONTENT_SETTINGS_TYPE_PLUGINS_DATA, nullptr},
{CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH, nullptr},
};
static_assert(arraysize(kContentSettingsTypeGroupNames) ==
// ContentSettingsType starts at -1, so add 1 here.
......
......@@ -2353,6 +2353,7 @@ test("unit_tests") {
"../browser/autocomplete/chrome_autocomplete_scheme_classifier_unittest.cc",
"../browser/autocomplete/search_provider_unittest.cc",
"../browser/autocomplete/shortcuts_provider_extension_unittest.cc",
"../browser/background_fetch/background_fetch_permission_context_unittest.cc",
"../browser/background_sync/background_sync_controller_impl_unittest.cc",
"../browser/background_sync/background_sync_permission_context_unittest.cc",
"../browser/banners/app_banner_settings_helper_unittest.cc",
......
......@@ -11,6 +11,7 @@
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_impl.h"
#include "chrome/browser/download/download_request_limiter.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/notifications/notification_platform_bridge.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
......@@ -374,7 +375,9 @@ DownloadStatusUpdater* TestingBrowserProcess::download_status_updater() {
}
DownloadRequestLimiter* TestingBrowserProcess::download_request_limiter() {
return nullptr;
if (!download_request_limiter_)
download_request_limiter_ = base::MakeRefCounted<DownloadRequestLimiter>();
return download_request_limiter_.get();
}
net_log::ChromeNetLog* TestingBrowserProcess::net_log() {
......
......@@ -177,6 +177,7 @@ class TestingBrowserProcess : public BrowserProcess {
std::unique_ptr<ProfileManager> profile_manager_;
std::unique_ptr<NotificationUIManager> notification_ui_manager_;
std::unique_ptr<NotificationPlatformBridge> notification_platform_bridge_;
scoped_refptr<DownloadRequestLimiter> download_request_limiter_;
#if BUILDFLAG(ENABLE_PRINTING)
std::unique_ptr<printing::PrintJobManager> print_job_manager_;
......
......@@ -29,7 +29,7 @@ struct HistogramValue {
// content settings type name instead.
//
// The array size must be explicit for the static_asserts below.
constexpr size_t kNumHistogramValues = 38;
constexpr size_t kNumHistogramValues = 39;
constexpr HistogramValue kHistogramValue[kNumHistogramValues] = {
{CONTENT_SETTINGS_TYPE_COOKIES, 0},
{CONTENT_SETTINGS_TYPE_IMAGES, 1},
......@@ -69,6 +69,7 @@ constexpr HistogramValue kHistogramValue[kNumHistogramValues] = {
{CONTENT_SETTINGS_TYPE_PLUGINS_DATA, 42},
{CONTENT_SETTINGS_TYPE_PAYMENT_HANDLER, 43},
{CONTENT_SETTINGS_TYPE_USB_GUARD, 44},
{CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH, 45},
};
} // namespace
......
......@@ -117,6 +117,11 @@ enum ContentSettingsType {
// stored under CONTENT_SETTINGS_TYPE_USB_CHOOSER_DATA.
CONTENT_SETTINGS_TYPE_USB_GUARD,
// Nothing is stored in this setting at present. Please refer to
// BackgroundFetchPermissionContext for details on how this permission
// is ascertained.
CONTENT_SETTINGS_TYPE_BACKGROUND_FETCH,
CONTENT_SETTINGS_NUM_TYPES,
};
......
......@@ -82,6 +82,9 @@ bool PermissionDescriptorToPermissionType(
case PermissionName::PAYMENT_HANDLER:
*permission_type = PermissionType::PAYMENT_HANDLER;
return true;
case PermissionName::BACKGROUND_FETCH:
*permission_type = PermissionType::BACKGROUND_FETCH;
return true;
}
NOTREACHED();
......
......@@ -29,6 +29,7 @@ enum class PermissionType {
CLIPBOARD_READ = 14,
CLIPBOARD_WRITE = 15,
PAYMENT_HANDLER = 16,
BACKGROUND_FETCH = 17,
// Always keep this at the end.
NUM,
......
......@@ -220,6 +220,8 @@ void LayoutTestMessageFilter::OnSetPermission(
} else if (name == "accelerometer" || name == "gyroscope" ||
name == "magnetometer" || name == "ambient-light-sensor") {
type = PermissionType::SENSORS;
} else if (name == "background-fetch") {
type = PermissionType::BACKGROUND_FETCH;
} else {
NOTREACHED();
type = PermissionType::NOTIFICATIONS;
......
......@@ -22,11 +22,12 @@ bool IsWhitelistedPermissionType(PermissionType permission) {
permission == PermissionType::MIDI ||
permission == PermissionType::SENSORS ||
permission == PermissionType::PAYMENT_HANDLER ||
// Background sync browser tests require permission to be granted by
// default.
// Background Sync and Background Fetch browser tests require
// permission to be granted by default.
// TODO(nsatragno): add a command line flag so that it's only granted
// for tests.
permission == PermissionType::BACKGROUND_SYNC ||
permission == PermissionType::BACKGROUND_FETCH ||
permission == PermissionType::ACCESSIBILITY_EVENTS;
}
......
......@@ -22,6 +22,7 @@ promise_test(async () => {
try {
self.permissionStatus = await navigator.permissions.query({ name: "geolocation" });
self.permissionStatus = await navigator.permissions.query({ name: "background-fetch"});
} catch (e) {
// Will be surfaced in idlharness.js's test_object below.
}
......
<!doctype html>
<meta charset=utf-8>
<title>Test Background Fetch Permission.</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<div id="log"></div>
<script>
promise_test(function(test) {
internals.runtimeFlags.backgroundFetchEnabled = true;
return navigator.permissions.query({name:'background-fetch'}).then(function(result) {
assert_true(result instanceof PermissionStatus);
});
});
</script>
......@@ -33,6 +33,8 @@ var PermissionsHelper = (function() {
return {name: "clipboard-write"};
case "payment-handler":
return {name: "payment-handler"};
case "background-fetch":
return {name: "background-fetch"};
default:
throw "Invalid permission name provided";
}
......
......@@ -20,6 +20,7 @@ enum PermissionName {
CLIPBOARD_READ,
CLIPBOARD_WRITE,
PAYMENT_HANDLER,
BACKGROUND_FETCH,
};
struct MidiPermissionDescriptor {
......
......@@ -12,6 +12,7 @@ enum PermissionName {
"microphone",
// "speaker",
// "device-info",
"background-fetch",
"background-sync",
// "bluetooth",
"persistent-storage",
......
......@@ -139,6 +139,13 @@ PermissionDescriptorPtr ParsePermission(ScriptState* script_state,
}
if (name == "payment-handler")
return CreatePermissionDescriptor(PermissionName::PAYMENT_HANDLER);
if (name == "background-fetch") {
if (!RuntimeEnabledFeatures::BackgroundFetchEnabled()) {
exception_state.ThrowTypeError("Background Fetch is not enabled.");
return nullptr;
}
return CreatePermissionDescriptor(PermissionName::BACKGROUND_FETCH);
}
return nullptr;
}
......
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