Skip to content

Commit

Permalink
Merge pull request #16716 from brave/adblock-has-selector
Browse files Browse the repository at this point in the history
Support cosmetic filters with the `:has` pseudoclass
  • Loading branch information
antonok-edm authored Jan 18, 2023
2 parents 04c775a + e196276 commit 581475f
Show file tree
Hide file tree
Showing 16 changed files with 141 additions and 50 deletions.
51 changes: 51 additions & 0 deletions browser/brave_shields/ad_block_service_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1998,6 +1998,57 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringFrames) {
EXPECT_EQ(base::Value(true), main_result.value);
}

// Test cosmetic filtering ignores rules with the `:has` pseudoclass in standard
// blocking mode
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest,
CosmeticFilteringHasPseudoclassStandard) {
ASSERT_TRUE(InstallDefaultAdBlockExtension());
DisableAggressiveMode();
UpdateAdBlockInstanceWithRules("b.com##.container:has(#promotion)\n");

GURL tab_url =
embedded_test_server()->GetURL("b.com", "/cosmetic_filtering.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));

content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();

EXPECT_EQ(true, EvalJs(contents,
"checkSelector('.container', 'display', 'block')"));
}

// Test cosmetic filtering applies rules with the `:has` pseudoclass in
// aggressive blocking mode
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest,
CosmeticFilteringHasPseudoclassAggressive) {
ASSERT_TRUE(InstallDefaultAdBlockExtension());
UpdateAdBlockInstanceWithRules("b.com##.container:has(#promotion)\n");

GURL tab_url =
embedded_test_server()->GetURL("b.com", "/cosmetic_filtering.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), tab_url));

content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();

// the `#promotion` element's container is hidden
EXPECT_EQ("none", EvalJs(contents,
"window.getComputedStyle(document.getElementById('"
"promotion').parentElement).display"));

// the `#real-user-content` element's container is not hidden
EXPECT_EQ("block", EvalJs(contents,
"window.getComputedStyle(document.getElementById('"
"real-user-content').parentElement).display"));

// both inner elements have no new styles applied
EXPECT_EQ(true, EvalJs(contents,
"checkSelector('#promotion', 'display', 'block')"));
EXPECT_EQ(true,
EvalJs(contents,
"checkSelector('#real-user-content', 'display', 'block')"));
}

// Test cosmetic filtering ignores content determined to be 1st party
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringProtect1p) {
ASSERT_TRUE(InstallDefaultAdBlockExtension());
Expand Down
4 changes: 2 additions & 2 deletions build/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion components/adblock_rust_ffi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Brian R. Bondy <[email protected]>"]
edition = "2018"

[dependencies]
adblock = { version = "0.6.2", default-features = false, features = ["full-regex-handling", "object-pooling", "unsync-regex-caching"] }
adblock = { version = "0.6.3", default-features = false, features = ["full-regex-handling", "object-pooling", "unsync-regex-caching"] }
serde_json = "1.0"
libc = "0.2"

Expand Down
19 changes: 12 additions & 7 deletions components/adblock_rust_ffi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/* Copyright (c) 2019 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

use adblock::engine::Engine;
use adblock::lists::FilterListMetadata;
use adblock::resources::{MimeType, Resource, ResourceType};
Expand Down Expand Up @@ -92,8 +97,11 @@ pub unsafe extern "C" fn engine_create(rules: *const c_char) -> *mut Engine {
/// Create a new `Engine`, interpreting `rules` as a null-terminated C string and then parsing as a
/// filter list in ABP syntax. Also populates metadata from the filter list into `metadata`.
#[no_mangle]
pub unsafe extern "C" fn engine_create_with_metadata(rules: *const c_char, metadata: *mut *mut FilterListMetadata) -> *mut Engine {
let rules = CStr::from_ptr(rules).to_str().unwrap_or_else(|_|{
pub unsafe extern "C" fn engine_create_with_metadata(
rules: *const c_char,
metadata: *mut *mut FilterListMetadata,
) -> *mut Engine {
let rules = CStr::from_ptr(rules).to_str().unwrap_or_else(|_| {
eprintln!("Failed to parse filter list with invalid UTF-8 content");
""
});
Expand Down Expand Up @@ -122,10 +130,7 @@ fn engine_create_from_str(rules: &str) -> (*mut FilterListMetadata, *mut Engine)
let mut filter_set = adblock::lists::FilterSet::new(false);
let metadata = filter_set.add_filter_list(&rules, Default::default());
let engine = Engine::from_filter_set(filter_set, true);
(
Box::into_raw(Box::new(metadata)),
Box::into_raw(Box::new(engine)),
)
(Box::into_raw(Box::new(metadata)), Box::into_raw(Box::new(engine)))
}

/// Checks if a `url` matches for the specified `Engine` within the context.
Expand Down Expand Up @@ -356,7 +361,7 @@ pub unsafe extern "C" fn engine_url_cosmetic_resources(
assert!(!engine.is_null());
let engine = Box::leak(Box::from_raw(engine));
CString::new(
serde_json::to_string(&engine.url_cosmetic_resources(url)).unwrap_or_else(|_| "".into()),
serde_json::to_string(&engine.url_cosmetic_resources(url)).unwrap_or_else(|_| "{}".into()),
)
.expect("Error: CString::new()")
.into_raw()
Expand Down
13 changes: 10 additions & 3 deletions components/brave_shields/browser/ad_block_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,17 @@ bool AdBlockEngine::TagExists(const std::string& tag) {
return base::Contains(tags_, tag);
}

absl::optional<base::Value> AdBlockEngine::UrlCosmeticResources(
const std::string& url) {
base::Value::Dict AdBlockEngine::UrlCosmeticResources(const std::string& url) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return base::JSONReader::Read(ad_block_client_->urlCosmeticResources(url));
absl::optional<base::Value> result =
base::JSONReader::Read(ad_block_client_->urlCosmeticResources(url));

if (!result) {
return base::Value::Dict();
} else {
DCHECK(result->is_dict());
return std::move(result->GetDict());
}
}

base::Value::List AdBlockEngine::HiddenClassIdSelectors(
Expand Down
2 changes: 1 addition & 1 deletion components/brave_shields/browser/ad_block_engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class AdBlockEngine : public base::SupportsWeakPtr<AdBlockEngine> {
void EnableTag(const std::string& tag, bool enabled);
bool TagExists(const std::string& tag);

absl::optional<base::Value> UrlCosmeticResources(const std::string& url);
base::Value::Dict UrlCosmeticResources(const std::string& url);
base::Value::List HiddenClassIdSelectors(
const std::vector<std::string>& classes,
const std::vector<std::string>& ids,
Expand Down
37 changes: 24 additions & 13 deletions components/brave_shields/browser/ad_block_service.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,24 +170,35 @@ absl::optional<std::string> AdBlockService::GetCspDirectives(
return csp_directives;
}

absl::optional<base::Value> AdBlockService::UrlCosmeticResources(
const std::string& url) {
base::Value::Dict AdBlockService::UrlCosmeticResources(
const std::string& url,
bool aggressive_blocking) {
DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
absl::optional<base::Value> resources =
default_engine_->UrlCosmeticResources(url);

if (!resources || !resources->is_dict()) {
return resources;
base::Value::Dict resources = default_engine_->UrlCosmeticResources(url);

if (!aggressive_blocking) {
// `:has` procedural selectors from the default engine should not be hidden
// in standard blocking mode.
base::Value::List* default_hide_selectors =
resources.FindList("hide_selectors");
if (default_hide_selectors) {
base::Value::List::iterator it = default_hide_selectors->begin();
while (it < default_hide_selectors->end()) {
DCHECK(it->is_string());
if (it->GetString().find(":has(") != std::string::npos) {
it = default_hide_selectors->erase(it);
} else {
it++;
}
}
}
}

absl::optional<base::Value> additional_resources =
base::Value::Dict additional_resources =
additional_filters_engine_->UrlCosmeticResources(url);

if (additional_resources && additional_resources->is_dict()) {
MergeResourcesInto(std::move(additional_resources->GetDict()),
resources->GetIfDict(),
/*force_hide=*/true);
}
MergeResourcesInto(std::move(additional_resources), resources,
/*force_hide=*/true);

return resources;
}
Expand Down
3 changes: 2 additions & 1 deletion components/brave_shields/browser/ad_block_service.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ class AdBlockService {
const GURL& url,
blink::mojom::ResourceType resource_type,
const std::string& tab_host);
absl::optional<base::Value> UrlCosmeticResources(const std::string& url);
base::Value::Dict UrlCosmeticResources(const std::string& url,
bool aggressive_blocking);
base::Value::Dict HiddenClassIdSelectors(
const std::vector<std::string>& classes,
const std::vector<std::string>& ids,
Expand Down
17 changes: 8 additions & 9 deletions components/brave_shields/browser/ad_block_service_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,17 @@ void MergeCspDirectiveInto(absl::optional<std::string> from,
// will be moved into a possibly new field of `into` called
// `force_hide_selectors`.
void MergeResourcesInto(base::Value::Dict from,
base::Value::Dict* into,
base::Value::Dict& into,
bool force_hide) {
DCHECK(into);
base::Value::List* resources_hide_selectors = nullptr;
if (force_hide) {
resources_hide_selectors = into->FindList("force_hide_selectors");
resources_hide_selectors = into.FindList("force_hide_selectors");
if (!resources_hide_selectors) {
resources_hide_selectors =
into->Set("force_hide_selectors", base::Value::List())->GetIfList();
into.Set("force_hide_selectors", base::Value::List())->GetIfList();
}
} else {
resources_hide_selectors = into->FindList("hide_selectors");
resources_hide_selectors = into.FindList("hide_selectors");
}
base::Value::List* from_resources_hide_selectors =
from.FindList("hide_selectors");
Expand All @@ -64,7 +63,7 @@ void MergeResourcesInto(base::Value::Dict from,
}

base::Value::Dict* resources_style_selectors =
into->FindDict("style_selectors");
into.FindDict("style_selectors");
base::Value::Dict* from_resources_style_selectors =
from.FindDict("style_selectors");
if (resources_style_selectors && from_resources_style_selectors) {
Expand All @@ -82,15 +81,15 @@ void MergeResourcesInto(base::Value::Dict from,
}
}

base::Value::List* resources_exceptions = into->FindList("exceptions");
base::Value::List* resources_exceptions = into.FindList("exceptions");
base::Value::List* from_resources_exceptions = from.FindList("exceptions");
if (resources_exceptions && from_resources_exceptions) {
for (auto& exception : *from_resources_exceptions) {
resources_exceptions->Append(std::move(exception));
}
}

auto* resources_injected_script = into->FindString("injected_script");
auto* resources_injected_script = into.FindString("injected_script");
auto* from_resources_injected_script = from.FindString("injected_script");
if (resources_injected_script && from_resources_injected_script) {
*resources_injected_script = base::StrCat(
Expand All @@ -99,7 +98,7 @@ void MergeResourcesInto(base::Value::Dict from,

auto from_resources_generichide = from.FindBool("generichide");
if (from_resources_generichide && *from_resources_generichide) {
into->Set("generichide", true);
into.Set("generichide", true);
}
}

Expand Down
2 changes: 1 addition & 1 deletion components/brave_shields/browser/ad_block_service_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ void MergeCspDirectiveInto(absl::optional<std::string> from,
absl::optional<std::string>* into);

void MergeResourcesInto(base::Value::Dict from,
base::Value::Dict* into,
base::Value::Dict& into,
bool force_hide);

} // namespace brave_shields
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class CosmeticResourceMergeTest : public testing::Test {
base::JSONReader::Read(expected);
ASSERT_TRUE(expected_val);

MergeResourcesInto(std::move(b_val->GetDict()), a_val->GetIfDict(),
MergeResourcesInto(std::move(b_val->GetDict()), *a_val->GetIfDict(),
force_hide);

ASSERT_EQ(*a_val, *expected_val);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 3.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "brave/components/cosmetic_filters/browser/cosmetic_filters_resources.h"

Expand Down Expand Up @@ -68,11 +68,12 @@ void CosmeticFiltersResources::HiddenClassIdSelectors(

void CosmeticFiltersResources::UrlCosmeticResources(
const std::string& url,
bool aggressive_blocking,
UrlCosmeticResourcesCallback callback) {
DCHECK(ad_block_service_->GetTaskRunner()->RunsTasksInCurrentSequence());
auto resources = ad_block_service_->UrlCosmeticResources(url);
std::move(callback).Run(resources ? std::move(resources.value())
: base::Value());
auto resources =
ad_block_service_->UrlCosmeticResources(url, aggressive_blocking);
std::move(callback).Run(base::Value(std::move(resources)));
}

} // namespace cosmetic_filters
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 3.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_COMPONENTS_COSMETIC_FILTERS_BROWSER_COSMETIC_FILTERS_RESOURCES_H_
#define BRAVE_COMPONENTS_COSMETIC_FILTERS_BROWSER_COSMETIC_FILTERS_RESOURCES_H_
Expand Down Expand Up @@ -46,6 +46,7 @@ class CosmeticFiltersResources final
// filtering to first party elements along with an initial set of rules and
// scripts to apply for the given URL.
void UrlCosmeticResources(const std::string& url,
bool aggressive_blocking,
UrlCosmeticResourcesCallback callback) override;

private:
Expand Down
8 changes: 7 additions & 1 deletion components/cosmetic_filters/common/cosmetic_filters.mojom
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

module cosmetic_filters.mojom;

import "mojo/public/mojom/base/values.mojom";
Expand All @@ -8,5 +13,6 @@ interface CosmeticFiltersResources {
mojo_base.mojom.DictionaryValue result);

[Sync]
UrlCosmeticResources(string url) => (mojo_base.mojom.Value result);
UrlCosmeticResources(string url, bool aggressive_blocking) => (
mojo_base.mojom.Value result);
};
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ bool CosmeticFiltersJSHandler::ProcessURL(
"Brave.CosmeticFilters.UrlCosmeticResources");
TRACE_EVENT1("brave.adblock", "UrlCosmeticResources", "url", url_.spec());
cosmetic_filters_resources_->UrlCosmeticResources(
url_.spec(),
url_.spec(), enabled_1st_party_cf_,
base::BindOnce(&CosmeticFiltersJSHandler::OnUrlCosmeticResources,
base::Unretained(this), std::move(callback.value())));
} else {
Expand All @@ -376,7 +376,8 @@ bool CosmeticFiltersJSHandler::ProcessURL(
SCOPED_UMA_HISTOGRAM_TIMER_MICROS(
"Brave.CosmeticFilters.UrlCosmeticResourcesSync");
base::Value result;
cosmetic_filters_resources_->UrlCosmeticResources(url_.spec(), &result);
cosmetic_filters_resources_->UrlCosmeticResources(
url_.spec(), enabled_1st_party_cf_, &result);

auto* dict = result.GetIfDict();
if (dict)
Expand Down
8 changes: 8 additions & 0 deletions test/data/cosmetic_filtering.html
Original file line number Diff line number Diff line change
Expand Up @@ -94,5 +94,13 @@
<img src="http://chrome.appspot.com/sponsored/640x820.png">
</div>
<div id="inline-block-important" style="display: block !important"></div>

<!-- `:has` pseudoclass -->
<div class="container">
<div id="promotion"></div>
</div>
<div class="container">
<div id="real-user-content"></div>
</div>
</body>
</html>

0 comments on commit 581475f

Please sign in to comment.