Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple adblock engines from component loading methods #10994

Merged
merged 18 commits into from
Feb 18, 2022

Conversation

antonok-edm
Copy link
Collaborator

Resolves brave/brave-browser#19366

Submitter Checklist:

  • I confirm that no security/privacy review is needed, or that I have requested one
  • There is a ticket for my issue
  • Used Github auto-closing keywords in the PR description above
  • Wrote a good PR/commit description
  • Added appropriate labels (QA/Yes or QA/No; release-notes/include or release-notes/exclude; OS/...) to the associated issue
  • Checked the PR locally: npm run test -- brave_browser_tests, npm run test -- brave_unit_tests, npm run lint, npm run gn_check, npm run tslint
  • Ran git rebase master (if needed)

Reviewer Checklist:

  • A security review is not needed, or a link to one is included in the PR description
  • New files have MPL-2.0 license header
  • Adequate test coverage exists to prevent regressions
  • Major classes, functions and non-trivial code blocks are well-commented
  • Changes in component dependencies are properly reflected in gn
  • Code follows the style guide
  • Test plan is specified in PR before merging

After-merge Checklist:

Test Plan:

This is a fairly large refactoring that shouldn't change any existing behavior. The automated unit and browser tests cover most core functionality, but it would be great to go over test plans related to previously-merged adblock-related PRs and ensure that things still work the same way. In particular, that updated adblock rules are applied, whether that be the default set of rules, regional lists, custom filters, or custom list subscriptions.

@antonok-edm antonok-edm self-assigned this Nov 11, 2021
@antonok-edm antonok-edm requested a review from a team as a code owner November 11, 2021 00:55
@antonok-edm antonok-edm removed the request for review from ShivanKaul November 11, 2021 05:33
@antonok-edm antonok-edm force-pushed the sourceprovider-adblock branch 2 times, most recently from aa460f7 to 89125c8 Compare November 11, 2021 18:25
void AdBlockCustomFiltersSourceProvider::Load(
base::OnceCallback<void(bool deserialize, const DATFileDataBuffer& dat_buf)>
cb) {
content::GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is still called on more than one thread which is not safe. Also usage of base::Unretained is not safe here. Each class should be called on one and only one thread. The only exception is that a class can be created on one thread and then used on a different thread after that. I think it's best to add thread checks to enforce that

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also avoid the use of named task runners like UIThread

const base::FilePath& path) {
component_path_ = path;

base::ThreadPool::PostTaskAndReplyWithResult(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would break these out into separate method calls and add comments to clarify what is happening

@@ -0,0 +1,45 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file name should match the class name

void AdBlockRegionalSourceProvider::Load(
base::OnceCallback<void(bool deserialize, const DATFileDataBuffer& dat_buf)>
cb) {
if (component_path_.empty()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not ok to just ignore the callback

public:
class Observer : public base::CheckedObserver {
public:
virtual void OnNewResourcesAvailable(const std::string& resources_json) = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these aren't "new" resources, I would call this OnLoaded or something

@@ -253,49 +266,85 @@ void AdBlockBaseService::OnGetDATFileData(base::OnceClosure callback,
return;
}
GetTaskRunner()->PostTask(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this shouldn't be using a task runner for anything anymore. AdBlockEngineService should only run on one thread

AdBlockDefaultSourceProvider::AdBlockDefaultSourceProvider(
component_updater::ComponentUpdateService* cus,
base::RepeatingCallback<void(const std::string& regional_catalog)>
regional_catalog_available_cb)
Copy link
Collaborator

@bridiver bridiver Dec 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is there a separate callback for this?

const DATFileDataBuffer& dat_buf)>) = 0;

protected:
void ProvideNewDAT(const DATFileDataBuffer& dat_buf);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The convention for this would be OnDATLoaded, but I'm confused about why ListSource is also a DATFileDataBuffer

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have to pass the list source across the Rust FFI as a buffer as well because it's a potentially untrusted/invalid input. The FFI will attempt to parse it to a string, and handle it correspondingly if it doesn't parse correctly.

public:
class Observer : public base::CheckedObserver {
public:
virtual void OnNewDATAvailable(const DATFileDataBuffer& dat_buf) = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as below, this should be OnDATLoaded or OnDATReady

component_update_service_(cus),
task_runner_(task_runner),
subscription_service_manager_(std::move(subscription_service_manager)) {
default_source_provider_ =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the source provider should be owned by browser_process_impl and passed in to the AdBlockService

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, it doesn't necessarily need to be owned by browser_process_impl right now, but it should be created in browser_process_impl and passed in

default_source_provider_ =
std::make_unique<brave_shields::AdBlockDefaultSourceProvider>(
component_update_service_,
base::BindRepeating(&AdBlockService::OnRegionalCatalogFileDataReady,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AdBlockService should register as an observer for the SourceProvider

return custom_filters_service_.get();
}

brave_shields::AdBlockCustomFiltersSourceProvider*
AdBlockService::custom_filters_source_provider() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nothing outside of this class should ever need access to the custom_filters_source_provider

@@ -61,27 +60,28 @@ std::unique_ptr<DomainBlockNavigationThrottle>
DomainBlockNavigationThrottle::MaybeCreateThrottleFor(
content::NavigationHandle* navigation_handle,
AdBlockService* ad_block_service,
AdBlockCustomFiltersService* ad_block_custom_filters_service,
AdBlockCustomFiltersSourceProvider* ad_block_custom_filters_source_provider,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is definitely not right, why are we passing in the source provider?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the same reason as #10994 (comment).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're missing what I'm saying, the SourceProvider shouldn't be passed to other classes, they should only deal with the service.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess my reasoning is that there isn't a CustomFiltersService in the current implementation; just the engine and SourceProvider. The method to add the new custom filter could be added to AdBlockService in the current implementation, or adding CustomFiltersService to handle it could work too.

ad_block_custom_filters_service_->GetCustomFilters();
ad_block_custom_filters_service_->UpdateCustomFilters(
ad_block_custom_filters_source_provider_->GetCustomFilters();
ad_block_custom_filters_source_provider_->UpdateCustomFilters(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're actually modifying the filter to disable the rule? That's definitely not great, but a separate issue from this PR. Can you open a ticket for that? In any case these methods should be called on the service, not the source provider and DomainBlockControllerClient shouldn't have to know anything about the rules format. We should just be calling ad_block_custom_filters_service_->DisableRule(request_url_.host()) or something along those lines

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed a followup at brave/brave-browser#19863

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but both this class and DomainBlockNavigationThrottle should only ever talk to the service and shouldn't know anything about the underlying source provider so you should pass the service here and add methods to pass through to the source provider

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 !

@@ -104,16 +104,17 @@ std::string ResourceTypeToString(blink::mojom::ResourceType resource_type) {

namespace brave_shields {

AdBlockBaseService::AdBlockBaseService(BraveComponent::Delegate* delegate)
: BaseBraveShieldsService(delegate),
AdBlockEngineService::AdBlockEngineService(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should drop Service here, it's very confusing

if (cus) {
RegisterAdBlockDefaultComponent(
cus,
base::BindRepeating(&AdBlockDefaultSourceProvider::OnComponentReady,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this causes the SourceProvider to be called on more than one thread because OnComponentReady is called on the UI thread and Load is called from the adblock thread

}

///////////////////////////////////////////////////////////////////////////////

std::unique_ptr<AdBlockEngineService> AdBlockEngineServiceFactory(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method serves no useful purpose

regional_catalog_available_cb);
~AdBlockDefaultSourceProvider() override;

void Load(base::OnceCallback<
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't overload method names that do different things. One of these loads a dat file and the other loads resources

Copy link
Collaborator Author

@antonok-edm antonok-edm Dec 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they are technically overridden from AdBlockSourceProvider and AdBlockResourceProvider respectively; they make sense in context there but maybe not when overloaded side-by-side. I can rename to LoadDATBuffer and LoadResources respectively

@antonok-edm antonok-edm force-pushed the sourceprovider-adblock branch 2 times, most recently from f3015a9 to 761c204 Compare February 16, 2022 01:07
initialized_ = true;
}

AdBlockRegionalServiceManager::~AdBlockRegionalServiceManager() {}
AdBlockRegionalServiceManager::~AdBlockRegionalServiceManager() {
catalog_provider_->RemoveObserver(this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pls prefer ScopedObservation, for future reference

@antonok-edm antonok-edm force-pushed the sourceprovider-adblock branch 2 times, most recently from 790525e to e2b5b59 Compare February 17, 2022 19:43
This method was only useful for the old extension-based cosmetic
filtering implementation, and has started causing some tests to fail on
the newer C++ implementation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants