Commit db4b7168 authored by yilkal's avatar yilkal Committed by Commit Bot

Adds WebTimeActivityProvider.

This CL adds WebTimeActivityProvider which is
BrowserListObserver, a TabStripModelObserver,
AppServiceWrapper::EventListener, and
WebTimeNavigationObserver::EventListener. It is hosted
in AppTimeController.

The instance of this class observes
the TabStripModel of each Browser in BrowserList. It also
observes the WebTimeNavigationObserver instances of each WebContent
hosted in each Browser window. The instance also observes
AppServiceWrapper to learn about the activation/deactivation of
chrome application windows.

WebTimeActivityProvider informs its observers when Chrome becomes
Active, Active with whitelisted content or inactive.

Bug: 1034551
Change-Id: I7d7dd3624ea4fb764c13e35f0ef542759cee8933
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2020522Reviewed-by: default avatarAga Wronska <agawronska@chromium.org>
Commit-Queue: Yilkal Abe <yilkal@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737492}
parent c646d0be
......@@ -822,6 +822,8 @@ source_set("chromeos") {
"child_accounts/time_limits/app_service_wrapper.h",
"child_accounts/time_limits/app_time_controller.cc",
"child_accounts/time_limits/app_time_controller.h",
"child_accounts/time_limits/app_time_limit_utils.cc",
"child_accounts/time_limits/app_time_limit_utils.h",
"child_accounts/time_limits/app_time_limits_whitelist_policy_wrapper.cc",
"child_accounts/time_limits/app_time_limits_whitelist_policy_wrapper.h",
"child_accounts/time_limits/app_time_notification_delegate.h",
......@@ -829,6 +831,8 @@ source_set("chromeos") {
"child_accounts/time_limits/app_time_policy_helpers.h",
"child_accounts/time_limits/app_types.cc",
"child_accounts/time_limits/app_types.h",
"child_accounts/time_limits/web_time_activity_provider.cc",
"child_accounts/time_limits/web_time_activity_provider.h",
"child_accounts/time_limits/web_time_limit_enforcer.cc",
"child_accounts/time_limits/web_time_limit_enforcer.h",
"child_accounts/time_limits/web_time_limit_interface.cc",
......
......@@ -4,15 +4,17 @@
#include "chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/time/default_tick_clock.h"
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_utils.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_notification_delegate.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/services/app_service/public/mojom/types.mojom.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "extensions/common/constants.h"
namespace chromeos {
namespace app_time {
......@@ -125,6 +127,9 @@ void AppActivityRegistry::OnAppActive(const AppId& app_id,
if (!base::Contains(activity_registry_, app_id))
return;
if (app_id == GetChromeAppId())
return;
AppDetails& app_details = activity_registry_[app_id];
DCHECK(IsAppAvailable(app_id));
......@@ -147,6 +152,9 @@ void AppActivityRegistry::OnAppInactive(const AppId& app_id,
if (!base::Contains(activity_registry_, app_id))
return;
if (app_id == GetChromeAppId())
return;
std::set<aura::Window*>& active_windows =
activity_registry_[app_id].active_windows;
......@@ -306,6 +314,30 @@ void AppActivityRegistry::UpdateAppLimits(
// TODO(agawronska): Propagate information about the limit changes.
}
void AppActivityRegistry::OnChromeAppActivityChanged(
ChromeAppActivityState state,
base::Time timestamp) {
AppId chrome_app_id = GetChromeAppId();
if (!base::Contains(activity_registry_, chrome_app_id))
return;
AppDetails& details = activity_registry_[chrome_app_id];
bool was_active = details.activity.is_active();
bool is_active = (state == ChromeAppActivityState::kActive);
// No change in state.
if (was_active == is_active)
return;
if (is_active) {
SetAppActive(chrome_app_id, timestamp);
return;
}
SetAppInactive(chrome_app_id, timestamp);
}
void AppActivityRegistry::OnResetTimeReached(base::Time timestamp) {
for (std::pair<const AppId, AppDetails>& info : activity_registry_) {
const AppId& app = info.first;
......
......@@ -103,6 +103,10 @@ class AppActivityRegistry : public AppServiceWrapper::EventListener {
// Reset time has been reached at |timestamp|.
void OnResetTimeReached(base::Time timestamp);
// Called from WebTimeActivityProvider to update chrome app state.
void OnChromeAppActivityChanged(ChromeAppActivityState state,
base::Time timestamp);
private:
// Bundles detailed data stored for a specific app.
struct AppDetails {
......
......@@ -40,12 +40,13 @@ AppId AppIdFromAppUpdate(const apps::AppUpdate& update) {
// Gets AppId from |update|.
AppId AppIdFromInstanceUpdate(const apps::InstanceUpdate& update,
apps::AppRegistryCache* app_cache) {
base::Optional<AppId> app_id;
AppId app_id(apps::mojom::AppType::kUnknown, update.AppId());
app_cache->ForOneApp(update.AppId(),
[&app_id](const apps::AppUpdate& update) {
app_id = AppIdFromAppUpdate(update);
});
return app_id.value();
return app_id;
}
// Gets app service id from |app_id|.
......
......@@ -14,6 +14,7 @@
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limits_whitelist_policy_wrapper.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_policy_helpers.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_types.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_activity_provider.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_features.h"
......@@ -71,7 +72,10 @@ AppTimeController::AppTimeController(Profile* profile)
: app_service_wrapper_(std::make_unique<AppServiceWrapper>(profile)),
app_registry_(
std::make_unique<AppActivityRegistry>(app_service_wrapper_.get(),
this)) {
this)),
web_time_activity_provider_(std::make_unique<WebTimeActivityProvider>(
this,
app_service_wrapper_.get())) {
DCHECK(profile);
if (WebTimeLimitEnforcer::IsEnabled())
......
......@@ -31,6 +31,7 @@ namespace app_time {
class AppActivityRegistry;
class AppServiceWrapper;
class WebTimeLimitEnforcer;
class WebTimeActivityProvider;
// Coordinates per-app time limit for child user.
class AppTimeController : public SystemClockClient::Observer,
......@@ -88,6 +89,14 @@ class AppTimeController : public SystemClockClient::Observer,
AppActivityRegistry* app_registry() { return app_registry_.get(); }
const WebTimeActivityProvider* web_time_activity_provider() const {
return web_time_activity_provider_.get();
}
WebTimeActivityProvider* web_time_activity_provider() {
return web_time_activity_provider_.get();
}
private:
void RegisterProfilePrefObservers(PrefService* pref_service);
void TimeLimitsPolicyUpdated(const std::string& pref_name);
......@@ -115,6 +124,7 @@ class AppTimeController : public SystemClockClient::Observer,
std::unique_ptr<AppServiceWrapper> app_service_wrapper_;
std::unique_ptr<AppActivityRegistry> app_registry_;
std::unique_ptr<WebTimeActivityProvider> web_time_activity_provider_;
std::unique_ptr<WebTimeLimitEnforcer> web_time_enforcer_;
// Used to observe when policy preferences change.
......
// 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/chromeos/child_accounts/time_limits/app_time_limit_utils.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_types.h"
#include "chrome/services/app_service/public/mojom/types.mojom.h"
#include "extensions/common/constants.h"
namespace chromeos {
namespace app_time {
AppId GetChromeAppId() {
return AppId(apps::mojom::AppType::kExtension, extension_misc::kChromeAppId);
}
} // namespace app_time
} // namespace chromeos
// 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_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_APP_TIME_LIMIT_UTILS_H_
#define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_APP_TIME_LIMIT_UTILS_H_
namespace chromeos {
namespace app_time {
class AppId;
AppId GetChromeAppId();
} // namespace app_time
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_APP_TIME_LIMIT_UTILS_H_
......@@ -54,6 +54,18 @@ enum class AppNotification {
kTimeLimitReached,
};
enum class ChromeAppActivityState {
// The browser is active and hosts urls in its active tab which are not
// whitelisted.
kActive,
// Same as |kActive| except the urls the browser hosts are whitelisted.
kActiveWhitelisted,
// The browser window is not active.
kInactive,
};
// Identifies an app for app time limits.
// Different types of use different identifier format. ARC++ apps are identified
// by Android package name. Other types of apps use 32 character long Chrome
......
// 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/chromeos/child_accounts/time_limits/web_time_activity_provider.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_controller.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_utils.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_types.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_navigation_observer.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "content/public/browser/web_contents.h"
#include "ui/aura/window.h"
namespace chromeos {
namespace app_time {
namespace {
const Browser* GetBrowserForWindow(const aura::Window* window) {
BrowserList* list = BrowserList::GetInstance();
for (const Browser* browser : *list) {
if (browser->window()->GetNativeWindow() == window)
return browser;
}
return nullptr;
}
const Browser* GetBrowserForTabStripModel(const TabStripModel* model) {
BrowserList* list = BrowserList::GetInstance();
for (const Browser* browser : *list) {
if (browser->tab_strip_model() == model)
return browser;
}
NOTREACHED();
return nullptr;
}
const Browser* GetBrowserForWebContents(const content::WebContents* contents) {
BrowserList* list = BrowserList::GetInstance();
for (const Browser* browser : *list) {
const auto* tab_strip_model = browser->tab_strip_model();
for (int i = 0; i < tab_strip_model->count(); i++) {
if (tab_strip_model->GetWebContentsAt(i) == contents)
return browser;
}
}
NOTREACHED();
return nullptr;
}
} // namespace
WebTimeActivityProvider::WebTimeActivityProvider(
AppTimeController* app_time_controller,
AppServiceWrapper* app_service_wrapper)
: app_time_controller_(app_time_controller),
app_service_wrapper_(app_service_wrapper) {
DCHECK(app_time_controller_);
DCHECK(app_service_wrapper_);
BrowserList::GetInstance()->AddObserver(this);
app_service_wrapper_->AddObserver(this);
}
WebTimeActivityProvider::~WebTimeActivityProvider() {
BrowserList::GetInstance()->RemoveObserver(this);
TabStripModelObserver::StopObservingAll(this);
app_service_wrapper_->RemoveObserver(this);
for (auto* navigation_observer : navigation_observers_) {
navigation_observer->RemoveObserver(this);
}
}
void WebTimeActivityProvider::OnWebActivityChanged(
const WebTimeNavigationObserver::NavigationInfo& info) {
if (info.is_web_app)
return;
const Browser* browser = GetBrowserForWebContents(info.web_contents);
// The browser window is not active. This may happen when a navigation
// finishes in the background.
if (!base::Contains(browser_activity_, browser))
return;
// Navigation finished in a background tab. Return.
if (browser->tab_strip_model()->GetActiveWebContents() != info.web_contents)
return;
browser_activity_[browser] = info.web_contents;
MaybeNotifyStateChange(base::Time::Now());
}
void WebTimeActivityProvider::WebTimeNavigationObserverDestroyed(
WebTimeNavigationObserver* navigation_observer) {
navigation_observer->RemoveObserver(this);
navigation_observers_.erase(navigation_observer);
}
void WebTimeActivityProvider::OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) {
if (change.type() == TabStripModelChange::Type::kInserted)
TabsInserted(change.GetInsert());
const Browser* browser = GetBrowserForTabStripModel(tab_strip_model);
// If the Browser is not the active browser, simply return.
if (!base::Contains(browser_activity_, browser))
return;
// Let's check if the active tab changed, or the content::WebContents in the
// active tab was replaced:
bool active_tab_changed = selection.active_tab_changed();
bool web_content_replaced =
change.type() == TabStripModelChange::Type::kReplaced;
if (!(active_tab_changed || web_content_replaced))
return;
browser_activity_[browser] = tab_strip_model->GetActiveWebContents();
MaybeNotifyStateChange(base::Time::Now());
}
void WebTimeActivityProvider::OnBrowserAdded(Browser* browser) {
browser->tab_strip_model()->AddObserver(this);
}
void WebTimeActivityProvider::OnBrowserRemoved(Browser* browser) {
if (!base::Contains(browser_activity_, browser))
return;
browser_activity_.erase(browser);
MaybeNotifyStateChange(base::Time::Now());
}
void WebTimeActivityProvider::OnAppActive(const AppId& app_id,
aura::Window* window,
base::Time timestamp) {
if (app_id != GetChromeAppId())
return;
const Browser* browser = GetBrowserForWindow(window);
if (!browser)
return;
browser_activity_[browser] =
browser->tab_strip_model()->GetActiveWebContents();
MaybeNotifyStateChange(timestamp);
}
void WebTimeActivityProvider::OnAppInactive(const AppId& app_id,
aura::Window* window,
base::Time timestamp) {
if (app_id != GetChromeAppId())
return;
const Browser* browser = GetBrowserForWindow(window);
if (!browser)
return;
if (!base::Contains(browser_activity_, browser))
return;
browser_activity_.erase(browser);
MaybeNotifyStateChange(timestamp);
}
void WebTimeActivityProvider::TabsInserted(
const TabStripModelChange::Insert* insert) {
for (const TabStripModelChange::ContentsWithIndex& content_with_index :
insert->contents) {
WebTimeNavigationObserver* navigation_observer =
WebTimeNavigationObserver::FromWebContents(content_with_index.contents);
// Continue if the navigation observer is not created or if |this| already
// observes it.
if (!navigation_observer ||
base::Contains(navigation_observers_, navigation_observer)) {
continue;
}
navigation_observer->AddObserver(this);
navigation_observers_.insert(navigation_observer);
}
}
void WebTimeActivityProvider::MaybeNotifyStateChange(base::Time timestamp) {
ChromeAppActivityState new_state = CalculateChromeAppActivityState();
if (new_state == chrome_app_activity_state_)
return;
chrome_app_activity_state_ = new_state;
app_time_controller_->app_registry()->OnChromeAppActivityChanged(new_state,
timestamp);
}
ChromeAppActivityState
WebTimeActivityProvider::CalculateChromeAppActivityState() const {
int active_count = 0;
int active_whitelisted_count = 0;
for (const std::pair<const Browser*, content::WebContents*>& elem :
browser_activity_) {
if (!elem.second)
continue;
const WebTimeNavigationObserver* observer =
WebTimeNavigationObserver::FromWebContents(elem.second);
// If |observer| is not instantiated, that means that
// WebTimeNavigationObserver::MaybeCreateForWebContents didn't create it.
// This means that WebTimeLimitEnforcer::IsEnabled returned false.
// Mark it as active whitelisted.
if (!observer) {
active_whitelisted_count++;
continue;
}
const WebTimeNavigationObserver::NavigationInfo& info =
observer->last_navigation_info();
if (info.is_web_app)
continue;
WebTimeLimitEnforcer* enforcer = app_time_controller_->web_time_enforcer();
if (info.is_error || enforcer->IsURLWhitelisted(info.url)) {
active_whitelisted_count++;
continue;
}
active_count++;
}
if (active_count > 0)
return ChromeAppActivityState::kActive;
if (active_whitelisted_count > 0)
return ChromeAppActivityState::kActiveWhitelisted;
return ChromeAppActivityState::kInactive;
}
} // namespace app_time
} // namespace chromeos
// 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_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_WEB_TIME_ACTIVITY_PROVIDER_H_
#define CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_WEB_TIME_ACTIVITY_PROVIDER_H_
#include <map>
#include <set>
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_service_wrapper.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_navigation_observer.h"
#include "chrome/browser/ui/browser_list_observer.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
namespace aura {
class Window;
} // namespace aura
namespace base {
class Time;
} // namespace base
namespace content {
class WebContents;
} // namespace content
class Browser;
namespace chromeos {
namespace app_time {
class AppId;
class AppTimeController;
class AppServiceWrapper;
enum class ChromeAppActivityState;
class WebTimeActivityProvider : public WebTimeNavigationObserver::EventListener,
public BrowserListObserver,
public TabStripModelObserver,
public AppServiceWrapper::EventListener {
public:
WebTimeActivityProvider(AppTimeController* app_time_controller,
AppServiceWrapper* app_service_wrapper);
WebTimeActivityProvider(const WebTimeActivityProvider&) = delete;
WebTimeActivityProvider& operator=(const WebTimeActivityProvider&) = delete;
~WebTimeActivityProvider() override;
// WebTimeNavigationObserver::EventListener:
void OnWebActivityChanged(
const WebTimeNavigationObserver::NavigationInfo& info) override;
void WebTimeNavigationObserverDestroyed(
WebTimeNavigationObserver* navigation_observer) override;
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override;
// BrowserListObserver:
void OnBrowserAdded(Browser* browser) override;
void OnBrowserRemoved(Browser* browser) override;
// AppServiceWrapper::EventListener:
void OnAppActive(const AppId& app_id,
aura::Window* window,
base::Time timestamp) override;
void OnAppInactive(const AppId& app_id,
aura::Window* window,
base::Time timestamp) override;
ChromeAppActivityState chrome_app_activty_state() const {
return chrome_app_activity_state_;
}
private:
void TabsInserted(const TabStripModelChange::Insert* insert);
// Notifies AppActivityRegistry if there is a change in ChromeAppState.
void MaybeNotifyStateChange(base::Time timestamp);
// Calculates whether the Chrome app is kActive, kActiveWhitelisted or
// kInactive.
ChromeAppActivityState CalculateChromeAppActivityState() const;
// Reference to AppTimeController. Owned by ChildUserService.
AppTimeController* const app_time_controller_;
// Reference to AppServiceWrapper. Owned by AppTimeController.
AppServiceWrapper* const app_service_wrapper_;
// The set of navigation observers |this| instance is listening to.
std::set<WebTimeNavigationObserver*> navigation_observers_;
// A map between active browser instances and their selected WebContents.
std::map<const Browser*, content::WebContents*> browser_activity_;
// The default chrome app activity state.
ChromeAppActivityState chrome_app_activity_state_ =
ChromeAppActivityState::kInactive;
};
} // namespace app_time
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_CHILD_ACCOUNTS_TIME_LIMITS_WEB_TIME_ACTIVITY_PROVIDER_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/chromeos/child_accounts/time_limits/web_time_activity_provider.h"
#include <vector>
#include "base/callback.h"
#include "base/feature_list.h"
#include "base/json/json_writer.h"
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/child_accounts/child_user_service.h"
#include "chrome/browser/chromeos/child_accounts/child_user_service_factory.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_controller.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limit_utils.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/app_time_limits_whitelist_policy_test_utils.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer.h"
#include "chrome/browser/chromeos/child_accounts/time_limits/web_time_navigation_observer.h"
#include "chrome/browser/chromeos/login/test/scoped_policy_update.h"
#include "chrome/browser/chromeos/policy/user_policy_test_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
#include "chrome/browser/supervised_user/navigation_finished_waiter.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
#include "chrome/common/chrome_features.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/mixin_based_in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/reload_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
#include "net/dns/mock_host_resolver.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/window_open_disposition.h"
#include "url/gurl.h"
namespace {
constexpr char kExampleHost1[] = "www.example.com";
constexpr char kExampleHost2[] = "www.example2.com";
} // namespace
class WebTimeCalculationBrowserTest : public MixinBasedInProcessBrowserTest {
public:
WebTimeCalculationBrowserTest() = default;
WebTimeCalculationBrowserTest(const WebTimeCalculationBrowserTest&) = delete;
WebTimeCalculationBrowserTest& operator=(
const WebTimeCalculationBrowserTest&) = delete;
// InProcessBrowserTest:
void SetUp() override;
void SetUpOnMainThread() override;
void TearDown() override;
void WhitelistUrlRegx(const std::string& url);
Browser* DetachTabToNewBrowser(Browser* browser, int tab_index);
content::WebContents* Navigate(Browser* browser,
const std::string& url_in,
WindowOpenDisposition disposition);
chromeos::app_time::ChromeAppActivityState GetChromeAppActivityState();
private:
void UpdatePolicy();
Profile* profile_;
base::test::ScopedFeatureList scoped_feature_list_;
chromeos::app_time::AppTimeLimitsWhitelistPolicyBuilder builder_;
chromeos::LoggedInUserMixin logged_in_user_mixin_{
&mixin_host_, chromeos::LoggedInUserMixin::LogInType::kChild,
embedded_test_server(), this};
};
void WebTimeCalculationBrowserTest::SetUp() {
scoped_feature_list_.InitWithFeatures(
/* enabled_features */ {{features::kPerAppTimeLimits,
features::kWebTimeLimits}},
/* disabled_features */ {{}});
builder_.SetUp();
MixinBasedInProcessBrowserTest::SetUp();
}
void WebTimeCalculationBrowserTest::SetUpOnMainThread() {
MixinBasedInProcessBrowserTest::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Started());
// Resolve everything to localhost.
host_resolver()->AddIPLiteralRule("*", "127.0.0.1", "localhost");
logged_in_user_mixin_.LogInUser(false /*issue_any_scope_token*/,
true /*wait_for_active_session*/,
true /*request_policy_update*/);
profile_ = browser()->profile();
// During tests, AppService doesn't notify AppActivityRegistry that chrome app
// is installed. Mark chrome as installed here.
chromeos::ChildUserService* service =
chromeos::ChildUserServiceFactory::GetForBrowserContext(profile_);
chromeos::ChildUserService::TestApi test_api(service);
test_api.app_time_controller()->app_registry()->OnAppInstalled(
chromeos::app_time::GetChromeAppId());
}
void WebTimeCalculationBrowserTest::TearDown() {
builder_.Clear();
MixinBasedInProcessBrowserTest::TearDown();
}
void WebTimeCalculationBrowserTest::WhitelistUrlRegx(const std::string& url) {
builder_.AppendToWhitelistUrlList(url);
UpdatePolicy();
}
Browser* WebTimeCalculationBrowserTest::DetachTabToNewBrowser(Browser* browser,
int tab_index) {
std::vector<int> tabs{tab_index};
browser->tab_strip_model()->delegate()->MoveTabsToNewWindow(tabs);
return BrowserList::GetInstance()->GetLastActive();
}
content::WebContents* WebTimeCalculationBrowserTest::Navigate(
Browser* browser,
const std::string& url_in,
WindowOpenDisposition disposition) {
GURL url =
embedded_test_server()->GetURL(url_in, "/supervised_user/simple.html");
NavigateParams params(browser, url, ui::PageTransition::PAGE_TRANSITION_LINK);
params.disposition = disposition;
ui_test_utils::NavigateToURL(&params);
return params.navigated_or_inserted_contents;
}
chromeos::app_time::ChromeAppActivityState
WebTimeCalculationBrowserTest::GetChromeAppActivityState() {
chromeos::ChildUserService* service =
chromeos::ChildUserServiceFactory::GetForBrowserContext(profile_);
chromeos::ChildUserService::TestApi test_api(service);
auto* web_time_activity_provider =
test_api.app_time_controller()->web_time_activity_provider();
return web_time_activity_provider->chrome_app_activty_state();
}
void WebTimeCalculationBrowserTest::UpdatePolicy() {
std::string policy_value;
base::JSONWriter::Write(builder_.value(), &policy_value);
logged_in_user_mixin_.GetUserPolicyMixin()
->RequestPolicyUpdate()
->policy_payload()
->mutable_perapptimelimitswhitelist()
->set_value(policy_value);
logged_in_user_mixin_.GetUserPolicyTestHelper()->RefreshPolicyAndWait(
profile_);
}
IN_PROC_BROWSER_TEST_F(WebTimeCalculationBrowserTest, TabSelectionChanges) {
WhitelistUrlRegx(kExampleHost1);
Navigate(browser(), kExampleHost1, WindowOpenDisposition::CURRENT_TAB);
// Create a new tab and navigate it to some url.
Navigate(browser(), kExampleHost2, WindowOpenDisposition::NEW_FOREGROUND_TAB);
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kActive,
GetChromeAppActivityState());
browser()->tab_strip_model()->ActivateTabAt(0);
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kActiveWhitelisted,
GetChromeAppActivityState());
bool destroyed = browser()->tab_strip_model()->CloseWebContentsAt(
0, TabStripModel::CloseTypes::CLOSE_USER_GESTURE);
EXPECT_TRUE(destroyed);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kActive,
GetChromeAppActivityState());
}
IN_PROC_BROWSER_TEST_F(WebTimeCalculationBrowserTest, TabDetached) {
WhitelistUrlRegx(kExampleHost1);
Navigate(browser(), kExampleHost1, WindowOpenDisposition::CURRENT_TAB);
// Create a new tab and navigate it to some url.
Navigate(browser(), kExampleHost2, WindowOpenDisposition::NEW_FOREGROUND_TAB);
browser()->tab_strip_model()->ActivateTabAt(0);
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kActiveWhitelisted,
GetChromeAppActivityState());
Browser* new_browser = DetachTabToNewBrowser(browser(), 1);
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kActive,
GetChromeAppActivityState());
// Now we have two browser windows. One hosting a whitelisted url and the
// other hosting a non whitelisted url.
EXPECT_TRUE(new_browser->tab_strip_model()->CloseWebContentsAt(
0, TabStripModel::CloseTypes::CLOSE_USER_GESTURE));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kActiveWhitelisted,
GetChromeAppActivityState());
EXPECT_TRUE(browser()->tab_strip_model()->CloseWebContentsAt(
0, TabStripModel::CloseTypes::CLOSE_USER_GESTURE));
base::RunLoop().RunUntilIdle();
EXPECT_EQ(chromeos::app_time::ChromeAppActivityState::kInactive,
GetChromeAppActivityState());
}
// TODO(yilkal): Write test to check that going to a URL in the current tab of
// the first browser will result in chrome being active or active whitelisted.
......@@ -45,7 +45,7 @@ class WebTimeNavigationObserver
public:
virtual void OnWebActivityChanged(const NavigationInfo& info) {}
virtual void WebTimeNavigationObserverDestroyed(
const WebTimeNavigationObserver* observer) {}
WebTimeNavigationObserver* observer) {}
};
static void MaybeCreateForWebContents(content::WebContents* web_contents);
......
// 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/supervised_user/navigation_finished_waiter.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/web_contents.h"
NavigationFinishedWaiter::NavigationFinishedWaiter(
content::WebContents* web_contents,
int frame_id,
const GURL& url)
: content::WebContentsObserver(web_contents),
frame_id_(frame_id),
url_(url) {}
NavigationFinishedWaiter::NavigationFinishedWaiter(
content::WebContents* web_contents,
const GURL& url)
: content::WebContentsObserver(web_contents) {
frame_id_ = web_contents->GetMainFrame()->GetFrameTreeNodeId();
url_ = url;
}
void NavigationFinishedWaiter::Wait() {
if (did_finish_)
return;
run_loop_.Run();
}
void NavigationFinishedWaiter::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (!navigation_handle->HasCommitted())
return;
if (navigation_handle->GetFrameTreeNodeId() != frame_id_ ||
navigation_handle->GetURL() != url_)
return;
did_finish_ = true;
run_loop_.Quit();
}
// 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_SUPERVISED_USER_NAVIGATION_FINISHED_WAITER_H_
#define CHROME_BROWSER_SUPERVISED_USER_NAVIGATION_FINISHED_WAITER_H_
#include "base/run_loop.h"
#include "content/public/browser/web_contents_observer.h"
#include "url/gurl.h"
namespace content {
class NavigationHandle;
class WebContents;
} // namespace content
// Helper class to wait for a particular navigation in a particular render
// frame in tests.
class NavigationFinishedWaiter : public content::WebContentsObserver {
public:
NavigationFinishedWaiter(content::WebContents* web_contents,
int frame_id,
const GURL& url);
NavigationFinishedWaiter(content::WebContents* web_contents, const GURL& url);
NavigationFinishedWaiter(const NavigationFinishedWaiter&) = delete;
NavigationFinishedWaiter& operator=(const NavigationFinishedWaiter&) = delete;
~NavigationFinishedWaiter() override = default;
void Wait();
// content::WebContentsObserver:
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
private:
int frame_id_;
GURL url_;
bool did_finish_ = false;
base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
};
#endif // CHROME_BROWSER_SUPERVISED_USER_NAVIGATION_FINISHED_WAITER_H_
......@@ -13,6 +13,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_key.h"
#include "chrome/browser/supervised_user/logged_in_user_mixin.h"
#include "chrome/browser/supervised_user/navigation_finished_waiter.h"
#include "chrome/browser/supervised_user/permission_request_creator_mock.h"
#include "chrome/browser/supervised_user/supervised_user_constants.h"
#include "chrome/browser/supervised_user/supervised_user_features.h"
......@@ -46,47 +47,6 @@ static const char* kExampleHost2 = "www.example2.com";
static const char* kIframeHost1 = "www.iframe1.com";
static const char* kIframeHost2 = "www.iframe2.com";
// Helper class to wait for a particular navigation in a particular render
// frame.
class NavigationFinishedWaiter : public content::WebContentsObserver {
public:
NavigationFinishedWaiter(WebContents* web_contents,
int frame_id,
const GURL& url)
: content::WebContentsObserver(web_contents),
frame_id_(frame_id),
url_(url) {}
~NavigationFinishedWaiter() override = default;
void Wait() {
if (did_finish_)
return;
run_loop_.Run();
}
// content::WebContentsObserver:
void DidFinishNavigation(
content::NavigationHandle* navigation_handle) override;
private:
int frame_id_;
GURL url_;
bool did_finish_ = false;
base::RunLoop run_loop_{base::RunLoop::Type::kNestableTasksAllowed};
DISALLOW_COPY_AND_ASSIGN(NavigationFinishedWaiter);
};
void NavigationFinishedWaiter::DidFinishNavigation(
content::NavigationHandle* navigation_handle) {
if (navigation_handle->GetFrameTreeNodeId() != frame_id_ ||
navigation_handle->GetURL() != url_)
return;
did_finish_ = true;
run_loop_.Quit();
}
// Class to keep track of iframes created and destroyed.
class RenderFrameTracker : public content::WebContentsObserver {
public:
......
......@@ -2119,6 +2119,7 @@ if (!is_android) {
"../browser/chromeos/child_accounts/time_limits/app_time_limits_policy_builder.h",
"../browser/chromeos/child_accounts/time_limits/app_time_limits_whitelist_policy_test_utils.cc",
"../browser/chromeos/child_accounts/time_limits/app_time_limits_whitelist_policy_test_utils.h",
"../browser/chromeos/child_accounts/time_limits/web_time_calculation_browsertest.cc",
"../browser/chromeos/child_accounts/time_limits/web_time_limit_enforcer_browsertest.cc",
"../browser/chromeos/chrome_content_browser_client_chromeos_part_browsertest.cc",
"../browser/chromeos/customization/customization_document_browsertest.cc",
......@@ -2696,6 +2697,8 @@ if (!is_android) {
sources += [
"../browser/supervised_user/logged_in_user_mixin.cc",
"../browser/supervised_user/logged_in_user_mixin.h",
"../browser/supervised_user/navigation_finished_waiter.cc",
"../browser/supervised_user/navigation_finished_waiter.h",
"../browser/supervised_user/permission_request_creator_mock.cc",
"../browser/supervised_user/permission_request_creator_mock.h",
"../browser/supervised_user/supervised_user_navigation_throttle_browsertest.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