Commit 0e219689 authored by Reilly Grant's avatar Reilly Grant Committed by Commit Bot

idle-detection: Block permission requests from incognito profiles

This change implements automatic blocking of requests for the
"idle-detection" permission from incognito profiles in order to prevent
the feature from being used to correlate incognito and non-incognito
sessions.

As for requests for the "notifications" permission rather than simply
automatically denying the request (which would be an incognito oracle)
this patch waits a random amount of time before triggering the denial.
The code from the NotificationsPermissionContext is cleaned up and
reused for this.

Bug: 878979
Change-Id: I57c57f39457932f570d7094882dc00d9c0eafa16
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2360943
Commit-Queue: Reilly Grant <reillyg@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarBalazs Engedy <engedy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#807660}
parent 18c3d3d9
......@@ -1806,6 +1806,8 @@ static_library("browser") {
"usb/web_usb_service_impl.h",
"video_tutorials/video_tutorial_service_factory.cc",
"video_tutorials/video_tutorial_service_factory.h",
"visibility_timer_tab_helper.cc",
"visibility_timer_tab_helper.h",
"vr/ui_suppressed_element.h",
"vr/vr_tab_helper.cc",
"vr/vr_tab_helper.h",
......
......@@ -4,8 +4,13 @@
#include "chrome/browser/idle/idle_detection_permission_context.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/rand_util.h"
#include "chrome/browser/visibility_timer_tab_helper.h"
#include "components/content_settings/browser/page_specific_content_settings.h"
#include "components/permissions/permission_request_id.h"
#include "content/public/browser/browser_context.h"
#include "url/gurl.h"
IdleDetectionPermissionContext::IdleDetectionPermissionContext(
......@@ -35,3 +40,37 @@ void IdleDetectionPermissionContext::UpdateTabContext(
bool IdleDetectionPermissionContext::IsRestrictedToSecureOrigins() const {
return true;
}
void IdleDetectionPermissionContext::DecidePermission(
content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
permissions::BrowserPermissionCallback callback) {
// Idle detection permission is always denied in incognito. To prevent sites
// from using that to detect whether incognito mode is active, we deny after a
// random time delay, to simulate a user clicking a bubble/infobar. See also
// ContentSettingsRegistry::Init, which marks idle detection as
// INHERIT_IF_LESS_PERMISSIVE, and
// PermissionMenuModel::PermissionMenuModel which prevents users from manually
// allowing the permission.
if (browser_context()->IsOffTheRecord()) {
// Random number of seconds in the range [1.0, 2.0).
double delay_seconds = 1.0 + 1.0 * base::RandDouble();
VisibilityTimerTabHelper::CreateForWebContents(web_contents);
VisibilityTimerTabHelper::FromWebContents(web_contents)
->PostTaskAfterVisibleDelay(
FROM_HERE,
base::BindOnce(&IdleDetectionPermissionContext::NotifyPermissionSet,
weak_factory_.GetWeakPtr(), id, requesting_origin,
embedding_origin, std::move(callback),
/*persist=*/true, CONTENT_SETTING_BLOCK),
base::TimeDelta::FromSecondsD(delay_seconds));
return;
}
PermissionContextBase::DecidePermission(web_contents, id, requesting_origin,
embedding_origin, user_gesture,
std::move(callback));
}
......@@ -6,6 +6,7 @@
#define CHROME_BROWSER_IDLE_IDLE_DETECTION_PERMISSION_CONTEXT_H_
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/permissions/permission_context_base.h"
class IdleDetectionPermissionContext
......@@ -21,6 +22,15 @@ class IdleDetectionPermissionContext
const GURL& requesting_frame,
bool allowed) override;
bool IsRestrictedToSecureOrigins() const override;
void DecidePermission(
content::WebContents* web_contents,
const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedding_origin,
bool user_gesture,
permissions::BrowserPermissionCallback callback) override;
base::WeakPtrFactory<IdleDetectionPermissionContext> weak_factory_{this};
DISALLOW_COPY_AND_ASSIGN(IdleDetectionPermissionContext);
};
......
// Copyright 2020 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/idle/idle_detection_permission_context.h"
#include "base/test/task_environment.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/permissions/permission_request_id.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
namespace {
class TestIdleDetectionPermissionContext
: public IdleDetectionPermissionContext {
public:
explicit TestIdleDetectionPermissionContext(Profile* profile)
: IdleDetectionPermissionContext(profile),
permission_set_count_(0),
last_permission_set_persisted_(false),
last_permission_set_setting_(CONTENT_SETTING_DEFAULT) {}
int permission_set_count() const { return permission_set_count_; }
bool last_permission_set_persisted() const {
return last_permission_set_persisted_;
}
ContentSetting last_permission_set_setting() const {
return last_permission_set_setting_;
}
ContentSetting GetContentSettingFromMap(const GURL& url_a,
const GURL& url_b) {
return HostContentSettingsMapFactory::GetForProfile(browser_context())
->GetContentSetting(url_a.GetOrigin(), url_b.GetOrigin(),
content_settings_type(), std::string());
}
private:
// IdleDetectionPermissionContext:
void NotifyPermissionSet(const permissions::PermissionRequestID& id,
const GURL& requesting_origin,
const GURL& embedder_origin,
permissions::BrowserPermissionCallback callback,
bool persist,
ContentSetting content_setting) override {
permission_set_count_++;
last_permission_set_persisted_ = persist;
last_permission_set_setting_ = content_setting;
IdleDetectionPermissionContext::NotifyPermissionSet(
id, requesting_origin, embedder_origin, std::move(callback), persist,
content_setting);
}
int permission_set_count_;
bool last_permission_set_persisted_;
ContentSetting last_permission_set_setting_;
};
} // namespace
class IdleDetectionPermissionContextTest
: public ChromeRenderViewHostTestHarness {
public:
IdleDetectionPermissionContextTest()
: ChromeRenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
};
// Tests auto-denial after a time delay in incognito.
TEST_F(IdleDetectionPermissionContextTest, TestDenyInIncognitoAfterDelay) {
TestIdleDetectionPermissionContext permission_context(
profile()->GetPrimaryOTRProfile());
GURL url("https://www.example.com");
NavigateAndCommit(url);
const permissions::PermissionRequestID id(
web_contents()->GetMainFrame()->GetProcess()->GetID(),
web_contents()->GetMainFrame()->GetRoutingID(), -1);
ASSERT_EQ(0, permission_context.permission_set_count());
ASSERT_FALSE(permission_context.last_permission_set_persisted());
ASSERT_EQ(CONTENT_SETTING_DEFAULT,
permission_context.last_permission_set_setting());
permission_context.RequestPermission(
web_contents(), id, url, true /* user_gesture */, base::DoNothing());
// Should be blocked after 1-2 seconds, but the timer is reset whenever the
// tab is not visible, so these 500ms never add up to >= 1 second.
for (int n = 0; n < 10; n++) {
web_contents()->WasShown();
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
web_contents()->WasHidden();
}
EXPECT_EQ(0, permission_context.permission_set_count());
EXPECT_EQ(CONTENT_SETTING_ASK,
permission_context.GetContentSettingFromMap(url, url));
// Time elapsed whilst hidden is not counted.
// n.b. This line also clears out any old scheduled timer tasks. This is
// important, because otherwise Timer::Reset (triggered by
// VisibilityTimerTabHelper::WasShown) may choose to re-use an existing
// scheduled task, and when it fires Timer::RunScheduledTask will call
// TimeTicks::Now() (which unlike task_environment()->NowTicks(), we can't
// fake), and miscalculate the remaining delay at which to fire the timer.
task_environment()->FastForwardBy(base::TimeDelta::FromDays(1));
EXPECT_EQ(0, permission_context.permission_set_count());
EXPECT_EQ(CONTENT_SETTING_ASK,
permission_context.GetContentSettingFromMap(url, url));
// Should be blocked after 1-2 seconds. So 500ms is not enough.
web_contents()->WasShown();
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
EXPECT_EQ(0, permission_context.permission_set_count());
EXPECT_EQ(CONTENT_SETTING_ASK,
permission_context.GetContentSettingFromMap(url, url));
// But 5*500ms > 2 seconds, so it should now be blocked.
for (int n = 0; n < 4; n++)
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
EXPECT_EQ(1, permission_context.permission_set_count());
EXPECT_TRUE(permission_context.last_permission_set_persisted());
EXPECT_EQ(CONTENT_SETTING_BLOCK,
permission_context.last_permission_set_setting());
EXPECT_EQ(CONTENT_SETTING_BLOCK,
permission_context.GetContentSettingFromMap(url, url));
}
// Tests how multiple parallel permission requests get auto-denied in incognito.
TEST_F(IdleDetectionPermissionContextTest, TestParallelDenyInIncognito) {
TestIdleDetectionPermissionContext permission_context(
profile()->GetPrimaryOTRProfile());
GURL url("https://www.example.com");
NavigateAndCommit(url);
web_contents()->WasShown();
const permissions::PermissionRequestID id0(
web_contents()->GetMainFrame()->GetProcess()->GetID(),
web_contents()->GetMainFrame()->GetRoutingID(), 0);
const permissions::PermissionRequestID id1(
web_contents()->GetMainFrame()->GetProcess()->GetID(),
web_contents()->GetMainFrame()->GetRoutingID(), 1);
ASSERT_EQ(0, permission_context.permission_set_count());
ASSERT_FALSE(permission_context.last_permission_set_persisted());
ASSERT_EQ(CONTENT_SETTING_DEFAULT,
permission_context.last_permission_set_setting());
permission_context.RequestPermission(
web_contents(), id0, url, /*user_gesture=*/true, base::DoNothing());
permission_context.RequestPermission(
web_contents(), id1, url, /*user_gesture=*/true, base::DoNothing());
EXPECT_EQ(0, permission_context.permission_set_count());
EXPECT_EQ(CONTENT_SETTING_ASK,
permission_context.GetContentSettingFromMap(url, url));
// Fast forward up to 2.5 seconds. Stop as soon as the first permission
// request is auto-denied.
for (int n = 0; n < 5; n++) {
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
if (permission_context.permission_set_count())
break;
}
// Only the first permission request receives a response (crbug.com/577336).
EXPECT_EQ(1, permission_context.permission_set_count());
EXPECT_TRUE(permission_context.last_permission_set_persisted());
EXPECT_EQ(CONTENT_SETTING_BLOCK,
permission_context.last_permission_set_setting());
EXPECT_EQ(CONTENT_SETTING_BLOCK,
permission_context.GetContentSettingFromMap(url, url));
// After another 2.5 seconds, the second permission request should also have
// received a response.
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(2500));
EXPECT_EQ(2, permission_context.permission_set_count());
EXPECT_TRUE(permission_context.last_permission_set_persisted());
EXPECT_EQ(CONTENT_SETTING_BLOCK,
permission_context.last_permission_set_setting());
EXPECT_EQ(CONTENT_SETTING_BLOCK,
permission_context.GetContentSettingFromMap(url, url));
}
......@@ -7,24 +7,19 @@
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/containers/circular_deque.h"
#include "base/location.h"
#include "base/rand_util.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/timer/timer.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/visibility_timer_tab_helper.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#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/render_frame_host.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
#include "content/public/common/page_visibility_state.h"
#include "url/gurl.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
......@@ -38,146 +33,6 @@
#include "ui/message_center/public/cpp/notifier_id.h"
#endif // BUILDFLAG(ENABLE_EXTENSIONS)
namespace {
// At most one of these is attached to each WebContents. It allows posting
// delayed tasks whose timer only counts down whilst the WebContents is visible
// (and whose timer is reset whenever the WebContents stops being visible).
class VisibilityTimerTabHelper
: public content::WebContentsObserver,
public content::WebContentsUserData<VisibilityTimerTabHelper> {
public:
VisibilityTimerTabHelper(const VisibilityTimerTabHelper&) = delete;
VisibilityTimerTabHelper& operator=(const VisibilityTimerTabHelper&) = delete;
~VisibilityTimerTabHelper() override {}
// Runs |task| after the WebContents has been visible for a consecutive
// duration of at least |visible_delay|.
void PostTaskAfterVisibleDelay(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta visible_delay,
const permissions::PermissionRequestID& id);
// Deletes any earlier task(s) that match |id|.
void CancelTask(const permissions::PermissionRequestID& id);
// WebContentsObserver:
void OnVisibilityChanged(content::Visibility visibility) override;
void WebContentsDestroyed() override;
private:
friend class content::WebContentsUserData<VisibilityTimerTabHelper>;
explicit VisibilityTimerTabHelper(content::WebContents* contents);
void RunTask(base::OnceClosure task);
bool is_visible_;
struct Task {
Task(const permissions::PermissionRequestID& id,
std::unique_ptr<base::RetainingOneShotTimer> timer)
: id(id), timer(std::move(timer)) {}
// Move-only.
Task(Task&&) noexcept = default;
Task(const Task&) = delete;
Task& operator=(Task&& other) {
id = other.id;
timer = std::move(other.timer);
return *this;
}
permissions::PermissionRequestID id;
std::unique_ptr<base::RetainingOneShotTimer> timer;
};
base::circular_deque<Task> task_queue_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
WEB_CONTENTS_USER_DATA_KEY_IMPL(VisibilityTimerTabHelper)
VisibilityTimerTabHelper::VisibilityTimerTabHelper(
content::WebContents* contents)
: content::WebContentsObserver(contents) {
if (!contents->GetMainFrame()) {
is_visible_ = false;
} else {
switch (contents->GetMainFrame()->GetVisibilityState()) {
case content::PageVisibilityState::kHidden:
case content::PageVisibilityState::kHiddenButPainting:
is_visible_ = false;
break;
case content::PageVisibilityState::kVisible:
is_visible_ = true;
break;
}
}
}
void VisibilityTimerTabHelper::PostTaskAfterVisibleDelay(
const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta visible_delay,
const permissions::PermissionRequestID& id) {
if (web_contents()->IsBeingDestroyed())
return;
// Safe to use Unretained, as destroying |this| will destroy task_queue_,
// hence cancelling all timers.
// RetainingOneShotTimer is used which needs a RepeatingCallback, but we
// only have it run this callback a single time, and destroy it after.
auto timer = std::make_unique<base::RetainingOneShotTimer>(
from_here, visible_delay,
base::AdaptCallbackForRepeating(
base::BindOnce(&VisibilityTimerTabHelper::RunTask,
base::Unretained(this), std::move(task))));
DCHECK(!timer->IsRunning());
task_queue_.emplace_back(id, std::move(timer));
if (is_visible_ && task_queue_.size() == 1)
task_queue_.front().timer->Reset();
}
void VisibilityTimerTabHelper::CancelTask(
const permissions::PermissionRequestID& id) {
bool deleting_front = task_queue_.front().id == id;
base::EraseIf(task_queue_, [id](const Task& task) { return task.id == id; });
if (!task_queue_.empty() && is_visible_ && deleting_front)
task_queue_.front().timer->Reset();
}
void VisibilityTimerTabHelper::OnVisibilityChanged(
content::Visibility visibility) {
if (visibility == content::Visibility::VISIBLE) {
if (!is_visible_ && !task_queue_.empty())
task_queue_.front().timer->Reset();
is_visible_ = true;
} else {
if (is_visible_ && !task_queue_.empty())
task_queue_.front().timer->Stop();
is_visible_ = false;
}
}
void VisibilityTimerTabHelper::WebContentsDestroyed() {
task_queue_.clear();
}
void VisibilityTimerTabHelper::RunTask(base::OnceClosure task) {
DCHECK(is_visible_);
std::move(task).Run();
task_queue_.pop_front();
if (!task_queue_.empty())
task_queue_.front().timer->Reset();
}
} // namespace
// static
void NotificationPermissionContext::UpdatePermission(
content::BrowserContext* browser_context,
......@@ -300,7 +155,7 @@ void NotificationPermissionContext::DecidePermission(
requesting_origin, embedding_origin,
std::move(callback), true /* persist */,
CONTENT_SETTING_BLOCK),
base::TimeDelta::FromSecondsD(delay_seconds), id);
base::TimeDelta::FromSecondsD(delay_seconds));
return;
}
......
......@@ -79,8 +79,10 @@ void PermissionMenuModel::ExecuteCommand(int command_id, int event_flags) {
bool PermissionMenuModel::ShouldShowAllow(const GURL& url) {
switch (permission_.type) {
// Notifications does not support CONTENT_SETTING_ALLOW in incognito.
// Notifications and idle detection do not support CONTENT_SETTING_ALLOW in
// incognito.
case ContentSettingsType::NOTIFICATIONS:
case ContentSettingsType::IDLE_DETECTION:
return !permission_.is_incognito;
// Media only supports CONTENT_SETTING_ALLOW for secure origins.
case ContentSettingsType::MEDIASTREAM_MIC:
......
// Copyright 2020 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/visibility_timer_tab_helper.h"
#include <utility>
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/timer/timer.h"
#include "content/public/browser/visibility.h"
#include "content/public/browser/web_contents.h"
WEB_CONTENTS_USER_DATA_KEY_IMPL(VisibilityTimerTabHelper)
VisibilityTimerTabHelper::~VisibilityTimerTabHelper() = default;
void VisibilityTimerTabHelper::PostTaskAfterVisibleDelay(
const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta visible_delay) {
if (web_contents()->IsBeingDestroyed())
return;
// Safe to use Unretained, as destroying |this| will destroy task_queue_,
// hence cancelling all timers.
// RetainingOneShotTimer is used which needs a RepeatingCallback, but we
// only have it run this callback a single time, and destroy it after.
task_queue_.push_back(std::make_unique<base::RetainingOneShotTimer>(
from_here, visible_delay,
base::AdaptCallbackForRepeating(
base::BindOnce(&VisibilityTimerTabHelper::RunTask,
base::Unretained(this), std::move(task)))));
DCHECK(!task_queue_.back()->IsRunning());
if (web_contents()->GetVisibility() == content::Visibility::VISIBLE &&
task_queue_.size() == 1) {
task_queue_.front()->Reset();
}
}
void VisibilityTimerTabHelper::OnVisibilityChanged(
content::Visibility visibility) {
if (!task_queue_.empty()) {
if (visibility == content::Visibility::VISIBLE)
task_queue_.front()->Reset();
else
task_queue_.front()->Stop();
}
}
VisibilityTimerTabHelper::VisibilityTimerTabHelper(
content::WebContents* contents)
: content::WebContentsObserver(contents) {}
void VisibilityTimerTabHelper::RunTask(base::OnceClosure task) {
DCHECK_EQ(web_contents()->GetVisibility(), content::Visibility::VISIBLE);
task_queue_.pop_front();
if (!task_queue_.empty())
task_queue_.front()->Reset();
std::move(task).Run();
}
// Copyright 2020 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_VISIBILITY_TIMER_TAB_HELPER_H_
#define CHROME_BROWSER_VISIBILITY_TIMER_TAB_HELPER_H_
#include <memory>
#include "base/containers/circular_deque.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/browser/web_contents_user_data.h"
namespace base {
class RetainingOneShotTimer;
}
// At most one of these is attached to each WebContents. It allows posting
// delayed tasks whose timer only counts down whilst the WebContents is visible
// (and whose timer is reset whenever the WebContents stops being visible).
class VisibilityTimerTabHelper
: public content::WebContentsObserver,
public content::WebContentsUserData<VisibilityTimerTabHelper> {
public:
VisibilityTimerTabHelper(const VisibilityTimerTabHelper&&) = delete;
VisibilityTimerTabHelper& operator=(const VisibilityTimerTabHelper&&) =
delete;
~VisibilityTimerTabHelper() override;
// Runs |task| after the WebContents has been visible for a consecutive
// duration of at least |visible_delay|.
void PostTaskAfterVisibleDelay(const base::Location& from_here,
base::OnceClosure task,
base::TimeDelta visible_delay);
// WebContentsObserver:
void OnVisibilityChanged(content::Visibility visibility) override;
private:
friend class content::WebContentsUserData<VisibilityTimerTabHelper>;
explicit VisibilityTimerTabHelper(content::WebContents* contents);
void RunTask(base::OnceClosure task);
base::circular_deque<std::unique_ptr<base::RetainingOneShotTimer>>
task_queue_;
WEB_CONTENTS_USER_DATA_KEY_DECL();
};
#endif // CHROME_BROWSER_VISIBILITY_TIMER_TAB_HELPER_H_
// Copyright 2020 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/visibility_timer_tab_helper.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
#include "content/public/browser/web_contents.h"
class VisibilityTimerTabHelperTest : public ChromeRenderViewHostTestHarness {
public:
VisibilityTimerTabHelperTest()
: ChromeRenderViewHostTestHarness(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
};
TEST_F(VisibilityTimerTabHelperTest, Delay) {
bool task_executed = false;
VisibilityTimerTabHelper::CreateForWebContents(web_contents());
VisibilityTimerTabHelper::FromWebContents(web_contents())
->PostTaskAfterVisibleDelay(FROM_HERE,
base::BindLambdaForTesting([&task_executed] {
EXPECT_FALSE(task_executed);
task_executed = true;
}),
base::TimeDelta::FromSecondsD(1));
EXPECT_FALSE(task_executed);
// The task will be executed after 1 second, but the timer is reset whenever
// the tab is not visible, so these 500ms never add up to >= 1 second.
for (int n = 0; n < 10; n++) {
web_contents()->WasShown();
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
web_contents()->WasHidden();
}
EXPECT_FALSE(task_executed);
// Time elapsed whilst hidden is not counted.
// n.b. This line also clears out any old scheduled timer tasks. This is
// important, because otherwise Timer::Reset (triggered by
// VisibilityTimerTabHelper::WasShown) may choose to re-use an existing
// scheduled task, and when it fires Timer::RunScheduledTask will call
// TimeTicks::Now() (which unlike task_environment()->NowTicks(), we can't
// fake), and miscalculate the remaining delay at which to fire the timer.
task_environment()->FastForwardBy(base::TimeDelta::FromDays(1));
EXPECT_FALSE(task_executed);
// So 500ms is still not enough.
web_contents()->WasShown();
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
EXPECT_FALSE(task_executed);
// But 5*500ms > 1 second, so it should now be blocked.
for (int n = 0; n < 4; n++)
task_environment()->FastForwardBy(base::TimeDelta::FromMilliseconds(500));
EXPECT_TRUE(task_executed);
}
......@@ -3376,6 +3376,7 @@ test("unit_tests") {
"../browser/history/android/visit_sql_handler_unittest.cc",
"../browser/history/domain_diversity_reporter_unittest.cc",
"../browser/history/history_tab_helper_unittest.cc",
"../browser/idle/idle_detection_permission_context_unittest.cc",
"../browser/infobars/mock_infobar_service.cc",
"../browser/infobars/mock_infobar_service.h",
"../browser/install_verification/win/module_info_unittest.cc",
......@@ -3715,6 +3716,7 @@ test("unit_tests") {
"../browser/ui/webui/local_state/local_state_ui_unittest.cc",
"../browser/ui/webui/log_web_ui_url_unittest.cc",
"../browser/update_client/chrome_update_query_params_delegate_unittest.cc",
"../browser/visibility_timer_tab_helper_unittest.cc",
"../browser/vr/vr_tab_helper_unittest.cc",
"../browser/wake_lock/wake_lock_permission_context_unittest.cc",
"../browser/win/chrome_elf_init_unittest.cc",
......
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