Skip to content

Commit

Permalink
Uplift of #12950 (squashed) to beta
Browse files Browse the repository at this point in the history
  • Loading branch information
brave-browser-releases committed Apr 12, 2022
1 parent 62d2af2 commit b103f21
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 93 deletions.
5 changes: 3 additions & 2 deletions browser/brave_shields/ad_block_service_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1905,7 +1905,6 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringHide1pContent) {

// Test cosmetic filtering on elements added dynamically
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringDynamic) {
ASSERT_TRUE(InstallDefaultAdBlockExtension());
UpdateAdBlockInstanceWithRules("##.blockme");

GURL tab_url =
Expand Down Expand Up @@ -2157,8 +2156,10 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, CosmeticFilteringIframeScriptlet) {

// Test cosmetic filtering on an element that already has an `!important`
// marker on its `display` style.
// Temporarily disabled by https://github.com/brave/brave-core/pull/12950 due
// to performance impact of newer injection method.
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest,
CosmeticFilteringOverridesImportant) {
DISABLED_CosmeticFilteringOverridesImportant) {
ASSERT_TRUE(InstallDefaultAdBlockExtension());
UpdateAdBlockInstanceWithRules("###inline-block-important");

Expand Down
196 changes: 119 additions & 77 deletions components/cosmetic_filters/renderer/cosmetic_filters_js_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,88 @@ const char kCosmeticFilteringInitScript[] =

const char kHideSelectorsInjectScript[] =
R"((function() {
let nextIndex =
window.content_cosmetic.cosmeticStyleSheet.rules.length;
const selectors = %s;
selectors.forEach(selector => {
if ((typeof selector === 'string') &&
(window.content_cosmetic.hide1pContent ||
!window.content_cosmetic.allSelectorsToRules.has(selector))) {
let rule = selector + '{display:none !important;}';
let ruleIndex = 0;
window.content_cosmetic.cosmeticStyleSheet.insertRule(
`${rule}`, nextIndex);
if (!window.content_cosmetic.hide1pContent) {
ruleIndex = window.content_cosmetic.nextRuleIndex;
window.content_cosmetic.nextRuleIndex++;
window.content_cosmetic.allSelectorsToRules.set(
selector, ruleIndex);
selector, nextIndex);
window.content_cosmetic.firstRunQueue.add(selector);
}
window.cf_worker.injectStylesheet(rule, ruleIndex);
nextIndex++;
}
});
window.content_cosmetic.scheduleQueuePump(false, false);
if (!document.adoptedStyleSheets.includes(
window.content_cosmetic.cosmeticStyleSheet)) {
document.adoptedStyleSheets =
[window.content_cosmetic.cosmeticStyleSheet,
...document.adoptedStyleSheets];
};
})();)";

const char kForceHideSelectorsInjectScript[] =
R"((function() {
let nextIndex =
window.content_cosmetic.cosmeticStyleSheet.rules.length;
const selectors = %s;
selectors.forEach(selector => {
if (typeof selector === 'string') {
let rule = selector + '{display:none !important;}';
window.content_cosmetic.cosmeticStyleSheet.insertRule(
`${rule}`, nextIndex);
if (!window.content_cosmetic.hide1pContent) {
window.content_cosmetic.allSelectorsToRules.set(
selector, nextIndex);
}
nextIndex++;
}
});
if (!document.adoptedStyleSheets.includes(
window.content_cosmetic.cosmeticStyleSheet)) {
document.adoptedStyleSheets =
[window.content_cosmetic.cosmeticStyleSheet,
...document.adoptedStyleSheets];
};
})();)";

const char kStyleSelectorsInjectScript[] =
R"((function() {
let nextIndex =
window.content_cosmetic.cosmeticStyleSheet.rules.length;
const selectors = %s;
for (let selector in selectors) {
if (window.content_cosmetic.hide1pContent ||
!window.content_cosmetic.allSelectorsToRules.has(selector)) {
let rule = selector + '{';
selectors[selector].forEach(prop => {
if (!rule.endsWith('{')) {
rule += ';';
}
rule += prop;
});
rule += '}';
window.content_cosmetic.cosmeticStyleSheet.insertRule(
`${rule}`, nextIndex);
if (!window.content_cosmetic.hide1pContent) {
window.content_cosmetic.allSelectorsToRules.set(
selector, nextIndex);
}
nextIndex++;
};
};
if (!document.adoptedStyleSheets.includes(
window.content_cosmetic.cosmeticStyleSheet)){
document.adoptedStyleSheets =
[window.content_cosmetic.cosmeticStyleSheet,
...document.adoptedStyleSheets];
};
})();)";

std::string LoadDataResource(const int id) {
Expand Down Expand Up @@ -173,41 +237,6 @@ void CosmeticFiltersJSHandler::AddJavaScriptObjectToFrame(
bundle_injected_ = false;
}

// If `id` is nonzero, then the same number can be later passed to
// `UninjectStylesheet` to remove the stylesheet from the page.
void CosmeticFiltersJSHandler::InjectStylesheet(const std::string& stylesheet,
int id) {
blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();

blink::WebStyleSheetKey* style_sheet_key = nullptr;
if (id != 0) {
// Prepend a Brave-specific string to avoid collisions with stylesheets
// injected from other sources
std::string key = "BraveCFRule" + std::to_string(id);
inserted_stylesheet_ids.insert({id, std::make_unique<blink::WebString>(
blink::WebString::FromASCII(key))});
style_sheet_key = inserted_stylesheet_ids.at(id).get();
}
web_frame->GetDocument().InsertStyleSheet(
blink::WebString::FromUTF8(stylesheet), style_sheet_key,
blink::WebDocument::kUserOrigin);
}

void CosmeticFiltersJSHandler::UninjectStylesheet(int id) {
blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();

DCHECK_NE(id, 0);

auto i = inserted_stylesheet_ids.find(id);
if (i != inserted_stylesheet_ids.end()) {
std::unique_ptr<blink::WebStyleSheetKey> key = std::move(i->second);
inserted_stylesheet_ids.erase(i);

web_frame->GetDocument().RemoveInsertedStyleSheet(
*key, blink::WebDocument::kUserOrigin);
}
}

void CosmeticFiltersJSHandler::CreateWorkerObject(
v8::Isolate* isolate,
v8::Local<v8::Context> context) {
Expand Down Expand Up @@ -237,14 +266,6 @@ void CosmeticFiltersJSHandler::BindFunctionsToObject(
isolate, javascript_object, "isFirstPartyUrl",
base::BindRepeating(&CosmeticFiltersJSHandler::OnIsFirstParty,
base::Unretained(this)));
BindFunctionToObject(
isolate, javascript_object, "injectStylesheet",
base::BindRepeating(&CosmeticFiltersJSHandler::InjectStylesheet,
base::Unretained(this)));
BindFunctionToObject(
isolate, javascript_object, "uninjectStylesheet",
base::BindRepeating(&CosmeticFiltersJSHandler::UninjectStylesheet,
base::Unretained(this)));
}

template <typename Sig>
Expand Down Expand Up @@ -390,6 +411,11 @@ void CosmeticFiltersJSHandler::CSSRulesRoutine(
(IsVettedSearchEngine(url_) && !enabled_1st_party_cf_)) {
hide_selectors_list = nullptr;
}
base::ListValue* force_hide_selectors_list;
if (!resources_dict->GetList("force_hide_selectors",
&force_hide_selectors_list)) {
force_hide_selectors_list = nullptr;
}

if (hide_selectors_list && hide_selectors_list->GetList().size() != 0) {
std::string json_selectors;
Expand All @@ -407,34 +433,41 @@ void CosmeticFiltersJSHandler::CSSRulesRoutine(
blink::BackForwardCacheAware::kAllow);
}

base::Value* force_hide_selectors_list =
resources_dict->FindListKey("force_hide_selectors");
if (force_hide_selectors_list &&
force_hide_selectors_list->GetList().size() != 0) {
std::string stylesheet = "";
for (auto& selector : force_hide_selectors_list->GetList()) {
DCHECK(selector.is_string());
stylesheet += selector.GetString() + "{display:none !important}";
std::string json_selectors;
if (!base::JSONWriter::Write(*force_hide_selectors_list, &json_selectors) ||
json_selectors.empty()) {
json_selectors = "[]";
}
InjectStylesheet(stylesheet, 0);
// Building a script for stylesheet modifications
std::string new_selectors_script = base::StringPrintf(
kForceHideSelectorsInjectScript, json_selectors.c_str());
web_frame->ExecuteScriptInIsolatedWorld(
isolated_world_id_,
blink::WebScriptSource(
blink::WebString::FromUTF8(new_selectors_script)),
blink::BackForwardCacheAware::kAllow);
}

base::Value* style_selectors_dictionary =
resources_dict->FindDictKey("style_selectors");
if (style_selectors_dictionary) {
std::string stylesheet = "";
for (const auto kv : style_selectors_dictionary->DictItems()) {
std::string selector = kv.first;
base::Value& styles = kv.second;
DCHECK(styles.is_list());
stylesheet += selector + '{';
for (auto& style : styles.GetList()) {
DCHECK(style.is_string());
stylesheet += style.GetString() + ';';
}
stylesheet += '}';
base::DictionaryValue* style_selectors_dictionary = nullptr;
if (resources_dict->GetDictionary("style_selectors",
&style_selectors_dictionary)) {
std::string json_selectors;
if (!base::JSONWriter::Write(*style_selectors_dictionary,
&json_selectors) ||
json_selectors.empty()) {
json_selectors = "[]";
}
std::string new_selectors_script =
base::StringPrintf(kStyleSelectorsInjectScript, json_selectors.c_str());
if (!json_selectors.empty()) {
web_frame->ExecuteScriptInIsolatedWorld(
isolated_world_id_,
blink::WebScriptSource(
blink::WebString::FromUTF8(new_selectors_script)),
blink::BackForwardCacheAware::kAllow);
}
InjectStylesheet(stylesheet, 0);
}

if (!enabled_1st_party_cf_)
Expand All @@ -446,6 +479,8 @@ void CosmeticFiltersJSHandler::OnHiddenClassIdSelectors(base::Value result) {
return;
}

blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();

DCHECK(result.is_dict());

base::Value* hide_selectors = result.FindListKey("hide_selectors");
Expand All @@ -456,20 +491,27 @@ void CosmeticFiltersJSHandler::OnHiddenClassIdSelectors(base::Value result) {
DCHECK(force_hide_selectors);

if (force_hide_selectors->GetList().size() != 0) {
std::string stylesheet = "";
for (auto& selector : force_hide_selectors->GetList()) {
DCHECK(selector.is_string());
stylesheet += selector.GetString() + "{display:none !important}";
std::string json_selectors;
if (!base::JSONWriter::Write(force_hide_selectors->GetList(),
&json_selectors) ||
json_selectors.empty()) {
json_selectors = "[]";
}
InjectStylesheet(stylesheet, 0);
// Building a script for stylesheet modifications
std::string new_selectors_script = base::StringPrintf(
kForceHideSelectorsInjectScript, json_selectors.c_str());
web_frame->ExecuteScriptInIsolatedWorld(
isolated_world_id_,
blink::WebScriptSource(
blink::WebString::FromUTF8(new_selectors_script)),
blink::BackForwardCacheAware::kAllow);
}

// If its a vetted engine AND we're not in aggressive
// mode, don't check elements from the default engine (in hide_selectors).
if (!enabled_1st_party_cf_ && IsVettedSearchEngine(url_))
return;

blink::WebLocalFrame* web_frame = render_frame_->GetWebFrame();
std::string json_selectors;
if (!base::JSONWriter::Write(*hide_selectors, &json_selectors) ||
json_selectors.empty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#ifndef BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_HANDLER_H_
#define BRAVE_COMPONENTS_COSMETIC_FILTERS_RENDERER_COSMETIC_FILTERS_JS_HANDLER_H_

#include <map>
#include <memory>
#include <string>
#include <vector>
Expand All @@ -33,7 +32,7 @@ class CosmeticFiltersJSHandler {
const int32_t isolated_world_id);
~CosmeticFiltersJSHandler();

// Adds the "cf_worker" JavaScript object and its functions to the current
// Adds the "cs_worker" JavaScript object and its functions to the current
// render_frame_.
void AddJavaScriptObjectToFrame(v8::Local<v8::Context> context);
// Fetches an initial set of resources to inject into the page if cosmetic
Expand Down Expand Up @@ -69,12 +68,7 @@ class CosmeticFiltersJSHandler {
void OnHiddenClassIdSelectors(base::Value result);
bool OnIsFirstParty(const std::string& url_string);

void InjectStylesheet(const std::string& stylesheet, int id);
void UninjectStylesheet(int id);

bool generichide_ = false;
std::map<int, std::unique_ptr<blink::WebString>> inserted_stylesheet_ids;

raw_ptr<content::RenderFrame> render_frame_ = nullptr;
mojo::Remote<cosmetic_filters::mojom::CosmeticFiltersResources>
cosmetic_filters_resources_;
Expand Down
28 changes: 23 additions & 5 deletions components/cosmetic_filters/resources/data/content_cosmetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ const CC = window.content_cosmetic

CC.cosmeticStyleSheet = CC.cosmeticStyleSheet || new CSSStyleSheet()
CC.allSelectorsToRules = CC.allSelectorsToRules || new Map<string, number>()
// Start from 1; valid indices must be nonzero.
CC.nextRuleIndex = CC.nextRuleIndex || 1
CC.observingHasStarted = CC.observingHasStarted || false
// All new selectors go in `firstRunQueue`
CC.firstRunQueue = CC.firstRunQueue || new Set<string>()
Expand Down Expand Up @@ -333,11 +331,32 @@ const unhideSelectors = (selectors: Set<string>) => {
.sort()
.reverse()
// Delete the rules
let lastIdx: number = CC.allSelectorsToRules.size - 1
for (const ruleIdx of rulesToRemove) {
// Safe to asset ruleIdx is a number because we've already filtered out
// any `undefined` instances with the filter call above.
// @ts-expect-error
cf_worker.uninjectStylesheet(ruleIdx as number)
CC.cosmeticStyleSheet.deleteRule(ruleIdx as number)
}
// Re-sync the indexes
// TODO: Sync is hard, just re-build by iterating through the StyleSheet rules.
const ruleLookup = Array.from(CC.allSelectorsToRules.entries())
let countAtLastHighest = rulesToRemove.length
for (let i = lastIdx; i > 0; i--) {
const [selector, oldIdx] = ruleLookup[i]
// Is this one we removed?
if (rulesToRemove.includes(i)) {
CC.allSelectorsToRules.delete(selector)
countAtLastHighest--
if (countAtLastHighest === 0) {
break
}
continue
}
if (oldIdx !== i) {
// Probably out of sync
console.error('Cosmetic Filters: old index did not match lookup index', { selector, oldIdx, i })
}
CC.allSelectorsToRules.set(selector, oldIdx - countAtLastHighest)
}
}

Expand Down Expand Up @@ -520,6 +539,5 @@ const tryScheduleQueuePump = () => {
}

CC.tryScheduleQueuePump = CC.tryScheduleQueuePump || tryScheduleQueuePump
CC.scheduleQueuePump = CC.scheduleQueuePump || scheduleQueuePump

tryScheduleQueuePump()
2 changes: 0 additions & 2 deletions components/definitions/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ declare global {
content_cosmetic: {
cosmeticStyleSheet: CSSStyleSheet
allSelectorsToRules: Map<string, number>
nextRuleIndex: number
observingHasStarted: boolean
hide1pContent: boolean
generichide: boolean
Expand All @@ -41,7 +40,6 @@ declare global {
alreadyKnownFirstPartySubtrees: WeakSet
_hasDelayOcurred: boolean
_startCheckingId: number | undefined
scheduleQueuePump: ((hide1pContent: boolean, genericHide: boolean) => void)
tryScheduleQueuePump: (() => void)
}
}
Expand Down

0 comments on commit b103f21

Please sign in to comment.