diff --git a/browser/about_flags.cc b/browser/about_flags.cc index 8a8cf03f0ec1..bb6539e69f48 100644 --- a/browser/about_flags.cc +++ b/browser/about_flags.cc @@ -743,6 +743,14 @@ FEATURE_VALUE_TYPE( \ brave_rewards::features::kAnimatedBackgroundFeature), \ }, \ + { \ + "brave-rewards-platform-creator-detection", \ + "Detect Brave Creators on media platform sites", \ + "Enables detection of Brave Creator pages on media platform sites.", \ + kOsDesktop | kOsAndroid, \ + FEATURE_VALUE_TYPE( \ + brave_rewards::features::kPlatformCreatorDetectionFeature), \ + }, \ { \ "brave-ads-should-launch-brave-ads-as-an-in-process-service", \ "Launch Brave Ads as an in-process service", \ diff --git a/browser/brave_rewards/BUILD.gn b/browser/brave_rewards/BUILD.gn index 271f314b98f3..239022016794 100644 --- a/browser/brave_rewards/BUILD.gn +++ b/browser/brave_rewards/BUILD.gn @@ -3,12 +3,12 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this file, # You can obtain one at http://mozilla.org/MPL/2.0/. */ -import("//brave/components/greaselion/browser/buildflags/buildflags.gni") import("//extensions/buildflags/buildflags.gni") import("//testing/test.gni") source_set("brave_rewards") { sources = [ + "creator_detection_script_injector.h", "rewards_service_factory.h", "rewards_tab_helper.h", ] @@ -16,6 +16,7 @@ source_set("brave_rewards") { deps = [ "//base", "//brave/components/brave_rewards/browser", + "//brave/components/script_injector/common/mojom", "//chrome/browser/profiles", "//components/keyed_service/content", "//components/sessions", @@ -25,6 +26,7 @@ source_set("brave_rewards") { source_set("brave_rewards_impl") { sources = [ + "creator_detection_script_injector.cc", "rewards_prefs_util.cc", "rewards_prefs_util.h", "rewards_service_factory.cc", @@ -40,7 +42,8 @@ source_set("brave_rewards_impl") { "//brave/browser/ui:brave_rewards_source", "//brave/components/brave_rewards/browser", "//brave/components/brave_rewards/common", - "//brave/components/greaselion/browser/buildflags", + "//brave/components/brave_rewards/common:features", + "//brave/components/brave_rewards/resources/creator_detection:creator_detection_generated_grit", "//chrome/browser/bitmap_fetcher", "//chrome/browser/favicon", "//chrome/browser/profiles", @@ -61,10 +64,6 @@ source_set("brave_rewards_impl") { deps += [ "//extensions/browser" ] } - - if (enable_greaselion) { - deps += [ "//brave/browser/greaselion" ] - } } source_set("util") { diff --git a/browser/brave_rewards/creator_detection_script_injector.cc b/browser/brave_rewards/creator_detection_script_injector.cc new file mode 100644 index 000000000000..362042d86734 --- /dev/null +++ b/browser/brave_rewards/creator_detection_script_injector.cc @@ -0,0 +1,182 @@ +// 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/brave_rewards/creator_detection_script_injector.h" + +#include + +#include "base/containers/fixed_flat_map.h" +#include "base/feature_list.h" +#include "base/functional/bind.h" +#include "base/strings/strcat.h" +#include "base/strings/utf_string_conversions.h" +#include "brave/browser/brave_rewards/rewards_util.h" +#include "brave/components/brave_rewards/common/features.h" +#include "brave/components/brave_rewards/common/pref_names.h" +#include "brave/components/brave_rewards/common/publisher_utils.h" +#include "brave/components/brave_rewards/resources/grit/creator_detection_generated.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_isolated_world_ids.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/render_frame_host.h" +#include "content/public/common/content_client.h" +#include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h" +#include "ui/base/resource/resource_bundle.h" + +namespace brave_rewards { + +namespace { + +constexpr auto kScriptMap = base::MakeFixedFlatMap( + {{"github.com", IDR_CREATOR_DETECTION_GITHUB_BUNDLE_JS}, + {"www.github.com", IDR_CREATOR_DETECTION_GITHUB_BUNDLE_JS}, + {"gist.github.com", IDR_CREATOR_DETECTION_GITHUB_BUNDLE_JS}, + {"reddit.com", IDR_CREATOR_DETECTION_REDDIT_BUNDLE_JS}, + {"www.reddit.com", IDR_CREATOR_DETECTION_REDDIT_BUNDLE_JS}, + {"twitch.tv", IDR_CREATOR_DETECTION_TWITCH_BUNDLE_JS}, + {"www.twitch.tv", IDR_CREATOR_DETECTION_TWITCH_BUNDLE_JS}, + {"twitter.com", IDR_CREATOR_DETECTION_TWITTER_BUNDLE_JS}, + {"x.com", IDR_CREATOR_DETECTION_TWITTER_BUNDLE_JS}, + {"vimeo.com", IDR_CREATOR_DETECTION_VIMEO_BUNDLE_JS}, + {"www.youtube.com", IDR_CREATOR_DETECTION_YOUTUBE_BUNDLE_JS}, + {"m.youtube.com", IDR_CREATOR_DETECTION_YOUTUBE_BUNDLE_JS}}); + +std::string LoadScriptResource(int id) { + auto& bundle = ui::ResourceBundle::GetSharedInstance(); + if (bundle.IsGzipped(id)) { + return bundle.LoadDataResourceString(id); + } + return std::string(bundle.GetRawDataResource(id)); +} + +std::optional GetDetectionScript(content::RenderFrameHost* rfh) { + // Only run scripts for the main frame. + if (!rfh || !rfh->IsInPrimaryMainFrame()) { + return std::nullopt; + } + + // Only run scripts if the creator detection feature is enabled. + if (!base::FeatureList::IsEnabled( + features::kPlatformCreatorDetectionFeature)) { + return std::nullopt; + } + + auto* profile = Profile::FromBrowserContext(rfh->GetBrowserContext()); + + // Only run scripts if the Rewards service is available for this profile. + if (!IsSupportedForProfile(profile)) { + return std::nullopt; + } + + // Only run scripts if the user has enabled Brave Rewards. + if (!profile || !profile->GetPrefs()->GetBoolean(prefs::kEnabled)) { + return std::nullopt; + } + + // Only run scripts for known "media platform" sites. + GURL url = rfh->GetLastCommittedURL(); + if (!IsMediaPlatformURL(url)) { + return std::nullopt; + } + + // Only run scripts when there is an exact hostname match. + auto iter = kScriptMap.find(url.host_piece()); + if (iter == kScriptMap.end()) { + return std::nullopt; + } + + return LoadScriptResource(iter->second); +} + +} // namespace + +CreatorDetectionScriptInjector::CreatorDetectionScriptInjector() = default; +CreatorDetectionScriptInjector::~CreatorDetectionScriptInjector() = default; + +void CreatorDetectionScriptInjector::MaybeInjectScript( + content::RenderFrameHost* rfh) { + injector_.reset(); + injector_host_token_ = content::GlobalRenderFrameHostToken(); + + if (!rfh) { + return; + } + + auto script_source = GetDetectionScript(rfh); + if (!script_source) { + return; + } + + injector_host_token_ = rfh->GetGlobalFrameToken(); + rfh->GetRemoteAssociatedInterfaces()->GetInterface(&injector_); + + bool verbose_logging = + base::FeatureList::IsEnabled(features::kVerboseLoggingFeature); + + // Add a `braveRewards` global variable to the isolated world. + std::string initializer = + base::StrCat({"self.braveRewards = { verboseLogging: ", + verbose_logging ? "true" : "false", " }"}); + ExecuteScript(initializer, base::DoNothing()); + + // Execute the detection script. It must set `braveRewards.detectCreator` to a + // function. That function will be called by `DetectCreator`. + ExecuteScript(script_source.value(), base::DoNothing()); +} + +void CreatorDetectionScriptInjector::DetectCreator( + content::RenderFrameHost* rfh, + DetectCreatorCallback callback) { + if (!rfh || rfh->GetGlobalFrameToken() != injector_host_token_ || + !injector_.is_bound()) { + std::move(callback).Run(std::nullopt); + return; + } + + // Call the detection function set up by the detection script. + ExecuteScript( + "braveRewards.detectCreator()", + base::BindOnce(&CreatorDetectionScriptInjector::OnCreatorDetected, + weak_factory_.GetWeakPtr(), std::move(callback))); +} + +void CreatorDetectionScriptInjector::ExecuteScript( + std::string_view script, + ExecuteScriptCallback callback) { + CHECK(injector_.is_bound()); + injector_->RequestAsyncExecuteScript( + ISOLATED_WORLD_ID_BRAVE_INTERNAL, base::UTF8ToUTF16(script), + blink::mojom::UserActivationOption::kDoNotActivate, + blink::mojom::PromiseResultOption::kAwait, std::move(callback)); +} + +void CreatorDetectionScriptInjector::OnCreatorDetected( + DetectCreatorCallback callback, + base::Value value) { + Result result; + if (auto* dict = value.GetIfDict()) { + if (auto* id = dict->FindString("id")) { + result.id = *id; + } + if (auto* name = dict->FindString("name")) { + result.name = *name; + } + if (auto* url = dict->FindString("url")) { + result.url = *url; + } + if (auto* image_url = dict->FindString("imageURL")) { + result.image_url = *image_url; + } + } + std::move(callback).Run(std::move(result)); +} + +CreatorDetectionScriptInjector::Result::Result() = default; +CreatorDetectionScriptInjector::Result::~Result() = default; +CreatorDetectionScriptInjector::Result::Result(const Result&) = default; +CreatorDetectionScriptInjector::Result& +CreatorDetectionScriptInjector::Result::operator=(const Result&) = default; + +} // namespace brave_rewards diff --git a/browser/brave_rewards/creator_detection_script_injector.h b/browser/brave_rewards/creator_detection_script_injector.h new file mode 100644 index 000000000000..0895770ba148 --- /dev/null +++ b/browser/brave_rewards/creator_detection_script_injector.h @@ -0,0 +1,79 @@ +// 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_BRAVE_REWARDS_CREATOR_DETECTION_SCRIPT_INJECTOR_H_ +#define BRAVE_BROWSER_BRAVE_REWARDS_CREATOR_DETECTION_SCRIPT_INJECTOR_H_ + +#include +#include +#include + +#include "base/functional/callback.h" +#include "base/memory/weak_ptr.h" +#include "base/values.h" +#include "brave/components/script_injector/common/mojom/script_injector.mojom.h" +#include "content/public/browser/global_routing_id.h" +#include "mojo/public/cpp/bindings/associated_remote.h" + +namespace content { +class RenderFrameHost; +} + +namespace brave_rewards { + +// Responsible for detecting Brave creator information associated with media +// platform pages, using JS scripts that are injected into an isolated world. +class CreatorDetectionScriptInjector { + public: + CreatorDetectionScriptInjector(); + ~CreatorDetectionScriptInjector(); + + CreatorDetectionScriptInjector(const CreatorDetectionScriptInjector&) = + delete; + CreatorDetectionScriptInjector& operator=( + const CreatorDetectionScriptInjector&) = delete; + + // Injects creator detection scripts (if appropriate) into an isolated world + // associated with the specified render frame host. The scripts are expected + // to set up a JS function that will later be called by `DetectCreator`. + void MaybeInjectScript(content::RenderFrameHost* rfh); + + struct Result { + Result(); + ~Result(); + Result(const Result&); + Result& operator=(const Result&); + + std::string id; + std::string name; + std::string url; + std::string image_url; + }; + + using DetectCreatorCallback = base::OnceCallback)>; + + // Runs the creator detection routine initialized by `MaybeInjectScript` and + // asynchrounously returns the detection result. Return `nullopt` if the + // detection routine was not invoked (e.g. because Rewards is not enabled or + // because there is no script for this page). Returns a `Result` with empty + // fields if there is not creator associated with the current page. + void DetectCreator(content::RenderFrameHost* rfh, + DetectCreatorCallback callback); + + private: + using ExecuteScriptCallback = base::OnceCallback; + + void ExecuteScript(std::string_view script, ExecuteScriptCallback callback); + + void OnCreatorDetected(DetectCreatorCallback callback, base::Value value); + + mojo::AssociatedRemote injector_; + content::GlobalRenderFrameHostToken injector_host_token_; + base::WeakPtrFactory weak_factory_{this}; +}; + +} // namespace brave_rewards + +#endif // BRAVE_BROWSER_BRAVE_REWARDS_CREATOR_DETECTION_SCRIPT_INJECTOR_H_ diff --git a/browser/brave_rewards/rewards_service_factory.cc b/browser/brave_rewards/rewards_service_factory.cc index 1bca8a4bc6d9..414063bb3679 100644 --- a/browser/brave_rewards/rewards_service_factory.cc +++ b/browser/brave_rewards/rewards_service_factory.cc @@ -16,7 +16,6 @@ #include "brave/components/brave_rewards/browser/rewards_service.h" #include "brave/components/brave_rewards/browser/rewards_service_impl.h" #include "brave/components/brave_rewards/browser/rewards_service_observer.h" -#include "brave/components/greaselion/browser/buildflags/buildflags.h" #include "chrome/browser/bitmap_fetcher/bitmap_fetcher_service_factory.h" #include "chrome/browser/favicon/favicon_service_factory.h" #include "chrome/browser/profiles/incognito_helpers.h" @@ -31,10 +30,6 @@ #include "extensions/browser/event_router_factory.h" #endif -#if BUILDFLAG(ENABLE_GREASELION) -#include "brave/browser/greaselion/greaselion_service_factory.h" -#endif - namespace brave_rewards { RewardsService* testing_service_ = nullptr; @@ -65,9 +60,6 @@ RewardsServiceFactory::RewardsServiceFactory() BrowserContextDependencyManager::GetInstance()) { #if BUILDFLAG(ENABLE_EXTENSIONS) DependsOn(extensions::EventRouterFactory::GetInstance()); -#endif -#if BUILDFLAG(ENABLE_GREASELION) - DependsOn(greaselion::GreaselionServiceFactory::GetInstance()); #endif DependsOn(brave_wallet::BraveWalletServiceFactory::GetInstance()); } @@ -123,9 +115,6 @@ RewardsServiceFactory::BuildServiceInstanceForBrowserContext( profile, ServiceAccessType::EXPLICIT_ACCESS), request_image_callback, cancel_request_image_callback, profile->GetDefaultStoragePartition(), -#if BUILDFLAG(ENABLE_GREASELION) - greaselion::GreaselionServiceFactory::GetForBrowserContext(context), -#endif brave_wallet::BraveWalletServiceFactory::GetServiceForContext( context)); rewards_service->Init(std::move(extension_observer), diff --git a/browser/brave_rewards/rewards_tab_helper.cc b/browser/brave_rewards/rewards_tab_helper.cc index e21f4acce14d..9407f5eb735f 100644 --- a/browser/brave_rewards/rewards_tab_helper.cc +++ b/browser/brave_rewards/rewards_tab_helper.cc @@ -5,9 +5,11 @@ #include "brave/browser/brave_rewards/rewards_tab_helper.h" +#include + #include "brave/browser/brave_rewards/rewards_service_factory.h" -#include "brave/components/brave_rewards/browser/publisher_utils.h" #include "brave/components/brave_rewards/browser/rewards_service.h" +#include "brave/components/brave_rewards/common/publisher_utils.h" #include "chrome/browser/profiles/profile.h" #include "components/sessions/content/session_tab_helper.h" #include "content/public/browser/navigation_entry.h" @@ -98,19 +100,35 @@ void RewardsTabHelper::DidFinishLoad( rewards_service_->OnLoad(tab_id_, validated_url); } -void RewardsTabHelper::DidFinishNavigation(content::NavigationHandle* handle) { - if (!handle->IsInMainFrame() || !handle->HasCommitted() || - handle->IsDownload()) { +void RewardsTabHelper::DidFinishNavigation( + content::NavigationHandle* navigation_handle) { + if (!navigation_handle->HasCommitted() || + !navigation_handle->IsInPrimaryMainFrame() || + navigation_handle->IsDownload()) { return; } - auto id = GetPublisherIdFromURL(GetWebContents().GetLastCommittedURL()); - SetPublisherIdForTab(id ? *id : ""); + if (!rewards_service_) { + return; + } - MaybeSavePublisherInfo(); + auto& url = GetWebContents().GetLastCommittedURL(); - if (rewards_service_) { + if (!navigation_handle->IsSameDocument()) { + auto id = GetPublisherIdFromURL(url); + SetPublisherIdForTab(id ? *id : ""); + MaybeSavePublisherInfo(); rewards_service_->OnUnload(tab_id_); + creator_detection_.MaybeInjectScript( + navigation_handle->GetRenderFrameHost()); + } + + if (url != last_detection_url_) { + last_detection_url_ = url; + creator_detection_.DetectCreator( + navigation_handle->GetRenderFrameHost(), + base::BindOnce(&RewardsTabHelper::OnCreatorDetected, + base::Unretained(this), url)); } } @@ -192,13 +210,45 @@ void RewardsTabHelper::MaybeSavePublisherInfo() { } // The Rewards system currently assumes that the |publisher_info| table is - // populated by calling `GetPublisherActivityFromUrl` as the user nativates + // populated by calling `GetPublisherActivityFromUrl` as the user navigates // the web. Previously, this was accomplished within the background script of // the Rewards extension. rewards_service_->GetPublisherActivityFromUrl( tab_id_.id(), GetWebContents().GetLastCommittedURL().spec(), "", ""); } +void RewardsTabHelper::OnCreatorDetected( + GURL url, + std::optional result) { + if (!rewards_service_ || !result || + url != GetWebContents().GetLastCommittedURL()) { + return; + } + + SetPublisherIdForTab(result->id); + + if (!result->id.empty()) { + // When a creator has been detected for the current tab, we must send the + // creator data to the utility process so that the "publisher_info" database + // table can be populated. We must also notify the utility process that a + // "page view" has started for this creator. + auto visit = mojom::VisitData::New(); + visit->tab_id = static_cast(tab_id_.id()); + visit->domain = result->id; + visit->name = result->name; + visit->path = ""; + visit->url = result->url; + visit->favicon_url = result->image_url; + if (auto platform = GetMediaPlatformFromPublisherId(result->id)) { + visit->provider = *platform; + } + + rewards_service_->GetPublisherActivityFromVisitData(visit->Clone()); + rewards_service_->OnShow(tab_id_); + rewards_service_->OnLoad(std::move(visit)); + } +} + WEB_CONTENTS_USER_DATA_KEY_IMPL(RewardsTabHelper); } // namespace brave_rewards diff --git a/browser/brave_rewards/rewards_tab_helper.h b/browser/brave_rewards/rewards_tab_helper.h index 685d70c1c8aa..1258241a7fd1 100644 --- a/browser/brave_rewards/rewards_tab_helper.h +++ b/browser/brave_rewards/rewards_tab_helper.h @@ -7,12 +7,14 @@ #define BRAVE_BROWSER_BRAVE_REWARDS_REWARDS_TAB_HELPER_H_ #include +#include #include #include "base/memory/raw_ptr.h" #include "base/observer_list.h" #include "base/observer_list_types.h" #include "base/scoped_observation.h" +#include "brave/browser/brave_rewards/creator_detection_script_injector.h" #include "brave/components/brave_rewards/browser/rewards_service_observer.h" #include "build/build_config.h" #include "components/sessions/core/session_id.h" @@ -85,6 +87,10 @@ class RewardsTabHelper : public content::WebContentsUserData, void MaybeSavePublisherInfo(); + void OnCreatorDetected( + GURL url, + std::optional result); + #if !BUILDFLAG(IS_ANDROID) std::unique_ptr browser_list_observer_; #endif @@ -95,6 +101,8 @@ class RewardsTabHelper : public content::WebContentsUserData, raw_ptr rewards_service_ = nullptr; base::ObserverList observer_list_; std::string publisher_id_; + GURL last_detection_url_; + CreatorDetectionScriptInjector creator_detection_; WEB_CONTENTS_USER_DATA_KEY_DECL(); }; diff --git a/browser/brave_rewards/test/BUILD.gn b/browser/brave_rewards/test/BUILD.gn index f585b6a5fae9..82790816a5bb 100644 --- a/browser/brave_rewards/test/BUILD.gn +++ b/browser/brave_rewards/test/BUILD.gn @@ -9,6 +9,7 @@ source_set("browser_tests") { testonly = true if (!is_android) { sources = [ + "creator_detection_browsertest.cc", "rewards_ofac_browsertest.cc", "rewards_page_browsertest.cc", "rewards_policy_browsertest.cc", diff --git a/browser/brave_rewards/test/creator_detection_browsertest.cc b/browser/brave_rewards/test/creator_detection_browsertest.cc new file mode 100644 index 000000000000..502a6f6ae6a2 --- /dev/null +++ b/browser/brave_rewards/test/creator_detection_browsertest.cc @@ -0,0 +1,360 @@ +// 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 +#include +#include + +#include "base/memory/raw_ref.h" +#include "base/test/bind.h" +#include "base/test/scoped_feature_list.h" +#include "base/test/test_future.h" +#include "base/timer/timer.h" +#include "brave/browser/brave_rewards/rewards_tab_helper.h" +#include "brave/components/brave_rewards/common/features.h" +#include "brave/components/brave_rewards/common/pref_names.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/test/base/chrome_test_utils.h" +#include "chrome/test/base/in_process_browser_test.h" +#include "chrome/test/base/ui_test_utils.h" +#include "components/prefs/pref_service.h" +#include "content/public/test/browser_test.h" +#include "content/public/test/browser_test_utils.h" +#include "net/dns/mock_host_resolver.h" + +using net::test_server::BasicHttpResponse; +using net::test_server::HttpRequest; +using net::test_server::HttpResponse; + +namespace brave_rewards { + +class CreatorDetectionBrowserTest : public InProcessBrowserTest { + protected: + CreatorDetectionBrowserTest() { + scoped_feature_list_.InitWithFeatures( + {features::kPlatformCreatorDetectionFeature}, {}); + } + + ~CreatorDetectionBrowserTest() override = default; + + void SetUpOnMainThread() override { + InProcessBrowserTest::SetUpOnMainThread(); + + host_resolver()->AddRule("*", "127.0.0.1"); + embedded_https_test_server().SetCertHostnames( + {"twitter.com", "github.com", "api.github.com", "reddit.com", + "www.twitch.tv", "vimeo.com", "www.youtube.com"}); + embedded_https_test_server().RegisterRequestHandler(base::BindRepeating( + &CreatorDetectionBrowserTest::HandleRequest, base::Unretained(this))); + CHECK(embedded_https_test_server().Start()); + } + + void TearDownOnMainThread() override { + EXPECT_TRUE(embedded_https_test_server().ShutdownAndWaitUntilComplete()); + InProcessBrowserTest::TearDownOnMainThread(); + } + + using RequestCallback = + base::RepeatingCallback; + + template + void SetRequestCallback(F callback) { + request_callback_ = base::BindLambdaForTesting(callback); + } + + void SetResponseHTML(std::string_view html) { + std::string content(html); + SetRequestCallback( + [content](std::string_view path, BasicHttpResponse& response) { + response.set_content(content); + }); + } + + void SetRewardsEnabled() { + auto* prefs = browser()->profile()->GetPrefs(); + prefs->SetBoolean(brave_rewards::prefs::kEnabled, true); + } + + bool NavigateTo(std::string_view host, std::string_view path) { + auto url = embedded_https_test_server().GetURL(host, path); + return ui_test_utils::NavigateToURL(browser(), url); + } + + RewardsTabHelper& GetRewardsTabHelper() { + auto* web_contents = chrome_test_utils::GetActiveWebContents(this); + CHECK(web_contents); + auto* tab_helper = RewardsTabHelper::FromWebContents(web_contents); + CHECK(tab_helper); + return *tab_helper; + } + + void WaitForTimeout() { + base::RunLoop run_loop; + base::OneShotTimer timeout; + timeout.Start(FROM_HERE, base::Seconds(2), run_loop.QuitClosure()); + run_loop.Run(); + } + + class TabHelperObserver : public RewardsTabHelper::Observer { + public: + TabHelperObserver(RewardsTabHelper& tab_helper, + base::OnceCallback callback) + : callback_(std::move(callback)) { + tab_helper_observation_.Observe(&tab_helper); + } + + ~TabHelperObserver() override = default; + + void OnPublisherForTabUpdated(const std::string& publisher_id) override { + if (callback_) { + std::move(callback_).Run(publisher_id); + } + } + + private: + base::OnceCallback callback_; + RewardsTabHelper::Observation tab_helper_observation_{this}; + }; + + private: + std::unique_ptr HandleRequest(const HttpRequest& request) { + auto response = std::make_unique(); + response->set_code(net::HTTP_OK); + response->set_content_type("text/html;charset=utf-8"); + if (request_callback_) { + request_callback_.Run(request.relative_url, *response); + } + return response; + } + + base::test::ScopedFeatureList scoped_feature_list_; + RequestCallback request_callback_; + std::string response_html_; +}; + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, GithubDetection) { + SetRewardsEnabled(); + + SetRequestCallback([](std::string_view path, BasicHttpResponse& response) { + if (path == "/users/testuser") { + response.AddCustomHeader("Access-Control-Allow-Origin", "*"); + response.set_content_type("application/json"); + response.set_content(R"( + {"id": "1234567", + "avatar_url": "https://github.com/user-avatar"} )"); + return; + } + }); + + AddBlankTabAndShow(browser()); + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + ASSERT_TRUE(NavigateTo("github.com", "/testuser")); + + EXPECT_EQ(future.Get<0>(), "github#channel:1234567"); +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, RedditDetection) { + SetRewardsEnabled(); + + SetRequestCallback([](std::string_view path, BasicHttpResponse& response) { + if (path == "/user/testuser/about.json") { + response.set_content_type("application/json"); + response.set_content(R"( + {"kind": "t2", + "data": { + "id": "987654321", + "icon_img": "https://reddit.com/user-avatar"}} )"); + return; + } + }); + + AddBlankTabAndShow(browser()); + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + ASSERT_TRUE(NavigateTo("reddit.com", "/user/testuser")); + + EXPECT_EQ(future.Get<0>(), "reddit#channel:987654321"); +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, TwitchDetection) { + SetRewardsEnabled(); + + SetResponseHTML(R"( + + + +

Name

+
+
+ +
+
+ + + )"); + + AddBlankTabAndShow(browser()); + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + ASSERT_TRUE(NavigateTo("www.twitch.tv", "/testuser")); + + EXPECT_EQ(future.Get<0>(), "twitch#author:testuser"); +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, TwitterDetection) { + SetRewardsEnabled(); + + SetResponseHTML(R"( + + + + + + +
+
+
+ + + )"); + + AddBlankTabAndShow(browser()); + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + ASSERT_TRUE(NavigateTo("twitter.com", "/testuser")); + + EXPECT_EQ(future.Get<0>(), "twitter#channel:987654321"); +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, VimeoDetection) { + SetRewardsEnabled(); + + SetResponseHTML(R"( + + + + + + + + + )"); + + AddBlankTabAndShow(browser()); + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + ASSERT_TRUE(NavigateTo("vimeo.com", "/testuser")); + + EXPECT_EQ(future.Get<0>(), "vimeo#channel:987654321"); +} + +constexpr std::string_view kYouTubeHTML = R"( + + + + + + + + + + +
+ +
+ + + +)"; + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, YouTubeDetection) { + SetRewardsEnabled(); + SetResponseHTML(kYouTubeHTML); + + AddBlankTabAndShow(browser()); + + { + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + ASSERT_TRUE(NavigateTo("www.youtube.com", "/@testuser")); + EXPECT_EQ(future.Get<0>(), "youtube#channel:987654321"); + } + + { + base::test::TestFuture future; + TabHelperObserver observer(GetRewardsTabHelper(), future.GetCallback()); + auto* web_contents = chrome_test_utils::GetActiveWebContents(this); + EXPECT_TRUE(content::ExecJs(web_contents, "triggerSameDocNav()")); + EXPECT_EQ(future.Get<0>(), "youtube#channel:123456789"); + } +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, InvalidHost) { + SetRewardsEnabled(); + SetResponseHTML(kYouTubeHTML); + AddBlankTabAndShow(browser()); + ASSERT_TRUE(NavigateTo("abc.youtube.com", "/@testuser")); + WaitForTimeout(); + EXPECT_EQ(GetRewardsTabHelper().GetPublisherIdForTab(), ""); +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, RewardsDisabled) { + SetResponseHTML(kYouTubeHTML); + AddBlankTabAndShow(browser()); + ASSERT_TRUE(NavigateTo("www.youtube.com", "/@testuser")); + WaitForTimeout(); + EXPECT_EQ(GetRewardsTabHelper().GetPublisherIdForTab(), ""); +} + +IN_PROC_BROWSER_TEST_F(CreatorDetectionBrowserTest, IncognitoProfile) { + SetRewardsEnabled(); + SetResponseHTML(kYouTubeHTML); + auto* incognito_browser = CreateIncognitoBrowser(); + AddBlankTabAndShow(incognito_browser); + auto url = + embedded_https_test_server().GetURL("www.youtube.com", "/@testuser"); + ASSERT_TRUE(ui_test_utils::NavigateToURL(incognito_browser, url)); + WaitForTimeout(); + EXPECT_EQ(GetRewardsTabHelper().GetPublisherIdForTab(), ""); +} + +} // namespace brave_rewards diff --git a/browser/extensions/BUILD.gn b/browser/extensions/BUILD.gn index 13da5af129bd..b61a0581a6cb 100644 --- a/browser/extensions/BUILD.gn +++ b/browser/extensions/BUILD.gn @@ -25,11 +25,9 @@ source_set("component_loader") { deps = [ "//base", - "//brave/browser/brave_rewards:util", "//brave/browser/ethereum_remote_client/buildflags", "//brave/components/brave_component_updater/browser", "//brave/components/brave_extension:static_resources", - "//brave/components/brave_rewards/common", "//brave/components/brave_webtorrent:static_resources", "//chrome/browser:browser_process", "//chrome/browser/extensions", diff --git a/browser/extensions/api/brave_rewards_api.cc b/browser/extensions/api/brave_rewards_api.cc index 3f65b25a034f..6398476e65d8 100644 --- a/browser/extensions/api/brave_rewards_api.cc +++ b/browser/extensions/api/brave_rewards_api.cc @@ -157,29 +157,6 @@ ExtensionFunction::ResponseAction BraveRewardsOpenRewardsPanelFunction::Run() { return RespondNow(NoArguments()); } -BraveRewardsUpdateMediaDurationFunction:: - ~BraveRewardsUpdateMediaDurationFunction() = default; - -ExtensionFunction::ResponseAction -BraveRewardsUpdateMediaDurationFunction::Run() { - std::optional params = - brave_rewards::UpdateMediaDuration::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params); - - Profile* profile = Profile::FromBrowserContext(browser_context()); - RewardsService* rewards_service = - RewardsServiceFactory::GetForProfile(profile); - - if (!rewards_service) { - return RespondNow(NoArguments()); - } - - rewards_service->UpdateMediaDuration(params->window_id, params->publisher_key, - params->duration, params->first_visit); - - return RespondNow(NoArguments()); -} - BraveRewardsGetPublisherInfoFunction::~BraveRewardsGetPublisherInfoFunction() = default; @@ -226,24 +203,6 @@ void BraveRewardsGetPublisherInfoFunction::OnGetPublisherInfo( Respond(WithArguments(static_cast(result), std::move(dict))); } -BraveRewardsSetPublisherIdForTabFunction:: - ~BraveRewardsSetPublisherIdForTabFunction() = default; - -ExtensionFunction::ResponseAction -BraveRewardsSetPublisherIdForTabFunction::Run() { - auto params = brave_rewards::SetPublisherIdForTab::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params); - - auto* tab_helper = - GetRewardsTabHelperForTabId(params->tab_id, browser_context()); - - if (tab_helper) { - tab_helper->SetPublisherIdForTab(params->publisher_id); - } - - return RespondNow(NoArguments()); -} - BraveRewardsGetPublisherInfoForTabFunction:: ~BraveRewardsGetPublisherInfoForTabFunction() = default; @@ -300,90 +259,6 @@ void BraveRewardsGetPublisherInfoForTabFunction::OnGetPublisherPanelInfo( Respond(WithArguments(std::move(dict))); } -BraveRewardsGetPublisherPanelInfoFunction:: - ~BraveRewardsGetPublisherPanelInfoFunction() = default; - -ExtensionFunction::ResponseAction -BraveRewardsGetPublisherPanelInfoFunction::Run() { - std::optional params = - brave_rewards::GetPublisherPanelInfo::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params); - - Profile* profile = Profile::FromBrowserContext(browser_context()); - RewardsService* rewards_service = - RewardsServiceFactory::GetForProfile(profile); - - if (!rewards_service) { - return RespondNow(Error("Rewards service is not available")); - } - - rewards_service->GetPublisherPanelInfo( - params->publisher_key, - base::BindOnce( - &BraveRewardsGetPublisherPanelInfoFunction::OnGetPublisherPanelInfo, - this)); - - return RespondLater(); -} - -void BraveRewardsGetPublisherPanelInfoFunction::OnGetPublisherPanelInfo( - const ::brave_rewards::mojom::Result result, - ::brave_rewards::mojom::PublisherInfoPtr info) { - if (!info) { - Respond(WithArguments(static_cast(result))); - return; - } - - base::Value::Dict dict; - dict.Set("publisherKey", info->id); - dict.Set("name", info->name); - dict.Set("percentage", static_cast(info->percent)); - dict.Set("status", static_cast(info->status)); - dict.Set("excluded", info->excluded == - ::brave_rewards::mojom::PublisherExclude::EXCLUDED); - dict.Set("url", info->url); - dict.Set("provider", info->provider); - dict.Set("favIconUrl", info->favicon_url); - - Respond(WithArguments(static_cast(result), std::move(dict))); -} - -BraveRewardsSavePublisherInfoFunction:: - ~BraveRewardsSavePublisherInfoFunction() = default; - -ExtensionFunction::ResponseAction BraveRewardsSavePublisherInfoFunction::Run() { - std::optional params = - brave_rewards::SavePublisherInfo::Params::Create(args()); - EXTENSION_FUNCTION_VALIDATE(params); - - Profile* profile = Profile::FromBrowserContext(browser_context()); - RewardsService* rewards_service = - RewardsServiceFactory::GetForProfile(profile); - - if (!rewards_service) { - return RespondNow(Error("Rewards service is not available")); - } - - auto publisher_info = ::brave_rewards::mojom::PublisherInfo::New(); - publisher_info->id = params->publisher_key; - publisher_info->name = params->publisher_name; - publisher_info->url = params->url; - publisher_info->provider = params->media_type; - publisher_info->favicon_url = params->fav_icon_url; - - rewards_service->SavePublisherInfo( - params->window_id, std::move(publisher_info), - base::BindOnce( - &BraveRewardsSavePublisherInfoFunction::OnSavePublisherInfo, this)); - - return RespondLater(); -} - -void BraveRewardsSavePublisherInfoFunction::OnSavePublisherInfo( - const ::brave_rewards::mojom::Result result) { - Respond(WithArguments(static_cast(result))); -} - BraveRewardsTipSiteFunction::~BraveRewardsTipSiteFunction() = default; ExtensionFunction::ResponseAction BraveRewardsTipSiteFunction::Run() { @@ -424,22 +299,6 @@ BraveRewardsIncludeInAutoContributionFunction::Run() { return RespondNow(NoArguments()); } -BraveRewardsGetPublisherDataFunction::~BraveRewardsGetPublisherDataFunction() = - default; - -ExtensionFunction::ResponseAction BraveRewardsGetPublisherDataFunction::Run() { - std::optional params = - brave_rewards::GetPublisherData::Params::Create(args()); - Profile* profile = Profile::FromBrowserContext(browser_context()); - auto* rewards_service = RewardsServiceFactory::GetForProfile(profile); - if (rewards_service) { - rewards_service->GetPublisherActivityFromUrl(params->window_id, params->url, - params->favicon_url, - params->publisher_blob); - } - return RespondNow(NoArguments()); -} - BraveRewardsGetRewardsParametersFunction:: ~BraveRewardsGetRewardsParametersFunction() = default; diff --git a/browser/extensions/api/brave_rewards_api.h b/browser/extensions/api/brave_rewards_api.h index 7746acacf92d..512b9f08637a 100644 --- a/browser/extensions/api/brave_rewards_api.h +++ b/browser/extensions/api/brave_rewards_api.h @@ -48,16 +48,6 @@ class BraveRewardsOpenRewardsPanelFunction : public ExtensionFunction { ResponseAction Run() override; }; -class BraveRewardsUpdateMediaDurationFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveRewards.updateMediaDuration", UNKNOWN) - - protected: - ~BraveRewardsUpdateMediaDurationFunction() override; - - ResponseAction Run() override; -}; - class BraveRewardsGetPublisherInfoFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveRewards.getPublisherInfo", UNKNOWN) @@ -72,15 +62,6 @@ class BraveRewardsGetPublisherInfoFunction : public ExtensionFunction { brave_rewards::mojom::PublisherInfoPtr info); }; -class BraveRewardsSetPublisherIdForTabFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveRewards.setPublisherIdForTab", UNKNOWN) - - protected: - ~BraveRewardsSetPublisherIdForTabFunction() override; - ResponseAction Run() override; -}; - class BraveRewardsGetPublisherInfoForTabFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveRewards.getPublisherInfoForTab", UNKNOWN) @@ -95,33 +76,6 @@ class BraveRewardsGetPublisherInfoForTabFunction : public ExtensionFunction { brave_rewards::mojom::PublisherInfoPtr info); }; -class BraveRewardsGetPublisherPanelInfoFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveRewards.getPublisherPanelInfo", UNKNOWN) - - protected: - ~BraveRewardsGetPublisherPanelInfoFunction() override; - - ResponseAction Run() override; - - private: - void OnGetPublisherPanelInfo(const brave_rewards::mojom::Result result, - brave_rewards::mojom::PublisherInfoPtr info); -}; - -class BraveRewardsSavePublisherInfoFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveRewards.savePublisherInfo", UNKNOWN) - - protected: - ~BraveRewardsSavePublisherInfoFunction() override; - - ResponseAction Run() override; - - private: - void OnSavePublisherInfo(const brave_rewards::mojom::Result result); -}; - class BraveRewardsTipSiteFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveRewards.tipSite", UNKNOWN) @@ -132,16 +86,6 @@ class BraveRewardsTipSiteFunction : public ExtensionFunction { ResponseAction Run() override; }; -class BraveRewardsGetPublisherDataFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("braveRewards.getPublisherData", UNKNOWN) - - protected: - ~BraveRewardsGetPublisherDataFunction() override; - - ResponseAction Run() override; -}; - class BraveRewardsGetRewardsParametersFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("braveRewards.getRewardsParameters", UNKNOWN) diff --git a/browser/extensions/brave_component_loader.cc b/browser/extensions/brave_component_loader.cc index dff7e375792d..4795394c4dee 100644 --- a/browser/extensions/brave_component_loader.cc +++ b/browser/extensions/brave_component_loader.cc @@ -11,11 +11,9 @@ #include "base/command_line.h" #include "base/functional/bind.h" #include "base/json/json_reader.h" -#include "brave/browser/brave_rewards/rewards_util.h" #include "brave/components/brave_component_updater/browser/brave_component_installer.h" #include "brave/components/brave_component_updater/browser/brave_on_demand_updater.h" #include "brave/components/brave_extension/grit/brave_extension.h" -#include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/brave_webtorrent/grit/brave_webtorrent_resources.h" #include "brave/components/constants/brave_switches.h" #include "brave/components/constants/pref_names.h" @@ -53,11 +51,6 @@ BraveComponentLoader::BraveComponentLoader(ExtensionSystem* extension_system, kWebDiscoveryEnabled, base::BindRepeating(&BraveComponentLoader::UpdateBraveExtension, base::Unretained(this))); - - pref_change_registrar_.Add( - brave_rewards::prefs::kEnabled, - base::BindRepeating(&BraveComponentLoader::UpdateBraveExtension, - base::Unretained(this))); } BraveComponentLoader::~BraveComponentLoader() = default; @@ -155,8 +148,7 @@ void BraveComponentLoader::AddWebTorrentExtension() { bool BraveComponentLoader::UseBraveExtensionBackgroundPage() { // Keep sync with `pref_change_registrar_` in the ctor. - return profile_prefs_->GetBoolean(brave_rewards::prefs::kEnabled) || - profile_prefs_->GetBoolean(kWebDiscoveryEnabled); + return profile_prefs_->GetBoolean(kWebDiscoveryEnabled); } void BraveComponentLoader::UpdateBraveExtension() { diff --git a/browser/greaselion/BUILD.gn b/browser/greaselion/BUILD.gn index b3e0e121799b..9876244425ba 100644 --- a/browser/greaselion/BUILD.gn +++ b/browser/greaselion/BUILD.gn @@ -16,7 +16,6 @@ source_set("greaselion") { deps = [ "//base", "//brave/browser:browser_process", - "//brave/components/brave_rewards/common", "//brave/components/greaselion/browser", "//chrome/browser/extensions", "//chrome/browser/profiles", diff --git a/browser/greaselion/greaselion_browsertest.cc b/browser/greaselion/greaselion_browsertest.cc deleted file mode 100644 index a41f10532336..000000000000 --- a/browser/greaselion/greaselion_browsertest.cc +++ /dev/null @@ -1,621 +0,0 @@ -/* 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 http://mozilla.org/MPL/2.0/. */ - -#include "base/containers/flat_map.h" -#include "base/files/file_enumerator.h" -#include "base/memory/raw_ptr.h" -#include "base/path_service.h" -#include "base/run_loop.h" -#include "base/scoped_observation.h" -#include "base/task/thread_pool.h" -#include "base/test/bind.h" -#include "base/test/thread_test_helper.h" -#include "brave/browser/brave_browser_process.h" -#include "brave/browser/brave_rewards/rewards_service_factory.h" -#include "brave/browser/extensions/brave_base_local_data_files_browsertest.h" -#include "brave/browser/greaselion/greaselion_service_factory.h" -#include "brave/components/brave_component_updater/browser/local_data_files_service.h" -#include "brave/components/brave_rewards/browser/test/common/rewards_browsertest_network_util.h" -#include "brave/components/brave_rewards/browser/test/common/rewards_browsertest_response.h" -#include "brave/components/brave_rewards/browser/test/common/rewards_browsertest_util.h" -#include "brave/components/brave_rewards/common/pref_names.h" -#include "brave/components/constants/brave_paths.h" -#include "brave/components/greaselion/browser/greaselion_download_service.h" -#include "brave/components/greaselion/browser/greaselion_service.h" -#include "chrome/browser/extensions/extension_browsertest.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/test/base/ui_test_utils.h" -#include "components/prefs/pref_service.h" -#include "content/public/test/browser_test.h" -#include "content/public/test/browser_test_utils.h" -#include "extensions/common/file_util.h" -#include "ui/base/ui_base_switches.h" - -using brave_rewards::RewardsService; -using brave_rewards::RewardsServiceFactory; -using brave_rewards::RewardsServiceImpl; -using brave_rewards::test_util::RewardsBrowserTestResponse; -using extensions::ExtensionBrowserTest; -using greaselion::GreaselionDownloadService; -using greaselion::GreaselionService; -using greaselion::GreaselionServiceFactory; - -constexpr char kTestDataDirectory[] = "greaselion-data"; -constexpr char kEmbeddedTestServerDirectory[] = "greaselion"; - -constexpr char kWaitForTitleChangeScript[] = R"( - new Promise((resolve) => { - if (document.title !== 'OK') { - resolve(document.title) - } else { - new MutationObserver(function(mutations) { - resolve(mutations[0].target.text) - }).observe( - document.querySelector('title'), - { subtree: true, characterData: true, childList: true } - ); - } - }) -)"; - -class GreaselionDownloadServiceWaiter - : public GreaselionDownloadService::Observer { - public: - explicit GreaselionDownloadServiceWaiter( - GreaselionDownloadService* download_service) - : download_service_(download_service) { - scoped_observer_.Observe(download_service_); - } - GreaselionDownloadServiceWaiter(const GreaselionDownloadServiceWaiter&) = - delete; - GreaselionDownloadServiceWaiter& operator=( - const GreaselionDownloadServiceWaiter&) = delete; - ~GreaselionDownloadServiceWaiter() override = default; - - void Wait() { run_loop_.Run(); } - - private: - // GreaselionDownloadService::Observer: - void OnRulesReady(GreaselionDownloadService* download_service) override { - run_loop_.QuitWhenIdle(); - } - - const raw_ptr download_service_; - base::RunLoop run_loop_; - base::ScopedObservation - scoped_observer_{this}; -}; - -class GreaselionServiceWaiter : public GreaselionService::Observer { - public: - explicit GreaselionServiceWaiter(GreaselionService* greaselion_service) - : greaselion_service_(greaselion_service) { - scoped_observer_.Observe(greaselion_service_); - } - GreaselionServiceWaiter(const GreaselionServiceWaiter&) = delete; - GreaselionServiceWaiter& operator=(const GreaselionServiceWaiter&) = delete; - ~GreaselionServiceWaiter() override = default; - - void Wait() { - if (greaselion_service_->update_in_progress()) { - run_loop_.Run(); - } - } - - private: - // GreaselionService::Observer: - void OnExtensionsReady(GreaselionService* greaselion_service, - bool success) override { - ASSERT_TRUE(success); - run_loop_.QuitWhenIdle(); - } - - const raw_ptr greaselion_service_; - base::RunLoop run_loop_; - base::ScopedObservation - scoped_observer_{this}; -}; - -class GreaselionServiceTest : public BaseLocalDataFilesBrowserTest { - public: - GreaselionServiceTest(): https_server_(net::EmbeddedTestServer::TYPE_HTTPS) { - response_ = std::make_unique(); - } - - void SetUpOnMainThread() override { - BaseLocalDataFilesBrowserTest::SetUpOnMainThread(); - base::ScopedAllowBlockingForTesting allow_blocking; - response_->LoadMocks(); - profile()->GetPrefs()->SetBoolean(brave_rewards::prefs::kEnabled, true); - } - - // BaseLocalDataFilesBrowserTest overrides - const char* test_data_directory() override { return kTestDataDirectory; } - const char* embedded_test_server_directory() override { - return kEmbeddedTestServerDirectory; - } - LocalDataFilesObserver* service() override { - return g_brave_browser_process->greaselion_download_service(); - } - - void WaitForService() override { - // wait for Greaselion download service to load and parse its - // configuration file - greaselion::GreaselionDownloadService* download_service = - g_brave_browser_process->greaselion_download_service(); - GreaselionDownloadServiceWaiter(download_service).Wait(); - GreaselionService* greaselion_service = - GreaselionServiceFactory::GetForBrowserContext(profile()); - // Give a consistent browser version for testing. - static const base::NoDestructor version("1.2.3.4"); - greaselion_service->SetBrowserVersionForTesting(*version); - // wait for the Greaselion service to install all the extensions it creates - GreaselionServiceWaiter(greaselion_service).Wait(); - } - - int GetRulesSize() { - return g_brave_browser_process->greaselion_download_service() - ->rules() - ->size(); - } - - void ClearRules() { - g_brave_browser_process->greaselion_download_service()->rules()->clear(); - } - - void StartRewards() { - // HTTP resolver - https_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK); - https_server_.RegisterRequestHandler( - base::BindRepeating(&brave_rewards::test_util::HandleRequest)); - ASSERT_TRUE(https_server_.Start()); - - // Rewards service - rewards_service_ = static_cast( - RewardsServiceFactory::GetForProfile(profile())); - brave_rewards::test_util::StartProcess(rewards_service_); - - // Response mock - rewards_service_->ForTestingSetTestResponseCallback( - base::BindRepeating( - &GreaselionServiceTest::GetTestResponse, - base::Unretained(this))); - rewards_service_->SetEngineEnvForTesting(); - GreaselionService* greaselion_service = - GreaselionServiceFactory::GetForBrowserContext(profile()); - // wait for the Greaselion service to install all the extensions it creates - // after the rewards service is turned off or on - GreaselionServiceWaiter(greaselion_service).Wait(); - } - - void WaitForAutoContributeEnabled() { - auto* prefs = browser()->profile()->GetPrefs(); - if (prefs->GetBoolean(brave_rewards::prefs::kAutoContributeEnabled)) { - return; - } - - base::RunLoop run_loop; - PrefChangeRegistrar pref_change_registrar; - pref_change_registrar.Init(prefs); - pref_change_registrar.Add( - brave_rewards::prefs::kAutoContributeEnabled, - base::BindLambdaForTesting([&run_loop, &prefs] { - if (prefs->GetBoolean(brave_rewards::prefs::kAutoContributeEnabled)) { - run_loop.Quit(); - } - })); - run_loop.Run(); - } - - void GetTestResponse( - const std::string& url, - int32_t method, - int* response_status_code, - std::string* response, - base::flat_map* headers) { - response_->Get( - url, - method, - response_status_code, - response); - } - - std::unique_ptr response_; - net::test_server::EmbeddedTestServer https_server_; - raw_ptr rewards_service_ = nullptr; -}; - -#if !BUILDFLAG(IS_MAC) -class GreaselionServiceLocaleTest : public GreaselionServiceTest { - public: - explicit GreaselionServiceLocaleTest(const std::string& locale) - : locale_(locale) {} - - void SetUpCommandLine(base::CommandLine* command_line) override { - ExtensionBrowserTest::SetUpCommandLine(command_line); - command_line->AppendSwitchASCII(switches::kLang, locale_); - } - - private: - std::string locale_; -}; - -class GreaselionServiceLocaleTestEnglish : public GreaselionServiceLocaleTest { - public: - GreaselionServiceLocaleTestEnglish() : GreaselionServiceLocaleTest("en") {} -}; - -class GreaselionServiceLocaleTestGerman : public GreaselionServiceLocaleTest { - public: - GreaselionServiceLocaleTestGerman() : GreaselionServiceLocaleTest("de") {} -}; - -class GreaselionServiceLocaleTestFrench : public GreaselionServiceLocaleTest { - public: - GreaselionServiceLocaleTestFrench() : GreaselionServiceLocaleTest("fr") {} -}; -#endif - -// Ensure the site specific script service properly clears its cache of -// precompiled URLPatterns if initialized twice. (This can happen if -// the parent component is updated while Brave is running.) -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, ClearCache) { - ASSERT_TRUE(InstallMockExtension()); - int size = GetRulesSize(); - // clear the cache manually to make sure we're actually - // reinitializing it the second time - ClearRules(); - ASSERT_TRUE(InstallMockExtension()); - EXPECT_EQ(size, GetRulesSize()); - // now reinitialize without manually clearing (simulates an in-place - // component update) - ASSERT_TRUE(InstallMockExtension()); - EXPECT_EQ(size, GetRulesSize()); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, ScriptInjection) { - ASSERT_TRUE(InstallMockExtension()); - GURL url = embedded_test_server()->GetURL("www.a.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, ScriptInjectionDocumentStart) { - ASSERT_TRUE(InstallMockExtension()); - GURL url = embedded_test_server()->GetURL("runat1.b.com", "/intercept.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - EXPECT_EQ(content::EvalJs(contents, "document.title;"), "SCRIPT_FIRST"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, ScriptInjectionDocumentEnd) { - ASSERT_TRUE(InstallMockExtension()); - GURL url = embedded_test_server()->GetURL("runat2.b.com", "/intercept.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - EXPECT_EQ(content::EvalJs(contents, "document.title;"), "PAGE_FIRST"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, ScriptInjectionRunAtDefault) { - ASSERT_TRUE(InstallMockExtension()); - GURL url = embedded_test_server()->GetURL("runat3.b.com", "/intercept.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - EXPECT_EQ(content::EvalJs(contents, "document.title;"), "PAGE_FIRST"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - PRE_ScriptInjectionWithPrecondition) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL("pre1.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // should be unaltered because precondition did not match, so no Greaselion - // rules are active - EXPECT_EQ(content::EvalJs(contents, "document.title;"), "OK"); - - StartRewards(); - - auto* prefs = browser()->profile()->GetPrefs(); - EXPECT_FALSE(prefs->GetBoolean(brave_rewards::prefs::kAutoContributeEnabled)); - - // Enable auto-contribute and wait for it - rewards_service_->SetAutoContributeEnabled(true); - WaitForAutoContributeEnabled(); - - ASSERT_TRUE(prefs->GetBoolean(brave_rewards::prefs::kAutoContributeEnabled)); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, ScriptInjectionWithPrecondition) { - ASSERT_TRUE(InstallMockExtension()); - - StartRewards(); - - // Auto-contribute should still be enabled, due to PRE test - auto* prefs = browser()->profile()->GetPrefs(); - ASSERT_TRUE(prefs->GetBoolean(brave_rewards::prefs::kAutoContributeEnabled)); - - GURL url = embedded_test_server()->GetURL("pre1.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // should be altered because rewards precondition matched, so the relevant - // Greaselion rule is active - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, IsGreaselionExtension) { - ASSERT_TRUE(InstallMockExtension()); - - GreaselionService* greaselion_service = - GreaselionServiceFactory::GetForBrowserContext(profile()); - ASSERT_TRUE(greaselion_service); - - auto extension_ids = greaselion_service->GetExtensionIdsForTesting(); - ASSERT_GT(extension_ids.size(), 0UL); - - EXPECT_TRUE(greaselion_service->IsGreaselionExtension(extension_ids[0])); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, IsNotGreaselionExtension) { - ASSERT_TRUE(InstallMockExtension()); - - GreaselionService* greaselion_service = - GreaselionServiceFactory::GetForBrowserContext(profile()); - ASSERT_TRUE(greaselion_service); - - EXPECT_FALSE(greaselion_service->IsGreaselionExtension("INVALID")); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionLowWild) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-low-wild.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be altered because version is lower than current. - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionLowFormat) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-low-format.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be altered because version is lower than current, even though it - // omits last component. - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionMatchWild) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-match-wild.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be altered because version is wild match. - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionMatchExact) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-match-exact.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be altered because version is exact match. - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionHighWild) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-high-wild.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be unaltered because version is too high. - EXPECT_EQ(content::EvalJs(contents, "document.title"), "OK"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionHighExact) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-high-exact.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be unaltered because version is too high. - EXPECT_EQ(content::EvalJs(contents, "document.title"), "OK"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionEmpty) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-empty.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be altered because version is not good format. - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, - ScriptInjectionWithBrowserVersionConditionBadFormat) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL( - "version-bad-format.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - // Should be altered because version is not good format. - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, CleanShutdown) { - ASSERT_TRUE(InstallMockExtension()); - - GURL url = embedded_test_server()->GetURL("www.a.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - EXPECT_EQ(url, contents->GetURL()); - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), "Altered"); - - CloseAllBrowsers(); - ui_test_utils::WaitForBrowserToClose(browser()); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceTest, FoldersAreRemovedOnUpdate) { - ASSERT_TRUE(InstallMockExtension()); - - auto io_runner = base::ThreadPool::CreateSequencedTaskRunner( - {base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN}); - - auto count_folders_on_io_runner = [&io_runner] { - base::RunLoop run_loop; - size_t folder_count; - - auto set_folder_count = [&folder_count, &run_loop](size_t count) { - folder_count = count; - run_loop.Quit(); - }; - - auto count_folders = []() { - base::FilePath install_dir = - GreaselionServiceFactory::GetInstallDirectory(); - - base::FilePath extensions_dir = - extensions::file_util::GetInstallTempDir(install_dir); - - base::FileEnumerator enumerator(extensions_dir, false, - base::FileEnumerator::DIRECTORIES); - size_t count = 0; - for (base::FilePath name = enumerator.Next(); !name.empty(); - name = enumerator.Next()) { - ++count; - } - return count; - }; - - io_runner->PostTaskAndReplyWithResult( - FROM_HERE, base::BindOnce(count_folders), - base::BindLambdaForTesting(set_folder_count)); - - run_loop.Run(); - return folder_count; - }; - - size_t start_count = count_folders_on_io_runner(); - EXPECT_GT(start_count, 0ul); - - // Trigger an update to reinstall extension folders and wait for all - // extensions to finish loading. - GreaselionService* greaselion_service = - GreaselionServiceFactory::GetForBrowserContext(profile()); - ASSERT_TRUE(greaselion_service); - greaselion_service->UpdateInstalledExtensions(); - GreaselionServiceWaiter(greaselion_service).Wait(); - - size_t after_update = count_folders_on_io_runner(); - EXPECT_EQ(after_update, start_count); -} - -#if !BUILDFLAG(IS_MAC) -IN_PROC_BROWSER_TEST_F(GreaselionServiceLocaleTestEnglish, - ScriptInjectionWithMessagesDefaultLocale) { - ASSERT_TRUE(InstallMockExtension()); - - const GURL url = - embedded_test_server()->GetURL("messages.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - - EXPECT_EQ(url, contents->GetURL()); - - // Ensure that English localization is correct - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), - "Hello, world!"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceLocaleTestGerman, - ScriptInjectionWithMessagesNonDefaultLocale) { - ASSERT_TRUE(InstallMockExtension()); - - const GURL url = - embedded_test_server()->GetURL("messages.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - - EXPECT_EQ(url, contents->GetURL()); - - // Ensure that German localization is correct - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), - "Hallo, Welt!"); -} - -IN_PROC_BROWSER_TEST_F(GreaselionServiceLocaleTestFrench, - ScriptInjectionWithMessagesUnsupportedLocale) { - ASSERT_TRUE(InstallMockExtension()); - - const GURL url = - embedded_test_server()->GetURL("messages.example.com", "/simple.html"); - ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); - content::WebContents* contents = - browser()->tab_strip_model()->GetActiveWebContents(); - - EXPECT_EQ(url, contents->GetURL()); - - // We don't have a French localization, so ensure that the default - // (English) localization is shown instead - EXPECT_EQ(content::EvalJs(contents, kWaitForTitleChangeScript), - "Hello, world!"); -} -#endif diff --git a/browser/greaselion/greaselion_service_factory.cc b/browser/greaselion/greaselion_service_factory.cc index 491634ebf4da..852a577e4f12 100644 --- a/browser/greaselion/greaselion_service_factory.cc +++ b/browser/greaselion/greaselion_service_factory.cc @@ -12,7 +12,6 @@ #include "base/no_destructor.h" #include "base/path_service.h" #include "brave/browser/brave_browser_process.h" -#include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/greaselion/browser/greaselion_service.h" #include "brave/components/greaselion/browser/greaselion_service_impl.h" #include "chrome/browser/extensions/extension_service.h" @@ -38,20 +37,9 @@ class GreaselionServiceDelegateImpl content::BrowserContext* browser_context) : browser_context_(browser_context) { DCHECK(browser_context_); - pref_change_registrar_.Init( - Profile::FromBrowserContext(browser_context)->GetPrefs()); - pref_change_registrar_.Add( - brave_rewards::prefs::kEnabled, - base::BindRepeating( - &GreaselionServiceDelegateImpl::UpdateGreaselionExtensions, - base::Unretained(this))); } - bool IsEnabled() const override { - return Profile::FromBrowserContext(browser_context_) - ->GetPrefs() - ->GetBoolean(brave_rewards::prefs::kEnabled); - } + bool IsEnabled() const override { return false; } void AddExtension(extensions::Extension* extension) override { if (auto* extension_service = @@ -80,7 +68,6 @@ class GreaselionServiceDelegateImpl } raw_ptr browser_context_; // Not owned - PrefChangeRegistrar pref_change_registrar_; }; } // namespace diff --git a/common/extensions/api/brave_rewards.json b/common/extensions/api/brave_rewards.json index 3836f069fa46..37fb9255f0f0 100644 --- a/common/extensions/api/brave_rewards.json +++ b/common/extensions/api/brave_rewards.json @@ -244,29 +244,6 @@ "description": "Opens the Rewards panel in the active window.", "parameters": [] }, - { - "name": "updateMediaDuration", - "type": "function", - "description": "Update the media duration for a given resource", - "parameters": [ - { - "name": "windowId", - "type": "number" - }, - { - "name": "publisherKey", - "type": "string" - }, - { - "name": "duration", - "type": "integer" - }, - { - "name": "firstVisit", - "type": "boolean" - } - ] - }, { "name": "getPublisherInfo", "type": "function", @@ -319,21 +296,6 @@ "does_not_support_promises": "Multi-parameter callback" } }, - { - "name": "setPublisherIdForTab", - "type": "function", - "description": "Sets the current publisher ID for the specified tab", - "parameters": [ - { - "name": "tabId", - "type": "integer" - }, - { - "name": "publisherId", - "type": "string" - } - ] - }, { "name": "getPublisherInfoForTab", "type": "function", @@ -390,107 +352,6 @@ }, "does_not_support_promises": "Requires refactoring" }, - { - "name": "getPublisherPanelInfo", - "type": "function", - "description": "Get the panel info for a particular publisher", - "parameters": [ - { - "name": "publisherKey", - "type": "string" - } - ], - "returns_async": { - "name": "callback", - "parameters": [ - { - "name": "result", - "type": "integer" - }, - { - "name": "publisher", - "type": "object", - "optional": true, - "properties": { - "percentage": { - "type": "integer", - "description": "publisher attention score" - }, - "excluded": { - "type": "boolean", - "description": "is site excluded from auto contribute" - }, - "provider": { - "type": "string", - "description": "provider (if media publisher) for this publisher" - }, - "favIconUrl": { - "type": "string", - "description": "publisher image url" - }, - "publisherKey": { - "type": "string", - "description": "publisher key, unique identifier" - }, - "name": { - "type": "string", - "description": "publisher name" - }, - "url": { - "type": "string", - "description": "url of the current tab" - }, - "status": { - "type": "integer", - "description": "publisher status" - } - } - } - ], - "does_not_support_promises": "Multi-parameter callback" - } - }, - { - "name": "savePublisherInfo", - "type": "function", - "description": "Save the publisher info when visiting a site", - "parameters": [ - { - "name": "windowId", - "type": "number" - }, - { - "name": "mediaType", - "type": "string" - }, - { - "name": "url", - "type": "string" - }, - { - "name": "publisherKey", - "type": "string" - }, - { - "name": "publisherName", - "type": "string" - }, - { - "name": "favIconUrl", - "type": "string" - } - ], - "returns_async": { - "name": "callback", - "parameters": [ - { - "name": "result", - "type": "integer" - } - ] - }, - "does_not_support_promises": "Requires refactoring" - }, { "name": "tipSite", "type": "function", @@ -510,29 +371,6 @@ } ] }, - { - "name": "getPublisherData", - "type": "function", - "description": "Get publisher data", - "parameters": [ - { - "name": "windowId", - "type": "integer" - }, - { - "name": "url", - "type": "string" - }, - { - "name": "faviconUrl", - "type": "string" - }, - { - "name": "publisherBlob", - "type": "string" - } - ] - }, { "name": "getRewardsParameters", "type": "function", diff --git a/components/brave_extension/extension/brave_extension/background.ts b/components/brave_extension/extension/brave_extension/background.ts index 5410bfb7b71a..3d2fa7a9e03d 100644 --- a/components/brave_extension/extension/brave_extension/background.ts +++ b/components/brave_extension/extension/brave_extension/background.ts @@ -3,7 +3,6 @@ // 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/. -import './background/greaselion' import './background/webDiscoveryProject' if (chrome.test) { diff --git a/components/brave_extension/extension/brave_extension/background/greaselion.ts b/components/brave_extension/extension/brave_extension/background/greaselion.ts deleted file mode 100644 index 3ebe3f030c8e..000000000000 --- a/components/brave_extension/extension/brave_extension/background/greaselion.ts +++ /dev/null @@ -1,452 +0,0 @@ -/* 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/. */ - -interface GreaselionError { - errorMessage: string -} - -interface OnAPIRequest { - name: string - url: string - init: {} -} - -interface MediaDurationMetadata { - mediaKey: string - duration: number - firstVisit: boolean -} - -interface RegisterOnCompletedWebRequest { - urlPatterns: string[] -} - -interface RegisterOnSendHeadersWebRequest { - urlPatterns: string[] - extra?: string[] -} - -interface SavePublisherVisit { - url: string - publisherKey: string - publisherName: string - mediaKey?: string - favIconUrl?: string -} - -interface ConnectionState { - port: chrome.runtime.Port - onCompletedWebRequestListener?: ( - details: chrome.webRequest.WebResponseCacheDetails - ) => void - onSendHeadersWebRequestListener?: ( - details: chrome.webRequest.WebRequestHeadersDetails - ) => void - onUpdatedTabListener?: ( - tabId: number, - changeInfo: chrome.tabs.TabChangeInfo, - tab: chrome.tabs.Tab - ) => void -} - -// Maps Greaselion connection state by tabId:senderId -const connectionsByTabIdSenderId = new Map() - -// Maps publisher keys by media key -const publisherKeysByMediaKey = new Map() - -// Maps publisher keys by tabId -const publisherKeysByTabId = new Map() - -const braveRewardsExtensionId = 'jidkidbbcafjabdphckchenhfomhnfma' - -const buildTabIdSenderIdKey = (tabId: number, senderId: string) => { - if (!tabId || !senderId) { - return '' - } - - return `${tabId}:${senderId}` -} - -const handleGreaselionError = (tabId: number, mediaType: string, data: GreaselionError) => { - console.error(`Greaselion error: ${data.errorMessage}`) -} - -const handleOnAPIRequest = (data: OnAPIRequest, onSuccess: (response: any) => void, onFailure: (error: any) => void) => { - if (!data || !data.url || !data.init || !onSuccess || !onFailure) { - return - } - - fetch(data.url, data.init) - .then(response => { - if (!response.ok) { - throw new Error(`API request failed: ${response.statusText} (${response.status})`) - } - - return response.json() - }) - .then(responseData => onSuccess(responseData)) - .catch(error => onFailure(error)) -} - -const handleMediaDurationMetadata = (tabId: number, mediaType: string, data: MediaDurationMetadata) => { - const publisherKey = publisherKeysByMediaKey.get(data.mediaKey) - if (!publisherKey) { - console.error(`Failed to handle media duration metadata: missing publisher key for media key ${data.mediaKey}`) - return - } - - chrome.braveRewards.updateMediaDuration(tabId, publisherKey, data.duration, data.firstVisit) -} - -const onCompletedWebRequest = ( - registrationKey: string, - mediaType: string, - details: chrome.webRequest.WebResponseCacheDetails -) => { - const connectionState = connectionsByTabIdSenderId.get(registrationKey) - if (!connectionState || !connectionState.port) { - return - } - connectionState.port.postMessage({ - type: 'OnCompletedWebRequest', - mediaType, - details - }) -} - -const handleRegisterOnCompletedWebRequest = ( - registrationKey: string, - mediaType: string, - data: RegisterOnCompletedWebRequest -) => { - const connectionState = connectionsByTabIdSenderId.get(registrationKey) - if (!connectionState || connectionState.onCompletedWebRequestListener) { - return - } - - // Create and install the listener - const listener = onCompletedWebRequest.bind(null, registrationKey, mediaType) - chrome.webRequest.onCompleted.addListener( - // Listener - listener, - // Filters - { - types: [ - 'image', - 'media', - 'script', - 'xmlhttprequest' - ], - urls: data.urlPatterns - }) - - connectionState.onCompletedWebRequestListener = listener -} - -const onSendHeadersWebRequest = ( - registrationKey: string, - mediaType: string, - details: chrome.webRequest.WebRequestHeadersDetails -) => { - const connectionState = connectionsByTabIdSenderId.get(registrationKey) - if (!connectionState || !connectionState.port) { - return - } - connectionState.port.postMessage({ - type: 'OnSendHeadersWebRequest', - mediaType, - data: { - details - } - }) -} - -const handleRegisterOnSendHeadersWebRequest = (registrationKey: string, mediaType: string, data: RegisterOnSendHeadersWebRequest) => { - const connectionState = connectionsByTabIdSenderId.get(registrationKey) - if (!connectionState || connectionState.onSendHeadersWebRequestListener) { - return - } - - // Create and install the listener - const listener = - onSendHeadersWebRequest.bind(null, registrationKey, mediaType) - chrome.webRequest.onSendHeaders.addListener( - // Listener - listener, - // Filters - { - urls: data.urlPatterns - }, - // Extra - data.extra - ) - - connectionState.onSendHeadersWebRequestListener = listener -} - -const onUpdatedTab = ( - registrationKey: string, - mediaType: string, - tabId: number, - changeInfo: chrome.tabs.TabChangeInfo, - tab: chrome.tabs.Tab -) => { - const connectionState = connectionsByTabIdSenderId.get(registrationKey) - if (!connectionState || !connectionState.port) { - return - } - connectionState.port.postMessage({ - type: 'OnUpdatedTab', - mediaType, - data: { - tabId, - changeInfo, - tab - } - }) -} - -const handleRegisterOnUpdatedTab = (registrationKey: string, mediaType: string) => { - const connectionState = connectionsByTabIdSenderId.get(registrationKey) - if (!connectionState || connectionState.onUpdatedTabListener) { - return - } - - // Create and install the listener - const listener = onUpdatedTab.bind(null, registrationKey, mediaType) - chrome.tabs.onUpdated.addListener(listener) - - connectionState.onUpdatedTabListener = listener -} - -const getPublisherPanelInfo = (tabId: number, publisherKey: string) => { - chrome.braveRewards.getPublisherPanelInfo( - publisherKey, (result: RewardsExtension.Result, info?: RewardsExtension.Publisher) => { - if (result === 0 && info) { - chrome.runtime.sendMessage( - braveRewardsExtensionId, - { - type: 'OnPublisherData', - tabId, - info - }) - } - }) -} - -const getPublisherPanelInfoByTabId = (tabId: number) => { - if (!tabId) { - return - } - - const publisherKey = publisherKeysByTabId.get(tabId) - if (!publisherKey) { - return - } - - getPublisherPanelInfo(tabId, publisherKey) -} - -const savePublisherInfo = (tabId: number, mediaType: string, url: string, publisherKey: string, publisherName: string, favIconUrl: string) => { - chrome.braveRewards.savePublisherInfo( - tabId, - mediaType, - url, - publisherKey, - publisherName, - favIconUrl, - (result: RewardsExtension.Result) => { - if (result !== 0) { - console.error(`Failed to save publisher info for ${publisherKey}, result is ${result}`) - } - }) -} - -const handleSavePublisherVisit = (tabId: number, mediaType: string, data: SavePublisherVisit) => { - if (!data.publisherKey || !data.publisherName) { - console.error('Invalid parameter') - return - } - - publisherKeysByTabId.set(tabId, data.publisherKey) - - chrome.braveRewards.setPublisherIdForTab(tabId, data.publisherKey) - - if (data.mediaKey && !publisherKeysByMediaKey.has(data.mediaKey)) { - publisherKeysByMediaKey.set(data.mediaKey, data.publisherKey) - } - - chrome.braveRewards.getPublisherInfo( - data.publisherKey, (result: RewardsExtension.Result, info?: RewardsExtension.Publisher) => { - let shouldUpdate = false - if (result === 0 && info) { - shouldUpdate = (info.name !== data.publisherName || info.url !== data.url) - if (!shouldUpdate) { - getPublisherPanelInfo(tabId, data.publisherKey) - return - } - } - - // Failed to find the publisher info corresponding to this key or the - // publisher info needs to be updated, so save the info now - if (result === 9 || shouldUpdate) { - savePublisherInfo( - tabId, - mediaType, - data.url, - data.publisherKey, - data.publisherName, - data.favIconUrl || '') - } - }) -} - -const onMessageListener = (msg: any, port: chrome.runtime.Port) => { - if (!port || !port.sender || !port.sender.id || !port.sender.tab || !msg) { - return - } - - const tabId = port.sender.tab.id - if (!tabId) { - return - } - - const senderId = port.sender.id - if (!senderId) { - return - } - - const key = buildTabIdSenderIdKey(tabId, senderId) - if (!connectionsByTabIdSenderId.has(key)) { - const connectionState = { - port - } - connectionsByTabIdSenderId.set(key, connectionState) - } - - chrome.greaselion.isGreaselionExtension(port.sender.id, (valid: boolean) => { - if (!valid) { - return - } - switch (msg.type) { - case 'GreaselionError': { - const data = msg.data as GreaselionError - handleGreaselionError(tabId, msg.mediaType, data) - break - } - case 'MediaDurationMetadata': { - const data = msg.data as MediaDurationMetadata - handleMediaDurationMetadata(tabId, msg.mediaType, data) - break - } - case 'OnAPIRequest': { - const data = msg.data as OnAPIRequest - handleOnAPIRequest( - data, - (response: any) => { - if (connectionsByTabIdSenderId.has(key)) { - port.postMessage({ - type: 'OnAPIResponse', - mediaType: msg.mediaType, - data: { - name: data.name, - response - } - }) - } - }, - (error: any) => { - if (connectionsByTabIdSenderId.has(key)) { - port.postMessage({ - type: 'OnAPIResponse', - mediaType: msg.mediaType, - data: { - name: data.name, - error - } - }) - } - }) - break - } - case 'RegisterOnCompletedWebRequest': { - const data = msg.data as RegisterOnCompletedWebRequest - handleRegisterOnCompletedWebRequest(key, msg.mediaType, data) - break - } - case 'RegisterOnSendHeadersWebRequest': { - const data = msg.data as RegisterOnSendHeadersWebRequest - handleRegisterOnSendHeadersWebRequest(key, msg.mediaType, data) - break - } - case 'RegisterOnUpdatedTab': { - handleRegisterOnUpdatedTab(key, msg.mediaType) - break - } - case 'SavePublisherVisit': { - const data = msg.data as SavePublisherVisit - handleSavePublisherVisit(tabId, msg.mediaType, data) - break - } - } - }) -} - -chrome.runtime.onConnectExternal.addListener((port: chrome.runtime.Port) => { - if (!port || port.name !== 'Greaselion') { - return - } - - port.onMessage.addListener(onMessageListener) - - port.onDisconnect.addListener((port: chrome.runtime.Port) => { - if (chrome.runtime.lastError) { - console.error(`Greaselion port disconnected due to error: ${chrome.runtime.lastError}`) - } - if (port.sender && port.sender.id && port.sender.tab && port.sender.tab.id) { - const key = buildTabIdSenderIdKey(port.sender.tab.id, port.sender.id) - const connectionState = connectionsByTabIdSenderId.get(key) - if (connectionState) { - if (connectionState.onCompletedWebRequestListener) { - chrome.webRequest.onCompleted.removeListener( - connectionState.onCompletedWebRequestListener) - } - if (connectionState.onSendHeadersWebRequestListener) { - chrome.webRequest.onSendHeaders.removeListener( - connectionState.onSendHeadersWebRequestListener) - } - if (connectionState.onUpdatedTabListener) { - chrome.tabs.onUpdated.removeListener( - connectionState.onUpdatedTabListener) - } - } - - connectionsByTabIdSenderId.delete(key) - publisherKeysByTabId.delete(port.sender.tab.id) - - port.onMessage.removeListener(onMessageListener) - } - }) -}) - -chrome.runtime.onMessageExternal.addListener( - function (msg: any, sender: chrome.runtime.MessageSender, sendResponse: any) { - if (!msg || !msg.type || !sender || !sender.id) { - return - } - chrome.greaselion.isGreaselionExtension(sender.id, (valid: boolean) => { - if (!valid && sender.id !== braveRewardsExtensionId) { - return - } - switch (msg.type) { - case 'GetPublisherPanelInfo': - getPublisherPanelInfoByTabId(msg.tabId) - break - case 'SupportsGreaselion': - sendResponse({ supported: true }) - break - } - }) - }) diff --git a/components/brave_rewards/browser/BUILD.gn b/components/brave_rewards/browser/BUILD.gn index 7de87871a8d1..4185ed4eb8af 100644 --- a/components/brave_rewards/browser/BUILD.gn +++ b/components/brave_rewards/browser/BUILD.gn @@ -4,7 +4,6 @@ # You can obtain one at https://mozilla.org/MPL/2.0/. import("//brave/components/brave_rewards/core/config.gni") -import("//brave/components/greaselion/browser/buildflags/buildflags.gni") import("//extensions/buildflags/buildflags.gni") static_library("browser") { @@ -14,8 +13,6 @@ static_library("browser") { "diagnostic_log.cc", "diagnostic_log.h", "logging.h", - "publisher_utils.cc", - "publisher_utils.h", "rewards_notification_service.cc", "rewards_notification_service.h", "rewards_notification_service_impl.cc", @@ -44,7 +41,6 @@ static_library("browser") { "//brave/components/brave_wallet/browser", "//brave/components/brave_wallet/common", "//brave/components/brave_wallet/common:mojom", - "//brave/components/greaselion/browser/buildflags", "//brave/components/ntp_background_images/common", "//brave/components/p3a_utils", "//brave/components/resources", @@ -73,10 +69,6 @@ static_library("browser") { if (is_android) { deps += [ "//brave/components/safetynet:android" ] } - - if (enable_greaselion) { - deps += [ "//brave/components/greaselion/browser" ] - } } source_set("testutil") { diff --git a/components/brave_rewards/browser/publisher_utils_unittest.cc b/components/brave_rewards/browser/publisher_utils_unittest.cc deleted file mode 100644 index db329b670fc9..000000000000 --- a/components/brave_rewards/browser/publisher_utils_unittest.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */ - -#include "brave/components/brave_rewards/browser/publisher_utils.h" - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace brave_rewards { - -class RewardsPublisherUtilsTest : public testing::Test { - protected: - std::optional GetPublisherId(const std::string& url) { - return GetPublisherIdFromURL(GURL(url)); - } -}; - -TEST_F(RewardsPublisherUtilsTest, GetPublisherIdFromURL) { - EXPECT_EQ(GetPublisherId("https://brave.com"), "brave.com"); - EXPECT_EQ(GetPublisherId("http://brave.com"), "brave.com"); - EXPECT_EQ(GetPublisherId("https://search.brave.com"), "brave.com"); - EXPECT_EQ(GetPublisherId("http://search.brave.com"), "brave.com"); - - EXPECT_EQ(GetPublisherId("https://brave.co.uk"), "brave.co.uk"); - EXPECT_EQ(GetPublisherId("https://www.brave.co.uk"), "brave.co.uk"); - - EXPECT_EQ(GetPublisherId("file:///a/b/c/"), std::nullopt); - EXPECT_EQ(GetPublisherId("invalid-url"), std::nullopt); - - EXPECT_EQ(GetPublisherId("https://twitter.com/foo"), std::nullopt); - EXPECT_EQ(GetPublisherId("https://github.com/foo"), std::nullopt); - EXPECT_EQ(GetPublisherId("https://reddit.com/foo"), std::nullopt); - EXPECT_EQ(GetPublisherId("https://youtube.com/foo"), std::nullopt); - EXPECT_EQ(GetPublisherId("https://vimeo.com/foo"), std::nullopt); - EXPECT_EQ(GetPublisherId("https://twitch.tv/foo"), std::nullopt); -} - -} // namespace brave_rewards diff --git a/components/brave_rewards/browser/rewards_service.h b/components/brave_rewards/browser/rewards_service.h index 0c64c46a874b..2f15ae8e5aeb 100644 --- a/components/brave_rewards/browser/rewards_service.h +++ b/components/brave_rewards/browser/rewards_service.h @@ -144,6 +144,7 @@ class RewardsService : public KeyedService { virtual void GetExcludedList(GetPublisherInfoListCallback callback) = 0; virtual void RestorePublishers() = 0; + virtual void OnLoad(mojom::VisitDataPtr visit_data) = 0; virtual void OnLoad(SessionID tab_id, const GURL& gurl) = 0; virtual void OnUnload(SessionID tab_id) = 0; virtual void OnShow(SessionID tab_id) = 0; @@ -171,8 +172,10 @@ class RewardsService : public KeyedService { const uint32_t month, const uint32_t year, GetBalanceReportCallback callback) = 0; + virtual void GetPublisherActivityFromVisitData( + mojom::VisitDataPtr visit_data) = 0; virtual void GetPublisherActivityFromUrl( - uint64_t windowId, + uint64_t tab_id, const std::string& url, const std::string& favicon_url, const std::string& publisher_blob) = 0; @@ -223,12 +226,6 @@ class RewardsService : public KeyedService { virtual const RewardsNotificationService::RewardsNotificationsMap& GetAllNotifications() = 0; - virtual void UpdateMediaDuration( - const uint64_t window_id, - const std::string& publisher_key, - const uint64_t duration, - const bool firstVisit) = 0; - virtual void IsPublisherRegistered( const std::string& publisher_id, base::OnceCallback callback) = 0; diff --git a/components/brave_rewards/browser/rewards_service_impl.cc b/components/brave_rewards/browser/rewards_service_impl.cc index 3ed81f9c1856..c64e387c193d 100644 --- a/components/brave_rewards/browser/rewards_service_impl.cc +++ b/components/brave_rewards/browser/rewards_service_impl.cc @@ -36,7 +36,6 @@ #include "brave/components/brave_rewards/browser/android_util.h" #include "brave/components/brave_rewards/browser/diagnostic_log.h" #include "brave/components/brave_rewards/browser/logging.h" -#include "brave/components/brave_rewards/browser/publisher_utils.h" #include "brave/components/brave_rewards/browser/rewards_notification_service.h" #include "brave/components/brave_rewards/browser/rewards_notification_service_impl.h" #include "brave/components/brave_rewards/browser/rewards_p3a.h" @@ -45,6 +44,7 @@ #include "brave/components/brave_rewards/common/buildflags/buildflags.h" #include "brave/components/brave_rewards/common/features.h" #include "brave/components/brave_rewards/common/pref_names.h" +#include "brave/components/brave_rewards/common/publisher_utils.h" #include "brave/components/brave_rewards/common/rewards_util.h" #include "brave/components/brave_rewards/core/global_constants.h" #include "brave/components/brave_rewards/core/parameters/rewards_parameters_provider.h" @@ -256,18 +256,12 @@ RewardsServiceImpl::RewardsServiceImpl( RequestImageCallback request_image_callback, CancelImageRequestCallback cancel_image_request_callback, content::StoragePartition* storage_partition, -#if BUILDFLAG(ENABLE_GREASELION) - greaselion::GreaselionService* greaselion_service, -#endif brave_wallet::BraveWalletService* brave_wallet_service) : prefs_(prefs), favicon_service_(favicon_service), request_image_callback_(request_image_callback), cancel_image_request_callback_(cancel_image_request_callback), storage_partition_(storage_partition), -#if BUILDFLAG(ENABLE_GREASELION) - greaselion_service_(greaselion_service), -#endif brave_wallet_service_(brave_wallet_service), receiver_(this), file_task_runner_(base::ThreadPool::CreateSequencedTaskRunner( @@ -290,28 +284,11 @@ RewardsServiceImpl::RewardsServiceImpl( persist_log_level_ = kDiagnosticLogMaxVerboseLevel; } -#if BUILDFLAG(ENABLE_GREASELION) - if (greaselion_service_) { - // Greaselion's rules may be ready before we register our observer, so check - // for that here - if (!greaselion_enabled_ && greaselion_service_->rules_ready()) { - OnRulesReady(greaselion_service_); - } - greaselion_service_->AddObserver(this); - } -#endif - p3a::RecordAdTypesEnabled(prefs_); } RewardsServiceImpl::~RewardsServiceImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); - -#if BUILDFLAG(ENABLE_GREASELION) - if (greaselion_service_) { - greaselion_service_->RemoveObserver(this); - } -#endif } void RewardsServiceImpl::ConnectionClosed() { @@ -388,16 +365,6 @@ void RewardsServiceImpl::OnPreferenceChanged(const std::string& key) { base::BindOnce(&RewardsServiceImpl::OnRecordBackendP3AExternalWallet, AsWeakPtr(), false, true)); } - -#if BUILDFLAG(ENABLE_GREASELION) - if (key == brave_ads::prefs::kOptedInToNotificationAds) { - if (greaselion_service_) { - greaselion_service_->SetFeatureEnabled( - greaselion::ADS, - prefs_->GetBoolean(brave_ads::prefs::kOptedInToNotificationAds)); - } - } -#endif } void RewardsServiceImpl::CheckPreferences() { @@ -684,12 +651,15 @@ void RewardsServiceImpl::OnGetPublisherInfoList( std::move(callback).Run(std::move(list)); } -void RewardsServiceImpl::OnLoad(SessionID tab_id, const GURL& url) { +void RewardsServiceImpl::OnLoad(mojom::VisitDataPtr visit_data) { if (!Connected()) { return; } + engine_->OnLoad(std::move(visit_data), GetCurrentTimestamp()); +} - if (IsAutoContributeHandledByContentScript(url)) { +void RewardsServiceImpl::OnLoad(SessionID tab_id, const GURL& url) { + if (!Connected()) { return; } @@ -755,8 +725,12 @@ void RewardsServiceImpl::OnXHRLoad(SessionID tab_id, return; } - if (IsAutoContributeHandledByContentScript(url) || - !GetPublisherDomainFromURL(url)) { + if (base::FeatureList::IsEnabled( + features::kPlatformCreatorDetectionFeature)) { + return; + } + + if (!GetPublisherDomainFromURL(url)) { return; } @@ -1187,29 +1161,6 @@ void RewardsServiceImpl::GetReconcileStamp(GetReconcileStampCallback callback) { engine_->GetReconcileStamp(std::move(callback)); } -#if BUILDFLAG(ENABLE_GREASELION) -void RewardsServiceImpl::EnableGreaselion() { - if (!greaselion_service_) { - return; - } - - greaselion_service_->SetFeatureEnabled(greaselion::REWARDS, true); - greaselion_service_->SetFeatureEnabled( - greaselion::AUTO_CONTRIBUTION, - prefs_->GetBoolean(prefs::kAutoContributeEnabled)); - greaselion_service_->SetFeatureEnabled( - greaselion::ADS, - prefs_->GetBoolean(brave_ads::prefs::kOptedInToNotificationAds)); - - greaselion_enabled_ = true; -} - -void RewardsServiceImpl::OnRulesReady( - greaselion::GreaselionService* greaselion_service) { - EnableGreaselion(); -} -#endif - void RewardsServiceImpl::StopEngine(StopEngineCallback callback) { BLOG(1, "Shutting down rewards process"); if (!Connected()) { @@ -1476,21 +1427,36 @@ void RewardsServiceImpl::GetBalanceReport( std::move(callback))); } +void RewardsServiceImpl::GetPublisherActivityFromVisitData( + mojom::VisitDataPtr visit_data) { + if (!Connected()) { + return; + } + uint32_t tab_id = visit_data->tab_id; + engine_->GetPublisherActivityFromUrl(tab_id, std::move(visit_data), ""); +} + void RewardsServiceImpl::GetPublisherActivityFromUrl( - uint64_t windowId, + uint64_t tab_id, const std::string& url, const std::string& favicon_url, const std::string& publisher_blob) { GURL parsed_url(url); - if (!parsed_url.is_valid() || - IsAutoContributeHandledByContentScript(parsed_url)) { + if (!parsed_url.is_valid()) { return; } + if (base::FeatureList::IsEnabled( + features::kPlatformCreatorDetectionFeature)) { + if (IsMediaPlatformURL(parsed_url)) { + return; + } + } + auto publisher_domain = GetPublisherDomainFromURL(parsed_url); if (!publisher_domain) { mojom::PublisherInfoPtr info; - OnPanelPublisherInfo(mojom::Result::NOT_FOUND, std::move(info), windowId); + OnPanelPublisherInfo(mojom::Result::NOT_FOUND, std::move(info), tab_id); return; } @@ -1505,19 +1471,19 @@ void RewardsServiceImpl::GetPublisherActivityFromUrl( visit_data->url = parsed_url.scheme() + "://" + *publisher_domain + "/"; visit_data->favicon_url = favicon_url; - engine_->GetPublisherActivityFromUrl(windowId, std::move(visit_data), + engine_->GetPublisherActivityFromUrl(tab_id, std::move(visit_data), publisher_blob); } void RewardsServiceImpl::OnPanelPublisherInfo(mojom::Result result, mojom::PublisherInfoPtr info, - uint64_t windowId) { + uint64_t tab_id) { if (result != mojom::Result::OK && result != mojom::Result::NOT_FOUND) { return; } for (auto& observer : observers_) { - observer.OnPanelPublisherInfo(this, result, info.get(), windowId); + observer.OnPanelPublisherInfo(this, result, info.get(), tab_id); } } @@ -1664,18 +1630,6 @@ void RewardsServiceImpl::OnContributionSent( std::move(callback).Run(success); } -void RewardsServiceImpl::UpdateMediaDuration( - const uint64_t window_id, - const std::string& publisher_key, - const uint64_t duration, - const bool first_visit) { - if (!Connected()) { - return; - } - - engine_->UpdateMediaDuration(window_id, publisher_key, duration, first_visit); -} - void RewardsServiceImpl::IsPublisherRegistered( const std::string& publisher_id, base::OnceCallback callback) { diff --git a/components/brave_rewards/browser/rewards_service_impl.h b/components/brave_rewards/browser/rewards_service_impl.h index bb9001e04413..b93cd20e240f 100644 --- a/components/brave_rewards/browser/rewards_service_impl.h +++ b/components/brave_rewards/browser/rewards_service_impl.h @@ -30,7 +30,6 @@ #include "brave/components/brave_rewards/common/rewards_flags.h" #include "brave/components/brave_wallet/browser/brave_wallet_service.h" #include "brave/components/brave_wallet/common/brave_wallet.mojom.h" -#include "brave/components/greaselion/browser/buildflags/buildflags.h" #include "brave/components/services/bat_rewards/public/interfaces/rewards_engine_factory.mojom.h" #include "build/build_config.h" #include "components/prefs/pref_change_registrar.h" @@ -44,10 +43,6 @@ #include "brave/components/safetynet/safetynet_check.h" #endif -#if BUILDFLAG(ENABLE_GREASELION) -#include "brave/components/greaselion/browser/greaselion_service.h" -#endif - namespace base { class OneShotTimer; class SequencedTaskRunner; @@ -92,9 +87,6 @@ using RequestImageCallback = base::RepeatingCallback; using CancelImageRequestCallback = base::RepeatingCallback; class RewardsServiceImpl final : public RewardsService, -#if BUILDFLAG(ENABLE_GREASELION) - public greaselion::GreaselionService::Observer, -#endif public mojom::RewardsEngineClient { public: RewardsServiceImpl(PrefService* prefs, @@ -103,9 +95,6 @@ class RewardsServiceImpl final : public RewardsService, RequestImageCallback request_image_callback, CancelImageRequestCallback cancel_image_request_callback, content::StoragePartition* storage_partition, -#if BUILDFLAG(ENABLE_GREASELION) - greaselion::GreaselionService* greaselion_service, -#endif brave_wallet::BraveWalletService* brave_wallet_service); RewardsServiceImpl(const RewardsServiceImpl&) = delete; @@ -151,6 +140,7 @@ class RewardsServiceImpl final : public RewardsService, void OnGetPublisherInfoList(GetPublisherInfoListCallback callback, std::vector list); + void OnLoad(mojom::VisitDataPtr visit_data) override; void OnLoad(SessionID tab_id, const GURL& url) override; void OnUnload(SessionID tab_id) override; void OnShow(SessionID tab_id) override; @@ -172,11 +162,12 @@ class RewardsServiceImpl final : public RewardsService, const uint32_t month, const uint32_t year, GetBalanceReportCallback callback) override; - void GetPublisherActivityFromUrl( - uint64_t window_id, - const std::string& url, - const std::string& favicon_url, - const std::string& publisher_blob) override; + void GetPublisherActivityFromVisitData( + mojom::VisitDataPtr visit_data) override; + void GetPublisherActivityFromUrl(uint64_t tab_id, + const std::string& url, + const std::string& favicon_url, + const std::string& publisher_blob) override; void GetAutoContributionAmount( GetAutoContributionAmountCallback callback) override; void GetPublisherBanner(const std::string& publisher_id, @@ -225,12 +216,6 @@ class RewardsServiceImpl final : public RewardsService, void SetAutoContributionAmount(const double amount) const override; - void UpdateMediaDuration( - const uint64_t window_id, - const std::string& publisher_key, - const uint64_t duration, - const bool first_visit) override; - void IsPublisherRegistered(const std::string& publisher_id, base::OnceCallback callback) override; @@ -312,13 +297,6 @@ class RewardsServiceImpl final : public RewardsService, using SimpleURLLoaderList = std::list>; -#if BUILDFLAG(ENABLE_GREASELION) - void EnableGreaselion(); - - // GreaselionService::Observer: - void OnRulesReady(greaselion::GreaselionService* greaselion_service) override; -#endif - void InitPrefChangeRegistrar(); void OnPreferenceChanged(const std::string& key); @@ -389,7 +367,7 @@ class RewardsServiceImpl final : public RewardsService, void SetPublisherMinVisits(int visits) const override; void OnPanelPublisherInfo(mojom::Result result, mojom::PublisherInfoPtr info, - uint64_t window_id) override; + uint64_t tab_id) override; void FetchFavIcon(const std::string& url, const std::string& favicon_key, FetchFavIconCallback callback) override; @@ -554,11 +532,6 @@ class RewardsServiceImpl final : public RewardsService, const RequestImageCallback request_image_callback_; const CancelImageRequestCallback cancel_image_request_callback_; raw_ptr storage_partition_; // NOT OWNED -#if BUILDFLAG(ENABLE_GREASELION) - raw_ptr greaselion_service_ = - nullptr; // NOT OWNED - bool greaselion_enabled_ = false; -#endif raw_ptr brave_wallet_service_ = nullptr; mojo::AssociatedReceiver receiver_; mojo::AssociatedRemote engine_; diff --git a/components/brave_rewards/browser/rewards_service_impl_jp_unittest.cc b/components/brave_rewards/browser/rewards_service_impl_jp_unittest.cc index e4ed2bea1c2a..5d4b823ca9a7 100644 --- a/components/brave_rewards/browser/rewards_service_impl_jp_unittest.cc +++ b/components/brave_rewards/browser/rewards_service_impl_jp_unittest.cc @@ -10,7 +10,6 @@ #include "brave/components/brave_rewards/common/features.h" #include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/brave_rewards/core/global_constants.h" -#include "brave/components/greaselion/browser/buildflags/buildflags.h" #include "brave/components/l10n/common/test/scoped_default_locale.h" #include "chrome/browser/profiles/profile.h" #include "chrome/test/base/testing_browser_process.h" @@ -45,9 +44,6 @@ class RewardsServiceJPTest : public testing::Test { const net::NetworkTrafficAnnotationTag& traffic_annotation)>(), base::RepeatingCallback(), profile()->GetDefaultStoragePartition(), -#if BUILDFLAG(ENABLE_GREASELION) - nullptr, -#endif nullptr); ASSERT_TRUE(rewards_service()); diff --git a/components/brave_rewards/browser/rewards_service_impl_unittest.cc b/components/brave_rewards/browser/rewards_service_impl_unittest.cc index 6c6f45426962..9cf26617b9c9 100644 --- a/components/brave_rewards/browser/rewards_service_impl_unittest.cc +++ b/components/brave_rewards/browser/rewards_service_impl_unittest.cc @@ -15,7 +15,6 @@ #include "brave/components/brave_rewards/common/mojom/rewards.mojom.h" #include "brave/components/brave_rewards/common/pref_names.h" #include "brave/components/brave_rewards/core/global_constants.h" -#include "brave/components/greaselion/browser/buildflags/buildflags.h" #include "brave/components/l10n/common/test/scoped_default_locale.h" #include "chrome/browser/profiles/profile.h" #include "chrome/test/base/testing_browser_process.h" @@ -72,9 +71,6 @@ class RewardsServiceTest : public testing::Test { const net::NetworkTrafficAnnotationTag& traffic_annotation)>(), base::RepeatingCallback(), profile()->GetDefaultStoragePartition(), -#if BUILDFLAG(ENABLE_GREASELION) - nullptr, -#endif nullptr); ASSERT_TRUE(rewards_service()); observer_ = std::make_unique(); diff --git a/components/brave_rewards/common/BUILD.gn b/components/brave_rewards/common/BUILD.gn index 79c45b364c60..9c45c3c0cd08 100644 --- a/components/brave_rewards/common/BUILD.gn +++ b/components/brave_rewards/common/BUILD.gn @@ -20,6 +20,8 @@ static_library("common") { "pref_names.h", "pref_registry.cc", "pref_registry.h", + "publisher_utils.cc", + "publisher_utils.h", "rewards_flags.cc", "rewards_flags.h", "rewards_util.cc", @@ -31,6 +33,7 @@ static_library("common") { "//base", "//brave/components/l10n/common", "//components/prefs", - "//third_party/abseil-cpp:absl", + "//net", + "//url", ] } diff --git a/components/brave_rewards/common/features.cc b/components/brave_rewards/common/features.cc index d9acd9a13be8..1b3b0c87a9c3 100644 --- a/components/brave_rewards/common/features.cc +++ b/components/brave_rewards/common/features.cc @@ -41,4 +41,13 @@ BASE_FEATURE(kAnimatedBackgroundFeature, "BraveRewardsAnimatedBackground", base::FEATURE_DISABLED_BY_DEFAULT); +BASE_FEATURE(kPlatformCreatorDetectionFeature, + "BraveRewardsPlatformCreatorDetection", +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) + base::FEATURE_DISABLED_BY_DEFAULT +#else + base::FEATURE_ENABLED_BY_DEFAULT +#endif +); + } // namespace brave_rewards::features diff --git a/components/brave_rewards/common/features.h b/components/brave_rewards/common/features.h index bc386f6888f2..27bc8059b40a 100644 --- a/components/brave_rewards/common/features.h +++ b/components/brave_rewards/common/features.h @@ -30,6 +30,8 @@ BASE_DECLARE_FEATURE(kNewRewardsUIFeature); BASE_DECLARE_FEATURE(kAnimatedBackgroundFeature); +BASE_DECLARE_FEATURE(kPlatformCreatorDetectionFeature); + } // namespace brave_rewards::features #endif // BRAVE_COMPONENTS_BRAVE_REWARDS_COMMON_FEATURES_H_ diff --git a/components/brave_rewards/common/mojom/rewards_engine.mojom b/components/brave_rewards/common/mojom/rewards_engine.mojom index fcb072b888cf..641be2307337 100644 --- a/components/brave_rewards/common/mojom/rewards_engine.mojom +++ b/components/brave_rewards/common/mojom/rewards_engine.mojom @@ -110,11 +110,6 @@ interface RewardsEngine { StartContributionsForTesting(); - UpdateMediaDuration(uint64 window_id, - string publisher_key, - uint64 duration, - bool first_visit); - IsPublisherRegistered(string publisher_id) => (bool registered); GetPublisherInfo(string publisher_key) diff --git a/components/brave_rewards/browser/publisher_utils.cc b/components/brave_rewards/common/publisher_utils.cc similarity index 58% rename from components/brave_rewards/browser/publisher_utils.cc rename to components/brave_rewards/common/publisher_utils.cc index 3e6b0ebdc4b9..1643572e6da1 100644 --- a/components/brave_rewards/browser/publisher_utils.cc +++ b/components/brave_rewards/common/publisher_utils.cc @@ -1,33 +1,51 @@ /* Copyright (c) 2022 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 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/brave_rewards/browser/publisher_utils.h" +#include "brave/components/brave_rewards/common/publisher_utils.h" #include #include #include "base/ranges/algorithm.h" +#include "base/strings/string_util.h" #include "net/base/registry_controlled_domains/registry_controlled_domain.h" namespace brave_rewards { namespace { -const std::array kMediaPlatformDomains = {"twitter.com", "github.com", - "reddit.com", "twitch.tv", - "vimeo.com", "youtube.com"}; +constexpr auto kMediaPlatformDomains = std::to_array( + {"github.com", "reddit.com", "twitch.tv", "twitter.com", "vimeo.com", + "x.com", "youtube.com"}); + +constexpr auto kMediaPlatformPrefixes = std::to_array( + {"github#", "reddit#", "twitch#", "twitter#", "vimeo#", "youtube#"}); + +} // namespace bool IsMediaPlatformURL(const GURL& url) { - return base::ranges::any_of(kMediaPlatformDomains, [&url](auto* domain) { + if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS()) { + return false; + } + return base::ranges::any_of(kMediaPlatformDomains, [&url](auto domain) { return net::registry_controlled_domains::SameDomainOrHost( url, GURL("https://" + std::string(domain)), net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); }); } -} // namespace +std::optional GetMediaPlatformFromPublisherId( + std::string_view publisher_id) { + for (auto prefix : kMediaPlatformPrefixes) { + CHECK(prefix.length() > 1); + if (publisher_id.starts_with(prefix)) { + return std::string(publisher_id.substr(0, prefix.length() - 1)); + } + } + return std::nullopt; +} std::optional GetPublisherIdFromURL(const GURL& url) { if (IsMediaPlatformURL(url)) { @@ -50,12 +68,4 @@ std::optional GetPublisherDomainFromURL(const GURL& url) { return domain; } -bool IsAutoContributeHandledByContentScript(const GURL& url) { -#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS) - return false; -#else - return IsMediaPlatformURL(url); -#endif -} - } // namespace brave_rewards diff --git a/components/brave_rewards/browser/publisher_utils.h b/components/brave_rewards/common/publisher_utils.h similarity index 58% rename from components/brave_rewards/browser/publisher_utils.h rename to components/brave_rewards/common/publisher_utils.h index f248fe77f4bf..f55b6d8e6a70 100644 --- a/components/brave_rewards/browser/publisher_utils.h +++ b/components/brave_rewards/common/publisher_utils.h @@ -1,18 +1,27 @@ /* Copyright (c) 2022 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 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_BRAVE_REWARDS_BROWSER_PUBLISHER_UTILS_H_ -#define BRAVE_COMPONENTS_BRAVE_REWARDS_BROWSER_PUBLISHER_UTILS_H_ +#ifndef BRAVE_COMPONENTS_BRAVE_REWARDS_COMMON_PUBLISHER_UTILS_H_ +#define BRAVE_COMPONENTS_BRAVE_REWARDS_COMMON_PUBLISHER_UTILS_H_ #include #include +#include #include "url/gurl.h" namespace brave_rewards { +// Returns a value indicating whether the specified URL is a social media +// platform that can host Rewards publisher content. +bool IsMediaPlatformURL(const GURL& url); + +// Returns the media platform associated with the specified publisher ID. +std::optional GetMediaPlatformFromPublisherId( + std::string_view publisher_id); + // Returns the publisher ID associated with the specified URL, or `nullopt` if // the publisher ID cannot be statically determined from the URL. For example, // a `nullopt` will be returned if the URL points to a configured social media @@ -23,9 +32,6 @@ std::optional GetPublisherIdFromURL(const GURL& url); // platforms, the site domain will be returned (e.g "twitter.com"). std::optional GetPublisherDomainFromURL(const GURL& url); -// Returns a value indicating whether content scripting is used to measure AC. -bool IsAutoContributeHandledByContentScript(const GURL& url); - } // namespace brave_rewards -#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_BROWSER_PUBLISHER_UTILS_H_ +#endif // BRAVE_COMPONENTS_BRAVE_REWARDS_COMMON_PUBLISHER_UTILS_H_ diff --git a/components/brave_rewards/common/publisher_utils_unittest.cc b/components/brave_rewards/common/publisher_utils_unittest.cc new file mode 100644 index 000000000000..ec60f2c92a46 --- /dev/null +++ b/components/brave_rewards/common/publisher_utils_unittest.cc @@ -0,0 +1,85 @@ +/* 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/components/brave_rewards/common/publisher_utils.h" + +#include +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace brave_rewards { + +namespace { + +std::optional GetPublisherId(const std::string& url) { + return GetPublisherIdFromURL(GURL(url)); +} + +} // namespace + +class RewardsPublisherUtilsTest : public testing::Test {}; + +TEST(RewardsPublisherUtilsTest, IsMediaPlatformURL) { + EXPECT_FALSE(IsMediaPlatformURL(GURL("https://brave.com"))); + EXPECT_FALSE(IsMediaPlatformURL(GURL("http://brave.com"))); + EXPECT_FALSE(IsMediaPlatformURL(GURL("https://search.brave.com"))); + + EXPECT_FALSE(IsMediaPlatformURL(GURL("https://brave.co.uk"))); + EXPECT_FALSE(IsMediaPlatformURL(GURL("https://www.brave.co.uk"))); + + EXPECT_FALSE(IsMediaPlatformURL(GURL("file:///a/b/c/"))); + EXPECT_FALSE(IsMediaPlatformURL(GURL("invalid-url"))); + EXPECT_FALSE(IsMediaPlatformURL(GURL("abc://twitter.com/foo"))); + + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://twitter.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://www.twitter.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://x.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://www.x.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://github.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://reddit.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://youtube.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://vimeo.com/foo"))); + EXPECT_TRUE(IsMediaPlatformURL(GURL("https://twitch.tv/foo"))); +} + +TEST(RewardsPublisherUtilsTest, GetMediaPlatformFromPublisherId) { + EXPECT_EQ(GetMediaPlatformFromPublisherId("youtube#channel:123").value(), + "youtube"); + EXPECT_EQ(GetMediaPlatformFromPublisherId("reddit#channel:123").value(), + "reddit"); + EXPECT_EQ(GetMediaPlatformFromPublisherId("github#channel:123").value(), + "github"); + EXPECT_EQ(GetMediaPlatformFromPublisherId("twitch#author:123").value(), + "twitch"); + EXPECT_EQ(GetMediaPlatformFromPublisherId("vimeo#channel:123").value(), + "vimeo"); + EXPECT_EQ(GetMediaPlatformFromPublisherId("twitter#channel:123").value(), + "twitter"); + EXPECT_EQ(GetMediaPlatformFromPublisherId("example.com"), std::nullopt); +} + +TEST(RewardsPublisherUtilsTest, GetPublisherIdFromURL) { + EXPECT_EQ(GetPublisherId("https://brave.com"), "brave.com"); + EXPECT_EQ(GetPublisherId("http://brave.com"), "brave.com"); + EXPECT_EQ(GetPublisherId("https://search.brave.com"), "brave.com"); + EXPECT_EQ(GetPublisherId("http://search.brave.com"), "brave.com"); + + EXPECT_EQ(GetPublisherId("https://brave.co.uk"), "brave.co.uk"); + EXPECT_EQ(GetPublisherId("https://www.brave.co.uk"), "brave.co.uk"); + + EXPECT_EQ(GetPublisherId("file:///a/b/c/"), std::nullopt); + EXPECT_EQ(GetPublisherId("invalid-url"), std::nullopt); + + EXPECT_EQ(GetPublisherId("https://twitter.com/foo"), std::nullopt); + EXPECT_EQ(GetPublisherId("https://github.com/foo"), std::nullopt); + EXPECT_EQ(GetPublisherId("https://reddit.com/foo"), std::nullopt); + EXPECT_EQ(GetPublisherId("https://youtube.com/foo"), std::nullopt); + EXPECT_EQ(GetPublisherId("https://vimeo.com/foo"), std::nullopt); + EXPECT_EQ(GetPublisherId("https://twitch.tv/foo"), std::nullopt); +} + +} // namespace brave_rewards diff --git a/components/brave_rewards/core/publisher/publisher.cc b/components/brave_rewards/core/publisher/publisher.cc index 906c5d401d02..8bf823bb84f6 100644 --- a/components/brave_rewards/core/publisher/publisher.cc +++ b/components/brave_rewards/core/publisher/publisher.cc @@ -724,39 +724,6 @@ void Publisher::OnSearchPrefixListForGetServerPublisherInfo( } } -void Publisher::UpdateMediaDuration(const uint64_t window_id, - const std::string& publisher_key, - const uint64_t duration, - const bool first_visit) { - engine_->Log(FROM_HERE) << "Media duration: " << duration; - engine_->database()->GetPublisherInfo( - publisher_key, - base::BindOnce(&Publisher::OnGetPublisherInfoForUpdateMediaDuration, - weak_factory_.GetWeakPtr(), window_id, duration, - first_visit)); -} - -void Publisher::OnGetPublisherInfoForUpdateMediaDuration( - const uint64_t window_id, - const uint64_t duration, - const bool first_visit, - mojom::Result result, - mojom::PublisherInfoPtr info) { - if (result != mojom::Result::OK) { - engine_->LogError(FROM_HERE) - << "Failed to retrieve publisher info while updating media duration"; - return; - } - - mojom::VisitData visit_data; - visit_data.name = info->name; - visit_data.url = info->url; - visit_data.provider = info->provider; - visit_data.favicon_url = info->favicon_url; - - SaveVisit(info->id, visit_data, duration, first_visit, 0, base::DoNothing()); -} - void Publisher::GetPublisherPanelInfo(const std::string& publisher_key, GetPublisherPanelInfoCallback callback) { auto filter = CreateActivityFilter( diff --git a/components/brave_rewards/core/publisher/publisher.h b/components/brave_rewards/core/publisher/publisher.h index c57f04a70382..0eb8954dc195 100644 --- a/components/brave_rewards/core/publisher/publisher.h +++ b/components/brave_rewards/core/publisher/publisher.h @@ -85,11 +85,6 @@ class Publisher { bool use_prefix_list, GetServerPublisherInfoCallback callback); - void UpdateMediaDuration(const uint64_t window_id, - const std::string& publisher_key, - const uint64_t duration, - const bool first_visit); - void GetPublisherPanelInfo(const std::string& publisher_key, GetPublisherPanelInfoCallback callback); @@ -112,12 +107,6 @@ class Publisher { GetServerPublisherInfoCallback callback, bool publisher_exists); - void OnGetPublisherInfoForUpdateMediaDuration(const uint64_t window_id, - const uint64_t duration, - const bool first_visit, - mojom::Result result, - mojom::PublisherInfoPtr info); - void OnGetPanelPublisherInfo(GetPublisherPanelInfoCallback callback, const mojom::Result result, mojom::PublisherInfoPtr info); diff --git a/components/brave_rewards/core/rewards_engine.cc b/components/brave_rewards/core/rewards_engine.cc index b2b5c11b573b..f6de5f5c7efc 100644 --- a/components/brave_rewards/core/rewards_engine.cc +++ b/components/brave_rewards/core/rewards_engine.cc @@ -456,16 +456,6 @@ void RewardsEngine::StartContributionsForTesting() { }); } -void RewardsEngine::UpdateMediaDuration(uint64_t window_id, - const std::string& publisher_key, - uint64_t duration, - bool first_visit) { - WhenReady([this, window_id, publisher_key, duration, first_visit] { - publisher()->UpdateMediaDuration(window_id, publisher_key, duration, - first_visit); - }); -} - void RewardsEngine::IsPublisherRegistered( const std::string& publisher_id, IsPublisherRegisteredCallback callback) { diff --git a/components/brave_rewards/core/rewards_engine.h b/components/brave_rewards/core/rewards_engine.h index 6be3c41106ec..a4f24767f607 100644 --- a/components/brave_rewards/core/rewards_engine.h +++ b/components/brave_rewards/core/rewards_engine.h @@ -190,11 +190,6 @@ class RewardsEngine : public mojom::RewardsEngine, void StartContributionsForTesting() override; - void UpdateMediaDuration(uint64_t window_id, - const std::string& publisher_key, - uint64_t duration, - bool first_visit) override; - void IsPublisherRegistered(const std::string& publisher_id, IsPublisherRegisteredCallback callback) override; diff --git a/components/brave_rewards/resources/BUILD.gn b/components/brave_rewards/resources/BUILD.gn index 7ff8e7b65693..370bcc3a5e69 100644 --- a/components/brave_rewards/resources/BUILD.gn +++ b/components/brave_rewards/resources/BUILD.gn @@ -37,6 +37,7 @@ repack("resources") { ":internals_generated_resources", ":page_generated_resources", ":static_resources", + "creator_detection:creator_detection_generated", "rewards_page:rewards_page_generated", ] @@ -44,6 +45,7 @@ repack("resources") { "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_internals_generated.pak", "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_page_generated.pak", "$root_gen_dir/brave/components/brave_rewards/resources/brave_rewards_static.pak", + "$root_gen_dir/brave/components/brave_rewards/resources/creator_detection_generated.pak", "$root_gen_dir/brave/components/brave_rewards/resources/rewards_page_generated.pak", ] diff --git a/components/brave_rewards/resources/creator_detection/BUILD.gn b/components/brave_rewards/resources/creator_detection/BUILD.gn new file mode 100644 index 000000000000..e4bc58e57b46 --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/BUILD.gn @@ -0,0 +1,42 @@ +# 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/. + +import("//brave/components/common/typescript.gni") + +transpile_web_ui("creator_detection") { + entry_points = [ + [ + "github", + rebase_path("github.ts"), + ], + [ + "reddit", + rebase_path("reddit.ts"), + ], + [ + "twitch", + rebase_path("twitch.ts"), + ], + [ + "twitter", + rebase_path("twitter.ts"), + ], + [ + "vimeo", + rebase_path("vimeo.ts"), + ], + [ + "youtube", + rebase_path("youtube.ts"), + ], + ] + resource_name = "creator_detection" +} + +pack_web_resources("creator_detection_generated") { + resource_name = "creator_detection" + output_dir = "$root_gen_dir/brave/components/brave_rewards/resources" + deps = [ ":creator_detection" ] +} diff --git a/components/brave_rewards/resources/creator_detection/creator_detection.ts b/components/brave_rewards/resources/creator_detection/creator_detection.ts new file mode 100644 index 000000000000..b581c209801a --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/creator_detection.ts @@ -0,0 +1,57 @@ +// 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/. + +import { log, throttle } from './helpers' + +export interface CreatorInfo { + id: string + name: string + url: string + imageURL: string +} + +type DetectionHandler = () => Promise + +// The following global is injected by the browser prior to initializing the +// detection script. The script is responsible for setting the `detectCreator` +// function. +declare const braveRewards: { + verboseLogging: boolean + detectCreator?: DetectionHandler +} + +export function initializeDetector(initializer: () => DetectionHandler) { + if (braveRewards.verboseLogging) { + log.verbose = true + } + + const detectCreator = throttle(initializer()) + let currentURL = '' + + braveRewards.detectCreator = async () => { + if (location.href === currentURL) { + return null + } + + currentURL = location.href + + let creator: CreatorInfo | null = null + try { + creator = await detectCreator() + } catch (err) { + log.error('Error detecting creator', err) + } + + if (creator) { + log.info('Found creator:', creator) + } else { + log.info('No creator found for current URL') + } + + return creator + } +} + + diff --git a/components/brave_rewards/resources/creator_detection/github.ts b/components/brave_rewards/resources/creator_detection/github.ts new file mode 100644 index 000000000000..3e25c2935942 --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/github.ts @@ -0,0 +1,112 @@ +// 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/. + +import { CreatorInfo, initializeDetector } from './creator_detection' +import { log, getPathComponents } from './helpers' + +initializeDetector(() => { + + const nonUserPaths = new Set([ + 'about', + 'enterprise', + 'events', + 'explore', + 'home', + 'issues', + 'login', + 'logout', + 'marketplace', + 'nonprofit', + 'notifications', + 'orgs', + 'pricing', + 'pulls', + 'search', + 'settings', + 'team', + 'tos' + ]) + + function getScreenNameFromPath(path: string) { + const parts = getPathComponents(path) + if (parts.length === 0) { + return '' + } + + const screenName = parts[0] + if (screenName === 'orgs' && parts.length > 1) { + return parts[1] + } + + if (nonUserPaths.has(screenName)) { + return '' + } + + return screenName + } + + function getCreatorFromResponse(screenName: string, response: any): any { + if (!response) { + log.info('Empty response') + return null + } + if (!response.id) { + log.info('Missing "id" property') + return null + } + return { + id: `github#channel:${response.id}`, + name: screenName, + url: `https://github.com/${screenName}/`, + imageURL: String(response.avatar_url || '') + } + } + + let currentCreator: CreatorInfo | null = null + + async function fetchUserData(screenName: string) { + if (!screenName) { + return null + } + + if (currentCreator && currentCreator.name === screenName) { + return currentCreator + } + + const hostname = 'api.' + location.hostname.split('.').slice(-2).join('.') + const origin = location.origin.replace(location.hostname, hostname) + const response = await fetch(`${origin}/users/${screenName}`) + + if (!response.ok) { + log.error('Unable to fetch profile data') + return null + } + + let responseJSON: any + try { + responseJSON = await response.json() + } catch (err) { + log.error('Error parsing profile data', err) + return null + } + + currentCreator = getCreatorFromResponse(screenName, responseJSON) + if (!currentCreator) { + log.info('Profile data response', responseJSON) + } + + return currentCreator + } + + return async () => { + const screenName = getScreenNameFromPath(location.pathname) + if (!screenName) { + log.info('No screen name for the current path') + return null + } + return await fetchUserData(screenName) + } + +}) diff --git a/components/brave_rewards/resources/creator_detection/helpers.ts b/components/brave_rewards/resources/creator_detection/helpers.ts new file mode 100644 index 000000000000..171220ad3edd --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/helpers.ts @@ -0,0 +1,74 @@ +// 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/. + +export const log = new class { + verbose = false + + info(...args: any[]) { + if (this.verbose) { + console.info('[Rewards]', ...args) + } + } + + error(...args: any[]) { + console.error('[Rewards]', ...args) + } +} + +interface PollOptions { + name: string + timeout: number + interval: number +} + +export async function pollFor(fn: () => T | Promise, opts: PollOptions) { + const startTime = Date.now() + while (Date.now() - startTime < opts.timeout) { + const result = await fn() + if (result) { + return result + } + await new Promise((resolve) => setTimeout(resolve, opts.interval)) + } + log.info(`Timed out waiting for ${opts.name}`) + return null +} + +export function throttle(fn: () => Promise | T) { + let current: Promise | null = null + let next: Promise | null = null + + let start = () => { + current = Promise.resolve(fn()).finally(() => { current = null }) + return current + } + + return () => { + if (!current) { + return start() + } + if (!next) { + next = current.finally(() => { next = null }).then(start) + } + return next + } +} + +export function urlPath(url: string) { + try { return new URL(url, location.href).pathname } + catch { return '' } +} + +export function absoluteURL(url: string) { + try { return new URL(url, location.href).toString() } + catch { return '' } +} + +export function getPathComponents(path: string) { + return path + .split('/') + .filter((part) => part) + .map((part) => part.toLocaleLowerCase()) +} diff --git a/components/brave_rewards/resources/creator_detection/reddit.ts b/components/brave_rewards/resources/creator_detection/reddit.ts new file mode 100644 index 000000000000..8ee4343237f4 --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/reddit.ts @@ -0,0 +1,81 @@ +// 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/. + +import { CreatorInfo, initializeDetector } from './creator_detection' +import { log } from './helpers' + +initializeDetector(() => { + + function getCreatorFromResponse(screenName: string, response: any): any { + if (!response || response.kind !== 't2') { + log.info('Unexpected "kind" value') + return null + } + const { data } = response + if (!data) { + log.info('Missing "data" property') + return null + } + if (!data.id || typeof data.id !== 'string') { + log.info('Missing "id" property') + return null + } + return { + id: `reddit#channel:${data.id}`, + name: screenName, + url: `https://reddit.com/user/${screenName}/`, + imageURL: String(data.icon_img || '') + } + } + + let currentCreator: CreatorInfo | null = null + + async function fetchUserData(screenName: string) { + if (!screenName) { + return null + } + + if (currentCreator && currentCreator.name === screenName) { + return currentCreator + } + + log.info('Fetching profile data for', screenName) + const response = await fetch(`/user/${screenName}/about.json`) + if (!response.ok) { + log.error('Unable to fetch profile data') + return null + } + + let responseJSON: any + try { + responseJSON = await response.json() + } catch (err) { + log.error('Error parsing profile data', err) + return null + } + + currentCreator = getCreatorFromResponse(screenName, responseJSON) + if (!currentCreator) { + log.info('Profile data response', responseJSON) + } + + return currentCreator + } + + function getScreenNameFromPath(path: string) { + const match = path.match(/^\/user\/([^\/]+)/i) + return match ? match[1] : '' + } + + return async () => { + const screenName = getScreenNameFromPath(location.pathname) + if (!screenName) { + log.info('No screen name for the current path') + return null + } + return await fetchUserData(screenName) + } + +}) diff --git a/components/brave_rewards/resources/creator_detection/twitch.ts b/components/brave_rewards/resources/creator_detection/twitch.ts new file mode 100644 index 000000000000..523ffd3455e4 --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/twitch.ts @@ -0,0 +1,125 @@ +// 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/. + +import { CreatorInfo, initializeDetector } from './creator_detection' +import { log, getPathComponents, pollFor } from './helpers' + +initializeDetector(() => { + + function scrapeIcon() { + const elem = + document.querySelector('.channel-info-content .tw-avatar [src]') + return (elem && elem.getAttribute('src')) || '' + } + + function getChannelTitleElement() { + return document.querySelector('h1.tw-title') + } + + function scrapeChannelName() { + const elem = getChannelTitleElement() + return elem && elem.textContent || '' + } + + function firstPathComponent(path: string) { + return getPathComponents(path)[0] || '' + } + + function isVideoPath() { + return firstPathComponent(location.pathname) === 'videos' + } + + const excludedPaths = new Set([ + 'directory', + 'downloads', + 'jobs', + 'p', + 'search', + 'turbo' + ]) + + function scrapeChannelID() { + if (isVideoPath()) { + const elem = getChannelTitleElement() + if (!elem || !elem.parentElement) { + log.info('Unable to find parent of channel title element') + return '' + } + const href = elem.parentElement.getAttribute('href') + if (!href) { + log.info('Missing "href" attribute in title element') + return '' + } + let id = firstPathComponent(href) + if (!id) { + log.info('Unable to find channel ID in channel title "href":', href) + return '' + } + log.info('Found channel ID in channel title element:', id) + return id + } + + const pathPart = firstPathComponent(location.pathname) + if (!pathPart || excludedPaths.has(pathPart)) { + log.info('Path does not support creator detection') + return '' + } + + log.info('Found channel ID in path:', pathPart) + return pathPart + } + + function scrapeUser() { + const id = scrapeChannelID() + if (!id) { + return null + } + return { + id: `twitch#author:${id}`, + name: scrapeChannelName(), + url: `${location.origin}/${id}`, + imageURL: scrapeIcon() + } + } + + let currentUser: CreatorInfo | null = null + + function isPageLoadComplete() { + const user = scrapeUser() + if (!user) { + return !isVideoPath() + } + if (!user.name || !user.imageURL) { + return false + } + if (!currentUser) { + return true + } + if (user.id === currentUser.id) { + return true + } + return ( + user.name !== currentUser.name && + user.imageURL !== currentUser.imageURL + ) + } + + function getUser() { + if (!isPageLoadComplete()) { + return null + } + currentUser = scrapeUser() + return currentUser + } + + return async () => { + return await pollFor(getUser, { + name: 'creator', + interval: 500, + timeout: 6000 + }) + } + +}) diff --git a/components/brave_rewards/resources/creator_detection/twitter.ts b/components/brave_rewards/resources/creator_detection/twitter.ts new file mode 100644 index 000000000000..8222a4a0548a --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/twitter.ts @@ -0,0 +1,148 @@ +// 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/. + +import { CreatorInfo, initializeDetector } from './creator_detection' +import { log, pollFor } from './helpers' + +function getUserFromAppState(eventName: string, screenName: string) { + interface StateStore { + getState: () => any + } + + let messages: any[] = [] + + function getElementStore(elem: object | null) { + if (!elem) { + return null + } + for (const name of Object.getOwnPropertyNames(elem)) { + if (name.startsWith('__reactProps$')) { + let store: any = null + try { store = (elem as any)[name].children.props.store } + catch {} + if (store && typeof store.getState === 'function') { + return store as StateStore + } + } + } + return null + } + + function findStore(elem: Element | null, depth = 0): StateStore | null { + if (!elem) { + return null + } + let store = getElementStore(elem) + if (store) { + return store + } + if (depth === 4) { + return null + } + for (let child of elem.children) { + store = findStore(child, depth + 1) + if (store) { + return store + } + } + return null + } + + function screenNamesMatch(name1: any, name2: any) { + if (typeof name1 === 'string' && typeof name2 === 'string') { + return name1.toLocaleLowerCase() === name2.toLocaleLowerCase() + } + return false + } + + function getUserFromState(state: any, screenName: string) { + const userEntities = state.entities.users.entities + for (let [key, value] of Object.entries(userEntities) as any) { + if (screenNamesMatch(value.screen_name, screenName)) { + return { + id: `twitter#channel:${key}`, + name: screenName, + url: `${location.origin}/${screenName}`, + imageURL: String(value.profile_image_url_https || '') + } + } + } + messages = ['Screen name not found in state'] + return null + } + + function getUserByScreenName() { + const store = findStore(document.getElementById('react-root')) + if (!store) { + messages = ['State store could not be found'] + return null + } + const state = store.getState() + try { + return getUserFromState(state, screenName) + } catch (e) { + console.error(e) + messages = ['Error attempting to get user state', state] + } + return null + } + + document.dispatchEvent(new CustomEvent(eventName, { + detail: { user: getUserByScreenName(), messages } + })) +} + +initializeDetector(() => { + async function getUserFromState(screenName: string) { + return new Promise((resolve) => { + const eventName = 'brave-rewards-user-data' + const listener = (event: CustomEvent) => { + document.removeEventListener(eventName, listener) + const { user, messages } = event.detail || {} + resolve(user || null) + if (Array.isArray(messages) && messages.length > 0) { + log.info(...messages) + } + } + document.addEventListener(eventName, listener) + const script = document.createElement('script') + script.textContent = `(${getUserFromAppState})( + ${JSON.stringify(eventName)}, + ${JSON.stringify(screenName)})` + document.head.appendChild(script) + document.head.removeChild(script) + }) + } + + function getScreenNameFromPath(path: string) { + let match = path.match(/^\/([^\/]+)(\/|\/status\/[\s\S]+)?$/) + if (match) { + log.info('Found screen name in path:', match[1]) + return match[1] + } + log.info('Screen name not found in path') + return null + } + + async function getUserFromPath(path: string) { + const screenName = getScreenNameFromPath(path) + if (screenName) { + const user = await getUserFromState(screenName) + if (user) { + return user + } + } + return null + } + + return async () => { + return await pollFor(() => getUserFromPath(location.pathname), { + name: 'creator', + interval: 1000, + timeout: 6000 + }) + } + +}) diff --git a/components/brave_rewards/resources/creator_detection/vimeo.ts b/components/brave_rewards/resources/creator_detection/vimeo.ts new file mode 100644 index 000000000000..358f9462b14b --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/vimeo.ts @@ -0,0 +1,120 @@ +// 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/. + +import { initializeDetector } from './creator_detection' +import { log, pollFor, absoluteURL } from './helpers' + +initializeDetector(() => { + + function getIdFromPerson(person: any) { + if (!person) { + return '' + } + if (person.identifier) { + return String(person.identifier) + } + const { potentialAction } = person + if (!potentialAction) { + log.error('Missing "potentialAction" in Person') + return '' + } + const { target } = potentialAction + if (!target || typeof target !== 'string') { + log.error('Missing "potentialAction.target" in Person') + return '' + } + const match = target.match(/\/users\/([^\/]+)/i) + if (!match) { + log.error('Unable to read ID from Person') + return '' + } + return match[1] + } + + function getUserFromPerson(person: any) { + const id = getIdFromPerson(person) + if (!id) { + return null + } + return { + id: `vimeo#channel:${id}`, + name: String(person.name || ''), + url: absoluteURL(person.url || location.href), + imageURL: String(person.image || '') + } + } + + function getUserFromVideoData(video: any) { + if (!video) { + return null + } + const { author } = video + if (!author) { + log.error('Missing "author" in VideoObject') + return null + } + return getUserFromPerson(author) + } + + function getUserFromMetadata(json: string) { + let data: any + try { + data = JSON.parse(json) + } catch (err) { + log.error('Invalid JSON in metadata script') + return null + } + if (!Array.isArray(data)) { + log.error('Invalid metadata object: expected an array') + return null + } + for (const item of data) { + switch (item['@type']) { + case 'Person': { + const user = getUserFromPerson(item) + if (user) { + return user + } + } + case 'VideoObject': { + const user = getUserFromVideoData(item) + if (user) { + return user + } + } + } + } + log.info('Unable to find user in metadata scripts') + return null + } + + const metadataScriptSelector = 'script[type="application/ld+json"]' + + function parseMetadataScript() { + const elems = document.querySelectorAll(metadataScriptSelector) + for (const elem of elems) { + const user = getUserFromMetadata(elem.innerText) + if (user) { + return user + } + } + return null + } + + function hasMetadataScript() { + return Boolean(document.querySelector(metadataScriptSelector)) + } + + return async () => { + await pollFor(hasMetadataScript, { + name: 'metadata', + interval: 500, + timeout: 6000 + }) + + return parseMetadataScript() + } + +}) diff --git a/components/brave_rewards/resources/creator_detection/youtube.ts b/components/brave_rewards/resources/creator_detection/youtube.ts new file mode 100644 index 000000000000..a8f97894dd7a --- /dev/null +++ b/components/brave_rewards/resources/creator_detection/youtube.ts @@ -0,0 +1,238 @@ +// 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/. + +import { initializeDetector } from './creator_detection' +import { log, pollFor, urlPath, absoluteURL } from './helpers' + +initializeDetector(() => { + + function getChannelFromURL(url: string) { + let pathname = urlPath(url) + let match = pathname.match(/^\/channel\/([^\/]+)/i) + if (match) { + return match[1] + } + return '' + } + + function getVideoIdFromURL(url: string) { + if (!urlPath(url).match(/^\/watch(\/|$)/i)) { + return '' + } + let params = null + try { + params = new URL(url).searchParams + } catch { + return '' + } + return params.get('v') || '' + } + + function getChannelNameFromURL(url: string) { + let pathname = urlPath(url) + let match = pathname.match(/^\/@([^\/]+)/) + if (match) { + return match[1] + } + return '' + } + + type PathType = 'channel' | 'channel-name' | 'video' | '' + + function getPathType(): PathType { + if (getChannelFromURL(location.href)) { + return 'channel' + } + if (getChannelNameFromURL(location.href)) { + return 'channel-name' + } + if (getVideoIdFromURL(location.href)) { + return 'video' + } + return '' + } + + let firstChannelScrape = true + let currentPathType: PathType = '' + + // Attempts to find the channel identifier by inspecting the URL and various + // DOM elements. + function scrapeChannel() { + let firstScrape = firstChannelScrape + firstChannelScrape = false + + let channel = getChannelFromURL(location.href) + if (channel) { + return channel + } + + let elem = document.querySelector('a[aria-label=About]') + if (elem) { + channel = getChannelFromURL(elem.href) + if (channel) { + return channel + } + } + + elem = document.querySelector('a.ytp-ce-channel-title') + if (elem) { + channel = getChannelFromURL(elem.href) + if (channel) { + return channel + } + } + + // On initial page load, a "canonical" link element that contains the + // channel identifier may be present on channel pages. On subsequent + // same-document navigations, this element will still exist but will no + // longer be valid. Only look for this element the first time we attempt to + // find the channel ID on the page (i.e. the initial page load). + if (firstScrape) { + let elem = document.querySelector('link[rel=canonical]') + if (elem) { + channel = getChannelFromURL(elem.href) + if (channel) { + return channel + } + } + } + + return '' + } + + function scrapeChannelName() { + let name = getChannelNameFromURL(location.href) + if (name) { + return name + } + + let elems = document.querySelectorAll([ + 'ytd-video-owner-renderer a', + 'ytm-slim-owner-renderer a' + ].join(',')) + + for (let elem of elems) { + name = getChannelNameFromURL(elem.href) + if (name) { + return name + } + } + + return '' + } + + function scrapeImage() { + let elems = document.querySelectorAll([ + '#avatar img', + 'yt-page-header-renderer yt-avatar-shape img', + 'ytm-slim-owner-renderer a img' + ].join(',')) + + for (let elem of elems) { + if (elem.src) { + return elem.src + } + } + + return '' + } + + function scrapeChannelURL() { + let name = scrapeChannelName() + if (name) { + return `${location.origin}/@${name}` + } + if (currentPathType === 'channel') { + return location.href + } + return '' + } + + async function getChannel() { + let channel = scrapeChannel() + if (channel) { + return channel + } + let name = scrapeChannelName() + if (name) { + let channelURL = `/@${name}` + log.info(`Fetching "${channelURL}" for channel ID detection`) + let response = await fetch(channelURL) + let responseText = response.ok ? await response.text() : '' + let match = responseText.match(/( + 'a.ytp-ce-channel-title' + ) + if (elem) { + return absoluteURL(elem.href) === location.href + } + return Boolean(document.querySelector('#page-header')) + } + + if (currentPathType === 'channel-name') { + let elem = document.querySelector( + 'ytd-video-owner-renderer a' + ) + if (elem && absoluteURL(elem.href) === location.href) { + return true + } + let form = document.querySelector('#form') + let name = getChannelNameFromURL(location.href) + return Boolean(form && urlPath(form.action).startsWith(`/@${name}`)) + } + + if (currentPathType === 'video') { + let id = getVideoIdFromURL(location.href) + let elem = document.querySelector('ytd-watch-metadata') + return Boolean(elem && elem.getAttribute('video-id') === id) + } + + return true + } + + return async () => { + currentPathType = getPathType() + log.info(`Current path type: ${currentPathType} (${location.pathname})`) + + await pollFor(isPageLoadComplete, { + name: 'load complete', + interval: 500, + timeout: 6000 + }) + + return await getUser() + } + +}) diff --git a/components/brave_rewards/test/BUILD.gn b/components/brave_rewards/test/BUILD.gn index 9c342cbf4fdb..6d06bf992c20 100644 --- a/components/brave_rewards/test/BUILD.gn +++ b/components/brave_rewards/test/BUILD.gn @@ -11,10 +11,10 @@ source_set("brave_rewards_unit_tests") { testonly = true sources = [ - "//brave/components/brave_rewards/browser/publisher_utils_unittest.cc", "//brave/components/brave_rewards/browser/rewards_protocol_navigation_throttle_unittest.cc", "//brave/components/brave_rewards/browser/rewards_service_impl_jp_unittest.cc", "//brave/components/brave_rewards/browser/rewards_service_impl_unittest.cc", + "//brave/components/brave_rewards/common/publisher_utils_unittest.cc", ] deps = [ @@ -28,7 +28,6 @@ source_set("brave_rewards_unit_tests") { "//brave/components/brave_rewards/core:publishers_proto", "//brave/components/brave_rewards/resources:static_resources_grit", "//brave/components/challenge_bypass_ristretto", - "//brave/components/greaselion/browser/buildflags:buildflags", "//brave/components/l10n/common:test_support", "//brave/third_party/rapidjson", "//chrome/browser:browser", diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc index ab9a54276468..9f0a944adf81 100644 --- a/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.cc @@ -14,42 +14,10 @@ #include "brave/components/brave_shields/core/common/features.h" #include "brave/components/de_amp/common/features.h" #include "content/public/renderer/render_frame.h" -#include "third_party/blink/public/platform/web_isolated_world_info.h" -#include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/public/web/web_local_frame.h" namespace cosmetic_filters { -namespace { - -constexpr char kSecurityOrigin[] = "chrome://cosmetic_filters"; - -void EnsureIsolatedWorldInitialized(int world_id) { - static std::optional last_used_world_id; - if (last_used_world_id) { - // Early return since the isolated world info. is already initialized. - DCHECK_EQ(*last_used_world_id, world_id) - << "EnsureIsolatedWorldInitialized should always be called with the " - "same |world_id|"; - return; - } - - last_used_world_id = world_id; - - // Set an empty CSP so that the main world's CSP is not used in the isolated - // world. - constexpr char kContentSecurityPolicy[] = ""; - - blink::WebIsolatedWorldInfo info; - info.security_origin = - blink::WebSecurityOrigin::Create(GURL(kSecurityOrigin)); - info.content_security_policy = - blink::WebString::FromUTF8(kContentSecurityPolicy); - blink::SetIsolatedWorldInfo(world_id, info); -} - -} // namespace - CosmeticFiltersJsRenderFrameObserver::CosmeticFiltersJsRenderFrameObserver( content::RenderFrame* render_frame, const int32_t isolated_world_id, @@ -130,10 +98,6 @@ void CosmeticFiltersJsRenderFrameObserver::DidCreateScriptContext( native_javascript_handle_->AddJavaScriptObjectToFrame(context); } -void CosmeticFiltersJsRenderFrameObserver::DidCreateNewDocument() { - EnsureIsolatedWorldInitialized(isolated_world_id_); -} - void CosmeticFiltersJsRenderFrameObserver::OnDestruct() { delete this; } diff --git a/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h index 1773a95700d7..a54d38989d69 100644 --- a/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h +++ b/components/cosmetic_filters/renderer/cosmetic_filters_js_render_frame_observer.h @@ -48,7 +48,6 @@ class CosmeticFiltersJsRenderFrameObserver void DidCreateScriptContext(v8::Local context, int32_t world_id) override; - void DidCreateNewDocument() override; void RunScriptsAtDocumentStart(); diff --git a/components/definitions/chromel.d.ts b/components/definitions/chromel.d.ts index 2c57f6d4302b..9b388089b011 100644 --- a/components/definitions/chromel.d.ts +++ b/components/definitions/chromel.d.ts @@ -88,16 +88,11 @@ declare namespace chrome.braveRewards { const getUserType: (callback: (userType: string) => void) => void const getPublishersVisitedCount: (callback: (count: number) => void) => void const getRewardsParameters: (callback: (properties: RewardsExtension.RewardsParameters) => void) => {} - const updateMediaDuration: (tabId: number, publisherKey: string, duration: number, firstVisit: boolean) => {} const getPublisherInfo: (publisherKey: string, callback: (result: RewardsExtension.Result, properties: RewardsExtension.PublisherInfo) => void) => {} - const getPublisherPanelInfo: (publisherKey: string, callback: (result: RewardsExtension.Result, properties: RewardsExtension.PublisherInfo) => void) => {} - const setPublisherIdForTab: (tabId: number, publisherId: string) => void const getPublisherInfoForTab: (tabId: number, callback: (publisher?: RewardsExtension.PublisherInfo) => void) => void - const savePublisherInfo: (windowId: number, mediaType: string, url: string, publisherKey: string, publisherName: string, favIconUrl: string, callback: (result: RewardsExtension.Result) => void) => {} const tipSite: (tabId: number, publisherKey: string, entryPoint: RewardsExtension.TipDialogEntryPoint) => {} - const getPublisherData: (windowId: number, url: string, faviconUrl: string, publisherBlob: string | undefined) => {} const getBalanceReport: (month: number, year: number, callback: (properties: RewardsExtension.BalanceReport) => void) => {} const onPublisherData: { addListener: (callback: (windowId: number, publisher: RewardsExtension.Publisher) => void) => void diff --git a/components/test/testData.ts b/components/test/testData.ts index 256ffa361fca..5e5dda32037e 100644 --- a/components/test/testData.ts +++ b/components/test/testData.ts @@ -59,9 +59,6 @@ export const getMockChrome = () => { let mock = { send: (methodName: string, ...args: any[]) => undefined, getVariableValue: () => undefined, - braveRewards: { - getPublisherData: (id: number, url: string, favicon: string) => undefined - }, runtime: { onMessage: new ChromeEvent(), onConnect: new ChromeEvent(), diff --git a/renderer/brave_content_renderer_client.cc b/renderer/brave_content_renderer_client.cc index 57be5fb2a0a5..05ae3517bfc8 100644 --- a/renderer/brave_content_renderer_client.cc +++ b/renderer/brave_content_renderer_client.cc @@ -30,7 +30,9 @@ #include "chrome/renderer/url_loader_throttle_provider_impl.h" #include "content/public/renderer/render_thread.h" #include "third_party/blink/public/common/features.h" +#include "third_party/blink/public/platform/web_isolated_world_info.h" #include "third_party/blink/public/platform/web_runtime_features.h" +#include "third_party/blink/public/platform/web_url.h" #include "third_party/blink/public/web/modules/service_worker/web_service_worker_context_proxy.h" #include "third_party/blink/public/web/web_script_controller.h" #include "third_party/widevine/cdm/buildflags.h" @@ -63,6 +65,27 @@ #endif namespace { + +void EnsureBraveInternalWorldInitialized() { + static bool initialized = false; + if (initialized) { + return; + } + + initialized = true; + + // Set an empty CSP so that the main world's CSP is not used in the isolated + // world. + constexpr char kContentSecurityPolicy[] = ""; + + blink::WebIsolatedWorldInfo info; + info.security_origin = + blink::WebSecurityOrigin::Create(GURL("chrome://brave_internal")); + info.content_security_policy = + blink::WebString::FromUTF8(kContentSecurityPolicy); + blink::SetIsolatedWorldInfo(ISOLATED_WORLD_ID_BRAVE_INTERNAL, info); +} + void MaybeRemoveWidevineSupport(media::GetSupportedKeySystemsCB cb, media::KeySystemInfos key_systems) { #if BUILDFLAG(ENABLE_WIDEVINE) @@ -134,6 +157,8 @@ void BraveContentRendererClient::RenderFrameCreated( content::RenderFrame* render_frame) { ChromeContentRendererClient::RenderFrameCreated(render_frame); + EnsureBraveInternalWorldInitialized(); + if (base::FeatureList::IsEnabled( brave_shields::features::kBraveAdblockCosmeticFiltering)) { auto dynamic_params_closure = base::BindRepeating([]() { diff --git a/resources/resource_ids.spec b/resources/resource_ids.spec index 8c0c471b2330..cfb990c83f75 100644 --- a/resources/resource_ids.spec +++ b/resources/resource_ids.spec @@ -221,4 +221,8 @@ "META": {"sizes": {"includes": [50]}}, "includes": [64430], }, + "<(SHARED_INTERMEDIATE_DIR)/brave/web-ui-creator_detection/creator_detection.grd": { + "META": {"sizes": {"includes": [10]}}, + "includes": [64480], + }, } diff --git a/test/BUILD.gn b/test/BUILD.gn index d66a6783a620..b8cc391f191c 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -11,7 +11,6 @@ import("//brave/components/ai_chat/core/common/buildflags/buildflags.gni") import("//brave/components/brave_vpn/common/buildflags/buildflags.gni") import("//brave/components/brave_wayback_machine/buildflags/buildflags.gni") import("//brave/components/brave_webtorrent/browser/buildflags/buildflags.gni") -import("//brave/components/greaselion/browser/buildflags/buildflags.gni") import("//brave/components/ntp_background_images/buildflags/buildflags.gni") import("//brave/components/playlist/common/buildflags/buildflags.gni") import("//brave/components/request_otr/common/buildflags/buildflags.gni") @@ -1040,17 +1039,6 @@ test("brave_browser_tests") { sources += [ "//brave/browser/ui/views/crash_report_permission_ask_dialog_browsertest.cc" ] } - if (enable_greaselion) { - sources += [ "//brave/browser/greaselion/greaselion_browsertest.cc" ] - - deps += [ - "//brave/browser/brave_rewards", - "//brave/browser/greaselion", - "//brave/components/greaselion/browser", - "//chrome/browser", - ] - } - if (ethereum_remote_client_enabled) { sources += [ "//brave/browser/extensions/brave_wallet_apitest.cc" ]