Commit 1ec54376 authored by bauerb's avatar bauerb Committed by Commit bot

Don't notify existing observers in SupervisedUserWhitelistService::AddSiteListsChangedCallback().

The additional callbacks caused the SupervisedUserUrlFilter to update its filter unnecessarily, which in turn flakily quit the observer run loop too early.

This CL fixes the flakiness by using the main thread message loop for the SupervisedUserUrlFilter (removing non-determinism), spreading out the synchronization points for site list updates and URL filter updates, and adding additional sanity checks about observed changes.

BUG=452071

Review URL: https://codereview.chromium.org/875423002

Cr-Commit-Position: refs/heads/master@{#313483}
parent 24ce8ba9
...@@ -46,34 +46,108 @@ void OnProfileDownloadedFail(const base::string16& full_name) { ...@@ -46,34 +46,108 @@ void OnProfileDownloadedFail(const base::string16& full_name) {
ASSERT_TRUE(false) << "Profile download should not have succeeded."; ASSERT_TRUE(false) << "Profile download should not have succeeded.";
} }
class SupervisedUserURLFilterObserver : // Base class for helper objects that wait for certain events to happen.
public SupervisedUserURLFilter::Observer { // This class will ensure that calls to QuitRunLoop() (triggered by a subclass)
// are balanced with Wait() calls.
class AsyncTestHelper {
public: public:
explicit SupervisedUserURLFilterObserver(SupervisedUserURLFilter* url_filter) void Wait() {
: url_filter_(url_filter) { run_loop_->Run();
Reset(); Reset();
url_filter_->AddObserver(this);
} }
~SupervisedUserURLFilterObserver() { protected:
url_filter_->RemoveObserver(this); AsyncTestHelper() {
// |quit_called_| will be initialized in Reset().
Reset();
} }
void Wait() { ~AsyncTestHelper() {
message_loop_runner_->Run(); EXPECT_FALSE(quit_called_);
Reset();
} }
// SupervisedUserURLFilter::Observer void QuitRunLoop() {
void OnSiteListUpdated() override { message_loop_runner_->Quit(); } // QuitRunLoop() can not be called more than once between calls to Wait().
ASSERT_FALSE(quit_called_);
quit_called_ = true;
run_loop_->Quit();
}
private: private:
void Reset() { void Reset() {
message_loop_runner_ = new MessageLoopRunner; quit_called_ = false;
run_loop_.reset(new base::RunLoop);
}
scoped_ptr<base::RunLoop> run_loop_;
bool quit_called_;
DISALLOW_COPY_AND_ASSIGN(AsyncTestHelper);
};
class SupervisedUserURLFilterObserver
: public AsyncTestHelper,
public SupervisedUserURLFilter::Observer {
public:
SupervisedUserURLFilterObserver() : scoped_observer_(this) {}
~SupervisedUserURLFilterObserver() {}
void Init(SupervisedUserURLFilter* url_filter) {
scoped_observer_.Add(url_filter);
}
// SupervisedUserURLFilter::Observer
void OnSiteListUpdated() override {
QuitRunLoop();
}
private:
ScopedObserver<SupervisedUserURLFilter, SupervisedUserURLFilter::Observer>
scoped_observer_;
DISALLOW_COPY_AND_ASSIGN(SupervisedUserURLFilterObserver);
};
class SiteListObserver : public AsyncTestHelper {
public:
SiteListObserver() {}
~SiteListObserver() {}
void Init(SupervisedUserWhitelistService* service) {
service->AddSiteListsChangedCallback(base::Bind(
&SiteListObserver::OnSiteListsChanged, base::Unretained(this)));
// The initial call to AddSiteListsChangedCallback will call
// OnSiteListsChanged(), so we balance it out by calling Wait().
Wait();
}
const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists() {
return site_lists_;
}
const std::vector<SupervisedUserSiteList::Site>& sites() {
return sites_;
} }
SupervisedUserURLFilter* url_filter_; private:
scoped_refptr<MessageLoopRunner> message_loop_runner_; void OnSiteListsChanged(
const std::vector<scoped_refptr<SupervisedUserSiteList>>& site_lists) {
site_lists_ = site_lists;
sites_.clear();
for (const scoped_refptr<SupervisedUserSiteList>& site_list : site_lists) {
const std::vector<SupervisedUserSiteList::Site>& sites =
site_list->sites();
sites_.insert(sites_.end(), sites.begin(), sites.end());
}
QuitRunLoop();
}
std::vector<scoped_refptr<SupervisedUserSiteList>> site_lists_;
std::vector<SupervisedUserSiteList::Site> sites_;
DISALLOW_COPY_AND_ASSIGN(SiteListObserver);
}; };
class AsyncResultHolder { class AsyncResultHolder {
...@@ -291,9 +365,20 @@ class SupervisedUserServiceExtensionTestBase ...@@ -291,9 +365,20 @@ class SupervisedUserServiceExtensionTestBase
SupervisedUserService* service = SupervisedUserService* service =
SupervisedUserServiceFactory::GetForProfile(profile_.get()); SupervisedUserServiceFactory::GetForProfile(profile_.get());
service->Init(); service->Init();
service->GetWhitelistService()->AddSiteListsChangedCallback( site_list_observer_.Init(service->GetWhitelistService());
base::Bind(&SupervisedUserServiceExtensionTestBase::OnSiteListsChanged,
base::Unretained(this))); SupervisedUserURLFilter* url_filter = service->GetURLFilterForUIThread();
url_filter->SetBlockingTaskRunnerForTesting(
base::MessageLoopProxy::current());
url_filter_observer_.Init(url_filter);
// Wait for the initial update to finish.
url_filter_observer_.Wait();
}
void TearDown() override {
// Flush the message loop, to ensure all posted tasks run.
base::RunLoop().RunUntilIdle();
} }
protected: protected:
...@@ -322,21 +407,10 @@ class SupervisedUserServiceExtensionTestBase ...@@ -322,21 +407,10 @@ class SupervisedUserServiceExtensionTestBase
return extension; return extension;
} }
void OnSiteListsChanged(
const std::vector<scoped_refptr<SupervisedUserSiteList> >& site_lists) {
site_lists_ = site_lists;
sites_.clear();
for (const scoped_refptr<SupervisedUserSiteList>& site_list : site_lists) {
const std::vector<SupervisedUserSiteList::Site>& sites =
site_list->sites();
sites_.insert(sites_.end(), sites.begin(), sites.end());
}
}
bool is_supervised_; bool is_supervised_;
extensions::ScopedCurrentChannel channel_; extensions::ScopedCurrentChannel channel_;
std::vector<scoped_refptr<SupervisedUserSiteList> > site_lists_; SiteListObserver site_list_observer_;
std::vector<SupervisedUserSiteList::Site> sites_; SupervisedUserURLFilterObserver url_filter_observer_;
}; };
class SupervisedUserServiceExtensionTestUnsupervised class SupervisedUserServiceExtensionTestUnsupervised
...@@ -374,11 +448,7 @@ TEST_F(SupervisedUserServiceExtensionTestUnsupervised, ...@@ -374,11 +448,7 @@ TEST_F(SupervisedUserServiceExtensionTestUnsupervised,
TEST_F(SupervisedUserServiceExtensionTest, ExtensionManagementPolicyProvider) { TEST_F(SupervisedUserServiceExtensionTest, ExtensionManagementPolicyProvider) {
SupervisedUserService* supervised_user_service = SupervisedUserService* supervised_user_service =
SupervisedUserServiceFactory::GetForProfile(profile_.get()); SupervisedUserServiceFactory::GetForProfile(profile_.get());
SupervisedUserURLFilterObserver observer(
supervised_user_service->GetURLFilterForUIThread());
ASSERT_TRUE(profile_->IsSupervised()); ASSERT_TRUE(profile_->IsSupervised());
// Wait for the initial update to finish (otherwise we'll get leaks).
observer.Wait();
// Check that a supervised user can install a theme. // Check that a supervised user can install a theme.
scoped_refptr<extensions::Extension> theme = MakeThemeExtension(); scoped_refptr<extensions::Extension> theme = MakeThemeExtension();
...@@ -424,10 +494,11 @@ TEST_F(SupervisedUserServiceExtensionTest, NoContentPacks) { ...@@ -424,10 +494,11 @@ TEST_F(SupervisedUserServiceExtensionTest, NoContentPacks) {
SupervisedUserURLFilter* url_filter = SupervisedUserURLFilter* url_filter =
supervised_user_service->GetURLFilterForUIThread(); supervised_user_service->GetURLFilterForUIThread();
GURL url("http://youtube.com"); // ASSERT_EQ instead of ASSERT_TRUE([...].empty()) so that the error
// ASSERT_EQ instead of ASSERT_TRUE(site_lists_.empty()) so that the error
// message contains the size in case of failure. // message contains the size in case of failure.
ASSERT_EQ(0u, site_lists_.size()); ASSERT_EQ(0u, site_list_observer_.site_lists().size());
GURL url("http://youtube.com");
EXPECT_EQ(SupervisedUserURLFilter::ALLOW, EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
url_filter->GetFilteringBehaviorForURL(url)); url_filter->GetFilteringBehaviorForURL(url));
} }
...@@ -437,8 +508,6 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) { ...@@ -437,8 +508,6 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) {
SupervisedUserServiceFactory::GetForProfile(profile_.get()); SupervisedUserServiceFactory::GetForProfile(profile_.get());
SupervisedUserURLFilter* url_filter = SupervisedUserURLFilter* url_filter =
supervised_user_service->GetURLFilterForUIThread(); supervised_user_service->GetURLFilterForUIThread();
SupervisedUserURLFilterObserver observer(url_filter);
observer.Wait();
GURL example_url("http://example.com"); GURL example_url("http://example.com");
GURL moose_url("http://moose.org"); GURL moose_url("http://moose.org");
...@@ -465,14 +534,16 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) { ...@@ -465,14 +534,16 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) {
base::FilePath whitelist_path = base::FilePath whitelist_path =
test_data_dir.AppendASCII("whitelists/content_pack/site_list.json"); test_data_dir.AppendASCII("whitelists/content_pack/site_list.json");
whitelist_service->LoadWhitelistForTesting("aaaa", whitelist_path); whitelist_service->LoadWhitelistForTesting("aaaa", whitelist_path);
observer.Wait(); site_list_observer_.Wait();
ASSERT_EQ(1u, site_lists_.size()); ASSERT_EQ(1u, site_list_observer_.site_lists().size());
ASSERT_EQ(3u, sites_.size()); ASSERT_EQ(3u, site_list_observer_.sites().size());
EXPECT_EQ(base::ASCIIToUTF16("YouTube"), sites_[0].name); EXPECT_EQ(base::ASCIIToUTF16("YouTube"), site_list_observer_.sites()[0].name);
EXPECT_EQ(base::ASCIIToUTF16("Homestar Runner"), sites_[1].name); EXPECT_EQ(base::ASCIIToUTF16("Homestar Runner"),
EXPECT_EQ(base::string16(), sites_[2].name); site_list_observer_.sites()[1].name);
EXPECT_EQ(base::string16(), site_list_observer_.sites()[2].name);
url_filter_observer_.Wait();
EXPECT_EQ(SupervisedUserURLFilter::ALLOW, EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
url_filter->GetFilteringBehaviorForURL(example_url)); url_filter->GetFilteringBehaviorForURL(example_url));
EXPECT_EQ(SupervisedUserURLFilter::WARN, EXPECT_EQ(SupervisedUserURLFilter::WARN,
...@@ -482,20 +553,21 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) { ...@@ -482,20 +553,21 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) {
whitelist_path = whitelist_path =
test_data_dir.AppendASCII("whitelists/content_pack_2/site_list.json"); test_data_dir.AppendASCII("whitelists/content_pack_2/site_list.json");
whitelist_service->LoadWhitelistForTesting("bbbb", whitelist_path); whitelist_service->LoadWhitelistForTesting("bbbb", whitelist_path);
observer.Wait(); site_list_observer_.Wait();
ASSERT_EQ(2u, site_lists_.size()); ASSERT_EQ(2u, site_list_observer_.site_lists().size());
ASSERT_EQ(4u, sites_.size()); ASSERT_EQ(4u, site_list_observer_.sites().size());
// The site lists might be returned in any order, so we put them into a set. // The site lists might be returned in any order, so we put them into a set.
std::set<std::string> site_names; std::set<std::string> site_names;
for (const SupervisedUserSiteList::Site& site : sites_) for (const SupervisedUserSiteList::Site& site : site_list_observer_.sites())
site_names.insert(base::UTF16ToUTF8(site.name)); site_names.insert(base::UTF16ToUTF8(site.name));
EXPECT_EQ(1u, site_names.count("YouTube")); EXPECT_EQ(1u, site_names.count("YouTube"));
EXPECT_EQ(1u, site_names.count("Homestar Runner")); EXPECT_EQ(1u, site_names.count("Homestar Runner"));
EXPECT_EQ(1u, site_names.count(std::string())); EXPECT_EQ(1u, site_names.count(std::string()));
EXPECT_EQ(1u, site_names.count("Moose")); EXPECT_EQ(1u, site_names.count("Moose"));
url_filter_observer_.Wait();
EXPECT_EQ(SupervisedUserURLFilter::ALLOW, EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
url_filter->GetFilteringBehaviorForURL(example_url)); url_filter->GetFilteringBehaviorForURL(example_url));
EXPECT_EQ(SupervisedUserURLFilter::ALLOW, EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
...@@ -503,12 +575,13 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) { ...@@ -503,12 +575,13 @@ TEST_F(SupervisedUserServiceExtensionTest, InstallContentPacks) {
// Unload the first whitelist. // Unload the first whitelist.
whitelist_service->UnloadWhitelist("aaaa"); whitelist_service->UnloadWhitelist("aaaa");
observer.Wait(); site_list_observer_.Wait();
ASSERT_EQ(1u, site_lists_.size()); ASSERT_EQ(1u, site_list_observer_.site_lists().size());
ASSERT_EQ(1u, sites_.size()); ASSERT_EQ(1u, site_list_observer_.sites().size());
EXPECT_EQ(base::ASCIIToUTF16("Moose"), sites_[0].name); EXPECT_EQ(base::ASCIIToUTF16("Moose"), site_list_observer_.sites()[0].name);
url_filter_observer_.Wait();
EXPECT_EQ(SupervisedUserURLFilter::WARN, EXPECT_EQ(SupervisedUserURLFilter::WARN,
url_filter->GetFilteringBehaviorForURL(example_url)); url_filter->GetFilteringBehaviorForURL(example_url));
EXPECT_EQ(SupervisedUserURLFilter::ALLOW, EXPECT_EQ(SupervisedUserURLFilter::ALLOW,
......
...@@ -54,7 +54,6 @@ const char* kFilteredSchemes[] = { ...@@ -54,7 +54,6 @@ const char* kFilteredSchemes[] = {
"wss" "wss"
}; };
// This class encapsulates all the state that is required during construction of // This class encapsulates all the state that is required during construction of
// a new SupervisedUserURLFilter::Contents. // a new SupervisedUserURLFilter::Contents.
class FilterBuilder { class FilterBuilder {
...@@ -90,7 +89,6 @@ FilterBuilder::~FilterBuilder() { ...@@ -90,7 +89,6 @@ FilterBuilder::~FilterBuilder() {
} }
bool FilterBuilder::AddPattern(const std::string& pattern, int site_id) { bool FilterBuilder::AddPattern(const std::string& pattern, int site_id) {
DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
std::string scheme; std::string scheme;
std::string host; std::string host;
uint16 port; uint16 port;
...@@ -137,15 +135,12 @@ void FilterBuilder::AddSiteList( ...@@ -137,15 +135,12 @@ void FilterBuilder::AddSiteList(
} }
scoped_ptr<SupervisedUserURLFilter::Contents> FilterBuilder::Build() { scoped_ptr<SupervisedUserURLFilter::Contents> FilterBuilder::Build() {
DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
contents_->url_matcher.AddConditionSets(all_conditions_); contents_->url_matcher.AddConditionSets(all_conditions_);
return contents_.Pass(); return contents_.Pass();
} }
scoped_ptr<SupervisedUserURLFilter::Contents> CreateWhitelistFromPatterns( scoped_ptr<SupervisedUserURLFilter::Contents> CreateWhitelistFromPatterns(
const std::vector<std::string>& patterns) { const std::vector<std::string>& patterns) {
DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
FilterBuilder builder; FilterBuilder builder;
for (const std::string& pattern : patterns) { for (const std::string& pattern : patterns) {
// TODO(bauerb): We should create a fake site for the whitelist. // TODO(bauerb): We should create a fake site for the whitelist.
...@@ -158,8 +153,6 @@ scoped_ptr<SupervisedUserURLFilter::Contents> CreateWhitelistFromPatterns( ...@@ -158,8 +153,6 @@ scoped_ptr<SupervisedUserURLFilter::Contents> CreateWhitelistFromPatterns(
scoped_ptr<SupervisedUserURLFilter::Contents> scoped_ptr<SupervisedUserURLFilter::Contents>
LoadWhitelistsOnBlockingPoolThread( LoadWhitelistsOnBlockingPoolThread(
const std::vector<scoped_refptr<SupervisedUserSiteList> >& site_lists) { const std::vector<scoped_refptr<SupervisedUserSiteList> >& site_lists) {
DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
FilterBuilder builder; FilterBuilder builder;
for (const scoped_refptr<SupervisedUserSiteList>& site_list : site_lists) for (const scoped_refptr<SupervisedUserSiteList>& site_list : site_lists)
builder.AddSiteList(site_list); builder.AddSiteList(site_list);
...@@ -172,7 +165,8 @@ LoadWhitelistsOnBlockingPoolThread( ...@@ -172,7 +165,8 @@ LoadWhitelistsOnBlockingPoolThread(
SupervisedUserURLFilter::SupervisedUserURLFilter() SupervisedUserURLFilter::SupervisedUserURLFilter()
: default_behavior_(ALLOW), : default_behavior_(ALLOW),
contents_(new Contents()), contents_(new Contents()),
blacklist_(NULL) { blacklist_(nullptr),
blocking_task_runner_(BrowserThread::GetBlockingPool()) {
// Detach from the current thread so we can be constructed on a different // Detach from the current thread so we can be constructed on a different
// thread than the one where we're used. // thread than the one where we're used.
DetachFromThread(); DetachFromThread();
...@@ -396,7 +390,7 @@ void SupervisedUserURLFilter::LoadWhitelists( ...@@ -396,7 +390,7 @@ void SupervisedUserURLFilter::LoadWhitelists(
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
base::PostTaskAndReplyWithResult( base::PostTaskAndReplyWithResult(
BrowserThread::GetBlockingPool(), blocking_task_runner_.get(),
FROM_HERE, FROM_HERE,
base::Bind(&LoadWhitelistsOnBlockingPoolThread, site_lists), base::Bind(&LoadWhitelistsOnBlockingPoolThread, site_lists),
base::Bind(&SupervisedUserURLFilter::SetContents, this)); base::Bind(&SupervisedUserURLFilter::SetContents, this));
...@@ -415,7 +409,7 @@ void SupervisedUserURLFilter::SetFromPatterns( ...@@ -415,7 +409,7 @@ void SupervisedUserURLFilter::SetFromPatterns(
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
base::PostTaskAndReplyWithResult( base::PostTaskAndReplyWithResult(
BrowserThread::GetBlockingPool(), blocking_task_runner_.get(),
FROM_HERE, FROM_HERE,
base::Bind(&CreateWhitelistFromPatterns, patterns), base::Bind(&CreateWhitelistFromPatterns, patterns),
base::Bind(&SupervisedUserURLFilter::SetContents, this)); base::Bind(&SupervisedUserURLFilter::SetContents, this));
...@@ -464,6 +458,11 @@ void SupervisedUserURLFilter::RemoveObserver(Observer* observer) { ...@@ -464,6 +458,11 @@ void SupervisedUserURLFilter::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer); observers_.RemoveObserver(observer);
} }
void SupervisedUserURLFilter::SetBlockingTaskRunnerForTesting(
const scoped_refptr<base::TaskRunner>& task_runner) {
blocking_task_runner_ = task_runner;
}
void SupervisedUserURLFilter::SetContents(scoped_ptr<Contents> contents) { void SupervisedUserURLFilter::SetContents(scoped_ptr<Contents> contents) {
DCHECK(CalledOnValidThread()); DCHECK(CalledOnValidThread());
contents_ = contents.Pass(); contents_ = contents.Pass();
......
...@@ -159,6 +159,10 @@ class SupervisedUserURLFilter ...@@ -159,6 +159,10 @@ class SupervisedUserURLFilter
void AddObserver(Observer* observer); void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer); void RemoveObserver(Observer* observer);
// Sets a different task runner for testing.
void SetBlockingTaskRunnerForTesting(
const scoped_refptr<base::TaskRunner>& task_runner);
private: private:
friend class base::RefCountedThreadSafe<SupervisedUserURLFilter>; friend class base::RefCountedThreadSafe<SupervisedUserURLFilter>;
~SupervisedUserURLFilter(); ~SupervisedUserURLFilter();
...@@ -191,6 +195,8 @@ class SupervisedUserURLFilter ...@@ -191,6 +195,8 @@ class SupervisedUserURLFilter
scoped_ptr<SupervisedUserAsyncURLChecker> async_url_checker_; scoped_ptr<SupervisedUserAsyncURLChecker> async_url_checker_;
scoped_refptr<base::TaskRunner> blocking_task_runner_;
DISALLOW_COPY_AND_ASSIGN(SupervisedUserURLFilter); DISALLOW_COPY_AND_ASSIGN(SupervisedUserURLFilter);
}; };
......
...@@ -86,7 +86,10 @@ void SupervisedUserWhitelistService::Init() { ...@@ -86,7 +86,10 @@ void SupervisedUserWhitelistService::Init() {
void SupervisedUserWhitelistService::AddSiteListsChangedCallback( void SupervisedUserWhitelistService::AddSiteListsChangedCallback(
const SiteListsChangedCallback& callback) { const SiteListsChangedCallback& callback) {
site_lists_changed_callbacks_.push_back(callback); site_lists_changed_callbacks_.push_back(callback);
NotifyWhitelistsChanged();
std::vector<scoped_refptr<SupervisedUserSiteList>> whitelists;
GetLoadedWhitelists(&whitelists);
callback.Run(whitelists);
} }
void SupervisedUserWhitelistService::LoadWhitelistForTesting( void SupervisedUserWhitelistService::LoadWhitelistForTesting(
...@@ -287,10 +290,15 @@ void SupervisedUserWhitelistService::RegisterWhitelist(const std::string& id, ...@@ -287,10 +290,15 @@ void SupervisedUserWhitelistService::RegisterWhitelist(const std::string& id,
weak_ptr_factory_.GetWeakPtr(), id)); weak_ptr_factory_.GetWeakPtr(), id));
} }
void SupervisedUserWhitelistService::NotifyWhitelistsChanged() { void SupervisedUserWhitelistService::GetLoadedWhitelists(
std::vector<scoped_refptr<SupervisedUserSiteList> > whitelists; std::vector<scoped_refptr<SupervisedUserSiteList>>* whitelists) {
for (const auto& whitelist : loaded_whitelists_) for (const auto& whitelist : loaded_whitelists_)
whitelists.push_back(whitelist.second); whitelists->push_back(whitelist.second);
}
void SupervisedUserWhitelistService::NotifyWhitelistsChanged() {
std::vector<scoped_refptr<SupervisedUserSiteList>> whitelists;
GetLoadedWhitelists(&whitelists);
for (const auto& callback : site_lists_changed_callbacks_) for (const auto& callback : site_lists_changed_callbacks_)
callback.Run(whitelists); callback.Run(whitelists);
......
...@@ -98,6 +98,9 @@ class SupervisedUserWhitelistService : public syncer::SyncableService { ...@@ -98,6 +98,9 @@ class SupervisedUserWhitelistService : public syncer::SyncableService {
const std::string& name, const std::string& name,
bool new_installation); bool new_installation);
void GetLoadedWhitelists(
std::vector<scoped_refptr<SupervisedUserSiteList>>* whitelists);
void NotifyWhitelistsChanged(); void NotifyWhitelistsChanged();
void OnWhitelistReady(const std::string& id, void OnWhitelistReady(const std::string& id,
......
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