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

Content picker native inject #25411

Merged
merged 27 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/brave_command_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@
// Wayback machine
#define IDC_SHOW_WAYBACK_MACHINE_BUBBLE 56350

// Adblock (user-defined cosmetic filters)
#define IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS 56351

#define IDC_BRAVE_COMMANDS_LAST 57000

#endif // BRAVE_APP_BRAVE_COMMAND_IDS_H_
5 changes: 5 additions & 0 deletions app/brave_generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,11 @@ Or change later at <ph name="SETTINGS_EXTENIONS_LINK">$2<ex>brave://settings/ext
Check details
</message>

<!-- Adblock contenxt menu -->
<message name="IDS_ADBLOCK_CONTEXT_BLOCK_ELEMENTS" desc="Message for context menu that enables the element picker UI to apply cosmetic filter rules to the page">
Block elements
</message>

<message name="IDS_LOCATION_BAR_ONION_AVAILABLE" desc="Button in location bar to indicate onion available site to open a new tab in tor window.">
.onion
</message>
Expand Down
6 changes: 6 additions & 0 deletions browser/brave_content_browser_client.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "brave/browser/brave_wallet/brave_wallet_context_utils.h"
#include "brave/browser/brave_wallet/brave_wallet_provider_delegate_impl.h"
#include "brave/browser/brave_wallet/brave_wallet_service_factory.h"
#include "brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.h"
#include "brave/browser/debounce/debounce_service_factory.h"
#include "brave/browser/ephemeral_storage/ephemeral_storage_service_factory.h"
#include "brave/browser/ephemeral_storage/ephemeral_storage_tab_helper.h"
Expand Down Expand Up @@ -587,6 +588,11 @@ void BraveContentBrowserClient::
&render_frame_host));
#endif // BUILDFLAG(ENABLE_PLAYLIST)

associated_registry.AddInterface<
cosmetic_filters::mojom::CosmeticFiltersHandler>(base::BindRepeating(
&cosmetic_filters::CosmeticFiltersTabHelper::BindCosmeticFiltersHandler,
&render_frame_host));

ChromeContentBrowserClient::
RegisterAssociatedInterfaceBindersForRenderFrameHost(render_frame_host,
associated_registry);
Expand Down
79 changes: 69 additions & 10 deletions browser/brave_shields/ad_block_service_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "base/test/scoped_feature_list.h"
#include "base/test/thread_test_helper.h"
#include "base/threading/thread_restrictions.h"
#include "brave/app/brave_command_ids.h"
#include "brave/browser/brave_browser_process.h"
#include "brave/browser/net/brave_ad_block_tp_network_delegate_helper.h"
#include "brave/components/brave_shields/content/browser/ad_block_custom_filters_provider.h"
Expand All @@ -46,6 +47,7 @@
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_isolated_world_ids.h"
#include "chrome/test/base/chrome_test_utils.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_service.h"
Expand All @@ -58,6 +60,7 @@
#if BUILDFLAG(IS_ANDROID)
#include "chrome/test/base/android/android_browser_test.h"
#else
#include "chrome/browser/renderer_context_menu/render_view_context_menu_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
Expand Down Expand Up @@ -104,6 +107,16 @@ using brave_shields::features::kBraveAdblockDefault1pBlocking;
using brave_shields::features::kBraveAdblockScriptletDebugLogs;
using brave_shields::features::kCosmeticFilteringJsPerformance;

namespace {
void WaitForSelectorBlocked(const content::ToRenderFrameHost& target,
const std::string& selector) {
const char kTemplate[] = R"(waitCSSSelector($1, 'display', 'none'))";

ASSERT_TRUE(
EvalJs(target, content::JsReplace(kTemplate, selector)).ExtractBool());
}
} // namespace

AdBlockServiceTest::AdBlockServiceTest()
: https_server_(net::EmbeddedTestServer::Type::TYPE_HTTPS) {}
AdBlockServiceTest::~AdBlockServiceTest() = default;
Expand Down Expand Up @@ -1713,10 +1726,10 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, RemoveparamSubresource) {
#define MAYBE_RemoveparamTopLevelNavigation \
DISABLED_RemoveparamTopLevelNavigation
#define MAYBE_DefaultRemoveparamFromCustom DISABLED_DefaultRemoveparamFromCustom
#else
#else // BUILDFLAG(IS_ANDROID)
#define MAYBE_RemoveparamTopLevelNavigation RemoveparamTopLevelNavigation
#define MAYBE_DefaultRemoveparamFromCustom DefaultRemoveparamFromCustom
#endif
#endif // BUILDFLAG(IS_ANDROID)

// `$removeparam` should be respected for top-level navigations
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest,
Expand Down Expand Up @@ -2719,6 +2732,60 @@ IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, ListEnabled) {
}
}

// Content Picker and the context menu are disabled for Android.
#if !BUILDFLAG(IS_ANDROID)
IN_PROC_BROWSER_TEST_F(AdBlockServiceTest, ContentPicker) {
const GURL tab_url =
embedded_test_server()->GetURL("a.com", "/cosmetic_filtering.html");
NavigateToURL(tab_url);
const char kPickerIsInjected[] =
"document.getElementById('brave-element-picker') != null";
ASSERT_FALSE(
content::EvalJs(web_contents(), kPickerIsInjected).ExtractBool());

const auto click_menu = [&]() {
content::ContextMenuParams params;
params.page_url = tab_url;
TestRenderViewContextMenu menu(*web_contents()->GetPrimaryMainFrame(),
params);
menu.Init();
EXPECT_TRUE(menu.IsItemEnabled(IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS));
menu.ExecuteCommand(IDC_ADBLOCK_CONTEXT_BLOCK_ELEMENTS, 0);
};

click_menu();

ASSERT_TRUE(content::EvalJs(web_contents(), kPickerIsInjected).ExtractBool());

EXPECT_TRUE(content::EvalJs(web_contents(),
"checkSelector('#ad-banner', 'display', 'block')")
.ExtractBool());

// Emulate selecting some element and clicking `Create` button.
ASSERT_TRUE(content::ExecJs(web_contents(),
"cf_worker.addSiteCosmeticFilter('#ad-banner')",
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
ISOLATED_WORLD_ID_BRAVE_INTERNAL));

// Reload the page and check the selector is blocked by the new rule.
NavigateToURL(tab_url);
WaitForSelectorBlocked(web_contents(), "#ad-banner");
EXPECT_FALSE(
content::EvalJs(web_contents(), kPickerIsInjected).ExtractBool());

click_menu();
// Emulate clicking `Manage filters`.
ASSERT_TRUE(content::ExecJs(web_contents(), "cf_worker.manageCustomFilters()",
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
ISOLATED_WORLD_ID_BRAVE_INTERNAL));

ASSERT_EQ(2, browser()->tab_strip_model()->count());
ASSERT_TRUE(content::WaitForLoadStop(web_contents()));
EXPECT_EQ(web_contents()->GetLastCommittedURL(),
"chrome://settings/shields/filters");
}
#endif // !BUILDFLAG(IS_ANDROID)

class AdBlockServiceTestJsPerformance : public AdBlockServiceTest {
public:
AdBlockServiceTestJsPerformance() {
Expand All @@ -2743,14 +2810,6 @@ class AdBlockServiceTestJsPerformance : public AdBlockServiceTest {
target, content::JsReplace(kTemplate, start_number, end_number)));
}

void WaitForSelectorBlocked(const content::ToRenderFrameHost& target,
const std::string& selector) const {
const char kTemplate[] = R"(waitCSSSelector($1, 'display', 'none'))";

ASSERT_TRUE(
EvalJs(target, content::JsReplace(kTemplate, selector)).ExtractBool());
}

void NonBlockingDelay(const base::TimeDelta& delay) {
base::RunLoop run_loop(base::RunLoop::Type::kNestableTasksAllowed);
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
Expand Down
100 changes: 100 additions & 0 deletions browser/cosmetic_filters/cosmetic_filters_tab_helper.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) 2024 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/.

#include "brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.h"

#include <utility>

#include "base/strings/string_util.h"
#include "brave/browser/brave_browser_process.h"
#include "brave/components/brave_shields/content/browser/ad_block_service.h"
#include "content/public/browser/web_contents.h"
#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"

#if !BUILDFLAG(IS_ANDROID)
#include "brave/browser/ui/brave_pages.h"
#include "chrome/browser/ui/browser_finder.h"
#endif // !BUILDFLAG(IS_ANDROID)

namespace cosmetic_filters {

namespace {
std::string SanitizeSelector(const std::string selector) {
if (!base::IsStringUTF8(selector)) {
return {};
}

// The rules are parsed by adblock-rust via lines() method.
// The method checks a newline byte (the 0xA byte) or CRLF (0xD, 0xA bytes).
// https://doc.rust-lang.org/stable/std/io/trait.BufRead.html#method.lines
if (base::Contains(selector, '\n')) {
return {};
}

return selector;
}
} // namespace

// static
void CosmeticFiltersTabHelper::LaunchContentPicker(
content::WebContents* web_contents) {
CosmeticFiltersTabHelper::CreateForWebContents(web_contents);
if (auto* main_rfh = web_contents->GetPrimaryMainFrame()) {
mojo::AssociatedRemote<mojom::CosmeticFiltersAgent> cosmetic_filter_agent;
main_rfh->GetRemoteAssociatedInterfaces()->GetInterface(
&cosmetic_filter_agent);
cosmetic_filter_agent->LaunchContentPicker();
}
}

// static
void CosmeticFiltersTabHelper::BindCosmeticFiltersHandler(
content::RenderFrameHost* rfh,
mojo::PendingAssociatedReceiver<mojom::CosmeticFiltersHandler> receiver) {
auto* web_contents = content::WebContents::FromRenderFrameHost(rfh);
if (!web_contents) {
return;
}
CosmeticFiltersTabHelper::CreateForWebContents(web_contents);
if (auto* tab_helper =
CosmeticFiltersTabHelper::FromWebContents(web_contents)) {
tab_helper->receivers_.Bind(rfh, std::move(receiver));
}
}

void CosmeticFiltersTabHelper::AddSiteCosmeticFilter(
const std::string& filter) {
// `filter` doesn't have a host, because we don't trust a renderer process.
// Instead, we calculate and add the host explicitly here.
const auto* sender_rfh = receivers_.GetCurrentTargetFrame();
CHECK(sender_rfh);
const std::string sanitized_filter = SanitizeSelector(filter);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@fmarier could you take a look?
To address old and new sec issues here I've:

  1. added a sanitization to drop \n and invalid UTF-8 symbols in filter.
  2. moved adding the host from a renderer process to a browser and use browser-verified origin to add the host part to a new rule.

if (!sanitized_filter.empty()) {
const auto host = sender_rfh->GetLastCommittedOrigin().host();
g_brave_browser_process->ad_block_service()->AddUserCosmeticFilter(
host + "##" + sanitized_filter);
}
}

void CosmeticFiltersTabHelper::ManageCustomFilters() {
#if !BUILDFLAG(IS_ANDROID)
Browser* browser = chrome::FindLastActive();
if (browser) {
brave::ShowBraveAdblock(browser);
}
#else // !BUILDFLAG(IS_ANDROID)
NOTIMPLEMENTED();
#endif // !BUILDFLAG(IS_ANDROID)
}

CosmeticFiltersTabHelper::CosmeticFiltersTabHelper(
content::WebContents* web_contents)
: content::WebContentsUserData<CosmeticFiltersTabHelper>(*web_contents),
receivers_(web_contents, this) {}

CosmeticFiltersTabHelper::~CosmeticFiltersTabHelper() = default;

WEB_CONTENTS_USER_DATA_KEY_IMPL(CosmeticFiltersTabHelper);
} // namespace cosmetic_filters
45 changes: 45 additions & 0 deletions browser/cosmetic_filters/cosmetic_filters_tab_helper.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2024 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/.

#ifndef BRAVE_BROWSER_COSMETIC_FILTERS_COSMETIC_FILTERS_TAB_HELPER_H_
#define BRAVE_BROWSER_COSMETIC_FILTERS_COSMETIC_FILTERS_TAB_HELPER_H_

#include <string>

#include "brave/components/cosmetic_filters/common/cosmetic_filters.mojom.h"
#include "content/public/browser/render_frame_host_receiver_set.h"
#include "content/public/browser/web_contents_user_data.h"

namespace cosmetic_filters {
// A tab helper to communicate with instances of CosmeticFiltersJSHandler.
// Currently it's created on demand and used for Content Picker feature.
class CosmeticFiltersTabHelper
: public content::WebContentsUserData<CosmeticFiltersTabHelper>,
public mojom::CosmeticFiltersHandler {
public:
CosmeticFiltersTabHelper(const CosmeticFiltersTabHelper&) = delete;
CosmeticFiltersTabHelper& operator=(const CosmeticFiltersTabHelper&) = delete;
~CosmeticFiltersTabHelper() override;

static void LaunchContentPicker(content::WebContents* web_contents);

static void BindCosmeticFiltersHandler(
content::RenderFrameHost* rfh,
mojo::PendingAssociatedReceiver<mojom::CosmeticFiltersHandler> receiver);

private:
void AddSiteCosmeticFilter(const std::string& filter) override;
void ManageCustomFilters() override;

friend class content::WebContentsUserData<CosmeticFiltersTabHelper>;

explicit CosmeticFiltersTabHelper(content::WebContents* web_contents);

content::RenderFrameHostReceiverSet<mojom::CosmeticFiltersHandler> receivers_;

WEB_CONTENTS_USER_DATA_KEY_DECL();
};
} // namespace cosmetic_filters
#endif // BRAVE_BROWSER_COSMETIC_FILTERS_COSMETIC_FILTERS_TAB_HELPER_H_
17 changes: 17 additions & 0 deletions browser/cosmetic_filters/sources.gni
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copyright (c) 2024 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/.

brave_browser_cosmetic_filters_sources = [
"//brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.cc",
"//brave/browser/cosmetic_filters/cosmetic_filters_tab_helper.h",
]

brave_browser_cosmetic_filters_deps = [
"//base",
"//brave/browser:browser_process",
"//brave/components/cosmetic_filters/common:mojom",
"//chrome/browser/ui",
"//third_party/blink/public/common",
]
2 changes: 0 additions & 2 deletions browser/extensions/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ source_set("extensions") {
"api/brave_extensions_api_client.h",
"api/brave_rewards_api.cc",
"api/brave_rewards_api.h",
"api/brave_shields_api.cc",
"api/brave_shields_api.h",
"api/brave_talk_api.cc",
"api/brave_talk_api.h",
"api/brave_theme_api.cc",
Expand Down
49 changes: 0 additions & 49 deletions browser/extensions/api/brave_shields_api.cc

This file was deleted.

Loading
Loading