Merge pull request #13215 from brave/issues/22505
Added viewed confirmation throttle for search result ad.
This commit is contained in:
@@ -124,6 +124,9 @@ void AdsTabHelper::DidFinishNavigation(
|
||||
redirect_chain_ = navigation_handle->GetRedirectChain();
|
||||
|
||||
if (!navigation_handle->IsSameDocument()) {
|
||||
if (search_result_ad_service_) {
|
||||
search_result_ad_service_->OnDidFinishNavigation(tab_id_);
|
||||
}
|
||||
should_process_ = navigation_handle->GetRestoreType() ==
|
||||
content::RestoreType::kNotRestored;
|
||||
return;
|
||||
@@ -136,16 +139,17 @@ void AdsTabHelper::DidFinishNavigation(
|
||||
}
|
||||
|
||||
void AdsTabHelper::DocumentOnLoadCompletedInPrimaryMainFrame() {
|
||||
content::RenderFrameHost* render_frame_host = web_contents()->GetMainFrame();
|
||||
if (search_result_ad_service_) {
|
||||
search_result_ad_service_->MaybeRetrieveSearchResultAd(
|
||||
render_frame_host, tab_id_, should_process_);
|
||||
}
|
||||
|
||||
if (!should_process_) {
|
||||
return;
|
||||
}
|
||||
|
||||
content::RenderFrameHost* render_frame_host = web_contents()->GetMainFrame();
|
||||
RunIsolatedJavaScript(render_frame_host);
|
||||
|
||||
if (search_result_ad_service_) {
|
||||
search_result_ad_service_->MaybeRetrieveSearchResultAd(render_frame_host);
|
||||
}
|
||||
}
|
||||
|
||||
void AdsTabHelper::DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
@@ -203,6 +207,10 @@ void AdsTabHelper::OnVisibilityChanged(content::Visibility visibility) {
|
||||
}
|
||||
|
||||
void AdsTabHelper::WebContentsDestroyed() {
|
||||
if (search_result_ad_service_) {
|
||||
search_result_ad_service_->OnTabClosed(tab_id_);
|
||||
}
|
||||
|
||||
if (!ads_service_) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10,15 +10,19 @@
|
||||
#include <utility>
|
||||
|
||||
#include "brave/browser/brave_ads/ads_service_factory.h"
|
||||
#include "brave/browser/brave_ads/search_result_ad/search_result_ad_service_factory.h"
|
||||
#include "brave/browser/brave_rewards/rewards_service_factory.h"
|
||||
#include "brave/browser/extensions/api/brave_action_api.h"
|
||||
#include "brave/browser/extensions/brave_component_loader.h"
|
||||
#include "brave/components/brave_ads/browser/ads_service.h"
|
||||
#include "brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_service.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service.h"
|
||||
#include "chrome/browser/extensions/extension_service.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
#include "chrome/browser/ui/browser_finder.h"
|
||||
#include "components/sessions/content/session_tab_helper.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "extensions/browser/extension_system.h"
|
||||
#include "extensions/common/constants.h"
|
||||
|
||||
@@ -30,12 +34,37 @@ constexpr char kAdsEnableRelativeUrl[] = "request_ads_enabled_panel.html";
|
||||
|
||||
} // namespace
|
||||
|
||||
BraveAdsHost::BraveAdsHost(Profile* profile) : profile_(profile) {
|
||||
BraveAdsHost::BraveAdsHost(Profile* profile, content::WebContents* web_contents)
|
||||
: profile_(profile),
|
||||
tab_id_(sessions::SessionTabHelper::IdForTab(web_contents)) {
|
||||
DCHECK(profile_);
|
||||
}
|
||||
|
||||
BraveAdsHost::~BraveAdsHost() {}
|
||||
|
||||
void BraveAdsHost::MaybeTriggerAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
MaybeTriggerAdViewedEventCallback callback) {
|
||||
DCHECK(callback);
|
||||
DCHECK(!creative_instance_id.empty());
|
||||
|
||||
if (!tab_id_.is_valid()) {
|
||||
std::move(callback).Run(/* event_triggered */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
SearchResultAdService* search_result_ad_service =
|
||||
SearchResultAdServiceFactory::GetForProfile(profile_);
|
||||
|
||||
if (!search_result_ad_service) {
|
||||
std::move(callback).Run(/* event_triggered */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
search_result_ad_service->MaybeTriggerSearchResultAdViewedEvent(
|
||||
creative_instance_id, tab_id_, std::move(callback));
|
||||
}
|
||||
|
||||
void BraveAdsHost::RequestAdsEnabled(RequestAdsEnabledCallback callback) {
|
||||
DCHECK(callback);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef BRAVE_BROWSER_BRAVE_ADS_BRAVE_ADS_HOST_H_
|
||||
#define BRAVE_BROWSER_BRAVE_ADS_BRAVE_ADS_HOST_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
@@ -13,22 +14,29 @@
|
||||
#include "brave/components/brave_ads/common/brave_ads_host.mojom.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service_observer.h"
|
||||
#include "components/sessions/core/session_id.h"
|
||||
|
||||
class Profile;
|
||||
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
// The class handles chrome.braveRequestAdsEnabled() js api call for Desktop
|
||||
// platforms. The js api asks the user for permission to enable ads.
|
||||
// The class handles ads requests from renderer side for Desktop platforms.
|
||||
class BraveAdsHost : public brave_ads::mojom::BraveAdsHost,
|
||||
public brave_rewards::RewardsServiceObserver {
|
||||
public:
|
||||
explicit BraveAdsHost(Profile* profile);
|
||||
BraveAdsHost(Profile* profile, content::WebContents* web_contents);
|
||||
BraveAdsHost(const BraveAdsHost&) = delete;
|
||||
BraveAdsHost& operator=(const BraveAdsHost&) = delete;
|
||||
~BraveAdsHost() override;
|
||||
|
||||
// brave_ads::mojom::BraveAdsHost
|
||||
void MaybeTriggerAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
MaybeTriggerAdViewedEventCallback callback) override;
|
||||
void RequestAdsEnabled(RequestAdsEnabledCallback callback) override;
|
||||
|
||||
// brave_rewards::RewardsServiceObserver
|
||||
@@ -41,6 +49,7 @@ class BraveAdsHost : public brave_ads::mojom::BraveAdsHost,
|
||||
void RunCallbacksAndReset(bool result);
|
||||
|
||||
raw_ptr<Profile> profile_ = nullptr;
|
||||
SessionID tab_id_;
|
||||
std::vector<RequestAdsEnabledCallback> callbacks_;
|
||||
base::ScopedObservation<brave_rewards::RewardsService,
|
||||
brave_rewards::RewardsServiceObserver>
|
||||
|
||||
@@ -10,17 +10,24 @@
|
||||
#include "base/check.h"
|
||||
#include "brave/browser/brave_ads/ads_service_factory.h"
|
||||
#include "brave/browser/brave_ads/android/jni_headers/BraveAdsHostAndroid_jni.h"
|
||||
#include "brave/browser/brave_ads/search_result_ad/search_result_ad_service_factory.h"
|
||||
#include "brave/browser/brave_rewards/rewards_service_factory.h"
|
||||
#include "brave/components/brave_ads/browser/ads_service.h"
|
||||
#include "brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_service.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "components/sessions/content/session_tab_helper.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
using base::android::AttachCurrentThread;
|
||||
using base::android::JavaParamRef;
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
BraveAdsHostAndroid::BraveAdsHostAndroid(Profile* profile) : profile_(profile) {
|
||||
BraveAdsHostAndroid::BraveAdsHostAndroid(Profile* profile,
|
||||
content::WebContents* web_contents)
|
||||
: profile_(profile),
|
||||
tab_id_(sessions::SessionTabHelper::IdForTab(web_contents)) {
|
||||
DCHECK(profile_);
|
||||
|
||||
java_object_.Reset(Java_BraveAdsHostAndroid_create(
|
||||
@@ -31,6 +38,29 @@ BraveAdsHostAndroid::~BraveAdsHostAndroid() {
|
||||
Java_BraveAdsHostAndroid_destroy(AttachCurrentThread(), java_object_);
|
||||
}
|
||||
|
||||
void BraveAdsHostAndroid::MaybeTriggerAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
MaybeTriggerAdViewedEventCallback callback) {
|
||||
DCHECK(callback);
|
||||
DCHECK(!creative_instance_id.empty());
|
||||
|
||||
if (!tab_id_.is_valid()) {
|
||||
std::move(callback).Run(/* event_triggered */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
SearchResultAdService* search_result_ad_service =
|
||||
SearchResultAdServiceFactory::GetForProfile(profile_);
|
||||
|
||||
if (!search_result_ad_service) {
|
||||
std::move(callback).Run(/* event_triggered */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
search_result_ad_service->MaybeTriggerSearchResultAdViewedEvent(
|
||||
creative_instance_id, tab_id_, std::move(callback));
|
||||
}
|
||||
|
||||
void BraveAdsHostAndroid::RequestAdsEnabled(
|
||||
RequestAdsEnabledCallback callback) {
|
||||
const AdsService* ads_service = AdsServiceFactory::GetForProfile(profile_);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef BRAVE_BROWSER_BRAVE_ADS_BRAVE_ADS_HOST_ANDROID_H_
|
||||
#define BRAVE_BROWSER_BRAVE_ADS_BRAVE_ADS_HOST_ANDROID_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/android/jni_android.h"
|
||||
@@ -14,24 +15,29 @@
|
||||
#include "brave/components/brave_ads/common/brave_ads_host.mojom.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service.h"
|
||||
#include "brave/components/brave_rewards/browser/rewards_service_observer.h"
|
||||
#include "components/sessions/core/session_id.h"
|
||||
|
||||
class Profile;
|
||||
|
||||
class Profile;
|
||||
namespace content {
|
||||
class WebContents;
|
||||
}
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
// The class handles chrome.braveRequestAdsEnabled() js api call for Android
|
||||
// platform. The js api asks the user for permission to enable ads.
|
||||
// The class handles ads requests from renderer side for Android platform.
|
||||
class BraveAdsHostAndroid : public brave_ads::mojom::BraveAdsHost,
|
||||
public brave_rewards::RewardsServiceObserver {
|
||||
public:
|
||||
explicit BraveAdsHostAndroid(Profile* profile);
|
||||
BraveAdsHostAndroid(Profile* profile, content::WebContents* web_contents);
|
||||
BraveAdsHostAndroid(const BraveAdsHostAndroid&) = delete;
|
||||
BraveAdsHostAndroid& operator=(const BraveAdsHostAndroid&) = delete;
|
||||
~BraveAdsHostAndroid() override;
|
||||
|
||||
// brave_ads::mojom::BraveAdsHost
|
||||
void MaybeTriggerAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
MaybeTriggerAdViewedEventCallback callback) override;
|
||||
void RequestAdsEnabled(RequestAdsEnabledCallback callback) override;
|
||||
|
||||
// brave_rewards::RewardsServiceObserver
|
||||
@@ -47,6 +53,7 @@ class BraveAdsHostAndroid : public brave_ads::mojom::BraveAdsHost,
|
||||
void RunCallbacksAndReset(bool ads_enabled);
|
||||
|
||||
raw_ptr<Profile> profile_ = nullptr;
|
||||
SessionID tab_id_;
|
||||
base::android::ScopedJavaGlobalRef<jobject> java_object_;
|
||||
std::vector<RequestAdsEnabledCallback> callbacks_;
|
||||
base::ScopedObservation<brave_rewards::RewardsService,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "brave/components/brave_ads/browser/ads_service.h"
|
||||
#include "brave/components/brave_ads/browser/mock_ads_service.h"
|
||||
#include "brave/components/brave_ads/common/features.h"
|
||||
#include "brave/components/brave_ads/common/search_result_ad_util.h"
|
||||
#include "brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_service.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
@@ -20,6 +21,7 @@
|
||||
#include "content/public/test/browser_test.h"
|
||||
#include "content/public/test/browser_test_utils.h"
|
||||
#include "content/public/test/content_mock_cert_verifier.h"
|
||||
#include "content/public/test/url_loader_interceptor.h"
|
||||
#include "net/dns/mock_host_resolver.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
@@ -98,6 +100,49 @@ class ScopedTestingAdsServiceSetter {
|
||||
raw_ptr<brave_ads::AdsService> previous_ads_service_ = nullptr;
|
||||
};
|
||||
|
||||
class SentViewedEventsWaiter final {
|
||||
public:
|
||||
explicit SentViewedEventsWaiter(
|
||||
std::vector<std::string> creative_instance_ids) {
|
||||
sent_viewed_creative_instance_ids_ = std::move(creative_instance_ids);
|
||||
|
||||
url_loader_interceptor_ =
|
||||
std::make_unique<content::URLLoaderInterceptor>(base::BindRepeating(
|
||||
&SentViewedEventsWaiter::URLLoaderInterceptorCallback,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
~SentViewedEventsWaiter() = default;
|
||||
|
||||
bool URLLoaderInterceptorCallback(
|
||||
content::URLLoaderInterceptor::RequestParams* params) {
|
||||
const std::string creative_instance_id =
|
||||
brave_ads::GetCreativeInstanceIdFromSearchAdsViewedUrl(
|
||||
params->url_request.url);
|
||||
if (!creative_instance_id.empty()) {
|
||||
auto it = std::find(sent_viewed_creative_instance_ids_.begin(),
|
||||
sent_viewed_creative_instance_ids_.end(),
|
||||
creative_instance_id);
|
||||
EXPECT_TRUE(it != sent_viewed_creative_instance_ids_.end())
|
||||
<< "Not expected creative instance id: " << creative_instance_id;
|
||||
if (it != sent_viewed_creative_instance_ids_.end()) {
|
||||
sent_viewed_creative_instance_ids_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
if (sent_viewed_creative_instance_ids_.empty()) {
|
||||
run_loop_.Quit();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void WaitForViewedEvents() { run_loop_.Run(); }
|
||||
|
||||
private:
|
||||
base::RunLoop run_loop_;
|
||||
std::vector<std::string> sent_viewed_creative_instance_ids_;
|
||||
std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class SearchResultAdTest : public InProcessBrowserTest {
|
||||
@@ -163,9 +208,9 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, SampleSearchAdMetadata) {
|
||||
brave_ads::MockAdsService ads_service;
|
||||
ScopedTestingAdsServiceSetter scoped_setter(profile(), &ads_service);
|
||||
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(true));
|
||||
brave_ads::TriggerSearchResultAdEventCallback trigger_callback;
|
||||
auto run_loop = std::make_unique<base::RunLoop>();
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(true));
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _))
|
||||
.WillOnce([&run_loop, &trigger_callback](
|
||||
ads::mojom::SearchResultAdPtr ad_mojom,
|
||||
@@ -176,10 +221,12 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, SampleSearchAdMetadata) {
|
||||
run_loop->Quit();
|
||||
});
|
||||
|
||||
SentViewedEventsWaiter sent_viewed_events_waiter(
|
||||
{"data-creative-instance-id-1", "not-existant"});
|
||||
LoadTestDataUrl(kAllowedDomain, "/brave_ads/search_result_ad_sample.html");
|
||||
|
||||
run_loop->Run();
|
||||
Mock::VerifyAndClearExpectations(&ads_service);
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
run_loop = std::make_unique<base::RunLoop>();
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _))
|
||||
@@ -192,51 +239,27 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, SampleSearchAdMetadata) {
|
||||
run_loop->Quit();
|
||||
});
|
||||
|
||||
// Continue to trigger ad viewed events even if one of them failed.
|
||||
std::move(trigger_callback)
|
||||
.Run(true, "placement-id-1",
|
||||
.Run(false, "placement-id-1",
|
||||
ads::mojom::SearchResultAdEventType::kViewed);
|
||||
run_loop->Run();
|
||||
Mock::VerifyAndClearExpectations(&ads_service);
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _)).Times(0);
|
||||
std::move(trigger_callback)
|
||||
.Run(true, "placement-id-2",
|
||||
ads::mojom::SearchResultAdEventType::kViewed);
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SearchResultAdTest, FailedToTriggerSearchResultAdEvent) {
|
||||
brave_ads::MockAdsService ads_service;
|
||||
ScopedTestingAdsServiceSetter scoped_setter(profile(), &ads_service);
|
||||
|
||||
brave_ads::TriggerSearchResultAdEventCallback trigger_callback;
|
||||
auto run_loop = std::make_unique<base::RunLoop>();
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(true));
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _))
|
||||
.WillOnce([&run_loop, &trigger_callback](
|
||||
ads::mojom::SearchResultAdPtr ad_mojom,
|
||||
const ads::mojom::SearchResultAdEventType event_type,
|
||||
brave_ads::TriggerSearchResultAdEventCallback callback) {
|
||||
CheckSampleSearchAdMetadata(ad_mojom, 1);
|
||||
trigger_callback = std::move(callback);
|
||||
run_loop->Quit();
|
||||
});
|
||||
|
||||
LoadTestDataUrl(kAllowedDomain, "/brave_ads/search_result_ad_sample.html");
|
||||
|
||||
run_loop->Run();
|
||||
Mock::VerifyAndClearExpectations(&ads_service);
|
||||
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _)).Times(0);
|
||||
std::move(trigger_callback)
|
||||
.Run(false, "placement-id-1",
|
||||
ads::mojom::SearchResultAdEventType::kViewed);
|
||||
sent_viewed_events_waiter.WaitForViewedEvents();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SearchResultAdTest, AdsDisabled) {
|
||||
brave_ads::MockAdsService ads_service;
|
||||
ScopedTestingAdsServiceSetter scoped_setter(profile(), &ads_service);
|
||||
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(false));
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(false));
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _)).Times(0);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
@@ -247,16 +270,20 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, AdsDisabled) {
|
||||
},
|
||||
run_loop.QuitClosure()));
|
||||
|
||||
SentViewedEventsWaiter sent_viewed_events_waiter(
|
||||
{"data-creative-instance-id-1", "data-creative-instance-id-1",
|
||||
"data-creative-instance-id-2", "not-existant"});
|
||||
LoadTestDataUrl(kAllowedDomain, "/brave_ads/search_result_ad_sample.html");
|
||||
|
||||
run_loop.Run();
|
||||
sent_viewed_events_waiter.WaitForViewedEvents();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SearchResultAdTest, NotAllowedDomain) {
|
||||
brave_ads::MockAdsService ads_service;
|
||||
ScopedTestingAdsServiceSetter scoped_setter(profile(), &ads_service);
|
||||
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(true));
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _)).Times(0);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
@@ -267,16 +294,20 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, NotAllowedDomain) {
|
||||
},
|
||||
run_loop.QuitClosure()));
|
||||
|
||||
SentViewedEventsWaiter sent_viewed_events_waiter(
|
||||
{"data-creative-instance-id-1", "data-creative-instance-id-1",
|
||||
"data-creative-instance-id-2", "not-existant"});
|
||||
LoadTestDataUrl(kNotAllowedDomain, "/brave_ads/search_result_ad_sample.html");
|
||||
|
||||
run_loop.Run();
|
||||
sent_viewed_events_waiter.WaitForViewedEvents();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SearchResultAdTest, NoSearchAdMetadata) {
|
||||
brave_ads::MockAdsService ads_service;
|
||||
ScopedTestingAdsServiceSetter scoped_setter(profile(), &ads_service);
|
||||
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(true));
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _)).Times(0);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
@@ -296,7 +327,7 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, BrokenSearchAdMetadata) {
|
||||
brave_ads::MockAdsService ads_service;
|
||||
ScopedTestingAdsServiceSetter scoped_setter(profile(), &ads_service);
|
||||
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillOnce(Return(true));
|
||||
EXPECT_CALL(ads_service, IsEnabled()).WillRepeatedly(Return(true));
|
||||
EXPECT_CALL(ads_service, TriggerSearchResultAdEvent(_, _, _)).Times(0);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
@@ -307,7 +338,28 @@ IN_PROC_BROWSER_TEST_F(SearchResultAdTest, BrokenSearchAdMetadata) {
|
||||
},
|
||||
run_loop.QuitClosure()));
|
||||
|
||||
SentViewedEventsWaiter sent_viewed_events_waiter(
|
||||
{"data-creative-instance-id-1"});
|
||||
LoadTestDataUrl(kAllowedDomain, "/brave_ads/search_result_ad_broken.html");
|
||||
|
||||
run_loop.Run();
|
||||
sent_viewed_events_waiter.WaitForViewedEvents();
|
||||
}
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(SearchResultAdTest, IncognitoBrowser) {
|
||||
SentViewedEventsWaiter sent_viewed_events_waiter(
|
||||
{"data-creative-instance-id-1", "data-creative-instance-id-1",
|
||||
"data-creative-instance-id-2", "not-existant"});
|
||||
|
||||
GURL url = https_server()->GetURL(kAllowedDomain,
|
||||
"/brave_ads/search_result_ad_sample.html");
|
||||
Browser* incognito_browser = OpenURLOffTheRecord(browser()->profile(), url);
|
||||
EXPECT_FALSE(GetSearchResultAdService(incognito_browser->profile()));
|
||||
|
||||
content::WebContents* web_contents =
|
||||
incognito_browser->tab_strip_model()->GetActiveWebContents();
|
||||
EXPECT_TRUE(content::NavigateToURL(web_contents, url));
|
||||
EXPECT_EQ(url, web_contents->GetVisibleURL());
|
||||
|
||||
sent_viewed_events_waiter.WaitForViewedEvents();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/* 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_ads/content/browser/search_result_ad/search_result_ad_service.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "brave/components/brave_ads/browser/mock_ads_service.h"
|
||||
#include "brave/components/brave_ads/common/features.h"
|
||||
#include "chrome/test/base/chrome_render_view_host_test_harness.h"
|
||||
#include "components/sessions/core/session_id.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
using testing::Return;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kAllowedDomain[] = "https://search.brave.com";
|
||||
constexpr char kNotAllowedDomain[] = "https://brave.com";
|
||||
|
||||
class SearchResultAdServiceTest : public ChromeRenderViewHostTestHarness {
|
||||
public:
|
||||
SearchResultAdServiceTest() {
|
||||
feature_list_.InitAndEnableFeature(
|
||||
brave_ads::features::kSupportBraveSearchResultAdConfirmationEvents);
|
||||
search_result_ad_service_ =
|
||||
std::make_unique<brave_ads::SearchResultAdService>(&ads_service_);
|
||||
}
|
||||
~SearchResultAdServiceTest() override = default;
|
||||
|
||||
SearchResultAdServiceTest(const SearchResultAdServiceTest&) = delete;
|
||||
SearchResultAdServiceTest& operator=(const SearchResultAdServiceTest&) =
|
||||
delete;
|
||||
|
||||
protected:
|
||||
brave_ads::MockAdsService ads_service_;
|
||||
std::unique_ptr<brave_ads::SearchResultAdService> search_result_ad_service_;
|
||||
base::test::ScopedFeatureList feature_list_;
|
||||
};
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, BraveAdsDisabledTryTriggerAd) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillOnce(Return(false));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
NavigateAndCommit(GURL(kAllowedDomain));
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, NotAllowedDomainTryTriggerAdBeforeRetrieve) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
NavigateAndCommit(GURL(kNotAllowedDomain));
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
search_result_ad_service_->MaybeRetrieveSearchResultAd(main_rfh(), session_id,
|
||||
true);
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, NotAllowedDomainTryTriggerAdAfterRetrieve) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
NavigateAndCommit(GURL(kNotAllowedDomain));
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
search_result_ad_service_->MaybeRetrieveSearchResultAd(main_rfh(), session_id,
|
||||
true);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, TabRestoredTryTriggerAdBeforeRetrieve) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
NavigateAndCommit(GURL(kAllowedDomain));
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
search_result_ad_service_->MaybeRetrieveSearchResultAd(main_rfh(), session_id,
|
||||
false);
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, TabRestoredTryTriggerAdAfterRetrieve) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
NavigateAndCommit(GURL(kAllowedDomain));
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
search_result_ad_service_->MaybeRetrieveSearchResultAd(main_rfh(), session_id,
|
||||
false);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, UnknownTabTryTriggerAd) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, TryTriggerAdRepeatedNavigation) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
// Simulate navigation was finished.
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
// Simulate navigation.
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, TryTriggerAdBeforeTabDeleted) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
// Simulate navigation was finished.
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
// Simulate tab deletion before search result JSON-ln loading.
|
||||
search_result_ad_service_->OnTabClosed(session_id);
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
TEST_F(SearchResultAdServiceTest, TryTriggerAdAfterTabDeleted) {
|
||||
EXPECT_CALL(ads_service_, IsEnabled()).WillRepeatedly(Return(true));
|
||||
|
||||
SessionID session_id = SessionID::NewUnique();
|
||||
// Simulate navigation was finished.
|
||||
search_result_ad_service_->OnDidFinishNavigation(session_id);
|
||||
// Simulate tab deletion before search result JSON-ln loading.
|
||||
search_result_ad_service_->OnTabClosed(session_id);
|
||||
|
||||
base::RunLoop run_loop;
|
||||
search_result_ad_service_->MaybeTriggerSearchResultAdViewedEvent(
|
||||
"creative_instance_id", session_id,
|
||||
base::BindOnce(
|
||||
[](base::RunLoop* run_loop, bool ad_was_triggered) {
|
||||
EXPECT_FALSE(ad_was_triggered);
|
||||
run_loop->Quit();
|
||||
},
|
||||
base::Unretained(&run_loop)));
|
||||
|
||||
run_loop.Run();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -240,6 +240,8 @@ void BindBraveAdsHost(
|
||||
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
auto* context = frame_host->GetBrowserContext();
|
||||
auto* profile = Profile::FromBrowserContext(context);
|
||||
content::WebContents* web_contents =
|
||||
content::WebContents::FromRenderFrameHost(frame_host);
|
||||
|
||||
mojo::MakeSelfOwnedReceiver(
|
||||
#if BUILDFLAG(IS_ANDROID)
|
||||
@@ -247,7 +249,7 @@ void BindBraveAdsHost(
|
||||
#elif BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
std::make_unique<brave_ads::BraveAdsHost>(
|
||||
#endif // BUILDFLAG(IS_ANDROID)
|
||||
profile),
|
||||
profile, web_contents),
|
||||
std::move(receiver));
|
||||
#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(ENABLE_EXTENSIONS)
|
||||
}
|
||||
@@ -497,7 +499,9 @@ void BraveContentBrowserClient::RegisterBrowserInterfaceBindersForFrame(
|
||||
base::BindRepeating(&BindBraveSearchDefaultHost));
|
||||
}
|
||||
|
||||
if (brave_ads::features::IsRequestAdsEnabledApiEnabled()) {
|
||||
if (brave_ads::features::IsRequestAdsEnabledApiEnabled() ||
|
||||
base::FeatureList::IsEnabled(
|
||||
brave_ads::features::kSupportBraveSearchResultAdConfirmationEvents)) {
|
||||
map->Add<brave_ads::mojom::BraveAdsHost>(
|
||||
base::BindRepeating(&BindBraveAdsHost));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
include_rules = [
|
||||
"+brave/components/brave_shields/common",
|
||||
"+brave/components/content_settings/renderer",
|
||||
"+brave/renderer",
|
||||
"+media/base/key_system_properties.h",
|
||||
"+third_party/widevine/cdm/buildflags.h",
|
||||
]
|
||||
|
||||
@@ -4,5 +4,11 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "brave/components/content_settings/renderer/brave_content_settings_agent_impl.h"
|
||||
#include "brave/renderer/brave_url_loader_throttle_provider.h"
|
||||
#include "chrome/renderer/url_loader_throttle_provider_impl.h"
|
||||
|
||||
#define URLLoaderThrottleProviderImpl BraveURLLoaderThrottleProvider
|
||||
|
||||
#include "src/chrome/renderer/chrome_content_renderer_client.cc"
|
||||
|
||||
#undef URLLoaderThrottleProviderImpl
|
||||
|
||||
@@ -478,6 +478,8 @@ bool ParseJSON(base::StringPiece json,
|
||||
{ "name": "payment.rewards.bravesoftware.com", "policy": "custom", "mode": "force-https", "pins": "brave"},
|
||||
{ "name": "payment.rewards.brave.software", "policy": "custom", "mode": "force-https", "pins": "brave"},
|
||||
{ "name": "rewards.brave.com", "mode": "force-https", "policy": "custom", "pins": "brave"},
|
||||
{ "name": "search.anonymous.brave.com", "mode": "force-https", "policy": "custom", "pins": "brave"},
|
||||
{ "name": "search.anonymous.bravesoftware.com", "mode": "force-https", "policy": "custom", "pins": "brave"},
|
||||
{ "name": "static.ads.brave.com", "mode": "force-https", "policy": "custom", "pins": "brave"},
|
||||
{ "name": "static.ads.bravesoftware.com", "mode": "force-https", "policy": "custom", "pins": "brave"},
|
||||
|
||||
|
||||
@@ -12,9 +12,14 @@ static_library("common") {
|
||||
"features.h",
|
||||
"pref_names.cc",
|
||||
"pref_names.h",
|
||||
"search_result_ad_util.cc",
|
||||
"search_result_ad_util.h",
|
||||
"switches.cc",
|
||||
"switches.h",
|
||||
]
|
||||
|
||||
deps = [ "//base" ]
|
||||
deps = [
|
||||
"//base",
|
||||
"//url",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
module brave_ads.mojom;
|
||||
|
||||
interface BraveAdsHost {
|
||||
MaybeTriggerAdViewedEvent(string creative_instance_id) => (bool event_triggered);
|
||||
|
||||
RequestAdsEnabled() => (bool result);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/* Copyright 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_ads/common/search_result_ad_util.h"
|
||||
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "url/gurl.h"
|
||||
#include "url/third_party/mozilla/url_parse.h"
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto kSearchAdsConfirmationVettedHosts =
|
||||
base::MakeFixedFlatSet<base::StringPiece>(
|
||||
{"search.anonymous.brave.com", "search.anonymous.bravesoftware.com"});
|
||||
constexpr char kSearchAdsViewedPath[] = "/v10/view";
|
||||
constexpr char kCreativeInstanceIdParameterName[] = "creativeInstanceId";
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string GetCreativeInstanceIdFromSearchAdsViewedUrl(const GURL& url) {
|
||||
if (!url.is_valid() || !url.SchemeIs(url::kHttpsScheme) ||
|
||||
url.path_piece() != kSearchAdsViewedPath || !url.has_query()) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (!base::Contains(kSearchAdsConfirmationVettedHosts, url.host_piece())) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
base::StringPiece query_str = url.query_piece();
|
||||
url::Component query(0, static_cast<int>(query_str.length())), key, value;
|
||||
while (url::ExtractQueryKeyValue(query_str.data(), &query, &key, &value)) {
|
||||
base::StringPiece key_str = query_str.substr(key.begin, key.len);
|
||||
if (key_str == kCreativeInstanceIdParameterName) {
|
||||
base::StringPiece value_str = query_str.substr(value.begin, value.len);
|
||||
return static_cast<std::string>(value_str);
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
} // namespace brave_ads
|
||||
@@ -0,0 +1,19 @@
|
||||
/* Copyright 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/. */
|
||||
|
||||
#ifndef BRAVE_COMPONENTS_BRAVE_ADS_COMMON_SEARCH_RESULT_AD_UTIL_H_
|
||||
#define BRAVE_COMPONENTS_BRAVE_ADS_COMMON_SEARCH_RESULT_AD_UTIL_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
class GURL;
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
std::string GetCreativeInstanceIdFromSearchAdsViewedUrl(const GURL& url);
|
||||
|
||||
} // namespace brave_ads
|
||||
|
||||
#endif // BRAVE_COMPONENTS_BRAVE_ADS_COMMON_SEARCH_RESULT_AD_UTIL_H_
|
||||
@@ -1,3 +1,8 @@
|
||||
# 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/.
|
||||
|
||||
source_set("search_result_ad") {
|
||||
sources = [
|
||||
"search_result_ad_parsing.cc",
|
||||
@@ -12,6 +17,7 @@ source_set("search_result_ad") {
|
||||
"//brave/components/brave_ads/common",
|
||||
"//brave/components/brave_search/common",
|
||||
"//components/schema_org/common:mojom",
|
||||
"//components/sessions",
|
||||
"//content/public/browser",
|
||||
"//mojo/public/cpp/bindings",
|
||||
"//services/service_manager/public/cpp",
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include "brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_parsing.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/containers/flat_set.h"
|
||||
@@ -23,7 +23,7 @@ namespace brave_ads {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kSearchResultAdsListType[] = "Product";
|
||||
constexpr char kProductType[] = "Product";
|
||||
constexpr char kSearchResultAdType[] = "SearchResultAd";
|
||||
|
||||
constexpr char kContextPropertyName[] = "@context";
|
||||
@@ -166,12 +166,12 @@ bool SetSearchAdProperty(const schema_org::mojom::PropertyPtr& ad_property,
|
||||
return false;
|
||||
}
|
||||
|
||||
absl::optional<SearchResultAdsList> ParseSearchResultAdsListEntityProperties(
|
||||
absl::optional<SearchResultAdMap> ParseSearchResultAdMapEntityProperties(
|
||||
const schema_org::mojom::EntityPtr& entity) {
|
||||
DCHECK(entity);
|
||||
DCHECK_EQ(entity->type, kSearchResultAdsListType);
|
||||
DCHECK_EQ(entity->type, kProductType);
|
||||
|
||||
SearchResultAdsList search_result_ads_list;
|
||||
SearchResultAdMap search_result_ads;
|
||||
|
||||
for (const auto& property : entity->properties) {
|
||||
if (!property || property->name == kContextPropertyName ||
|
||||
@@ -188,14 +188,14 @@ absl::optional<SearchResultAdsList> ParseSearchResultAdsListEntityProperties(
|
||||
if (!property->values->is_entity_values() ||
|
||||
property->values->get_entity_values().empty()) {
|
||||
LOG(ERROR) << "Search result ad attributes list is empty";
|
||||
return SearchResultAdsList();
|
||||
return SearchResultAdMap();
|
||||
}
|
||||
|
||||
for (const auto& ad_entity : property->values->get_entity_values()) {
|
||||
if (!ad_entity || ad_entity->type != kSearchResultAdType) {
|
||||
LOG(ERROR) << "Wrong search result ad type specified: "
|
||||
<< ad_entity->type;
|
||||
return SearchResultAdsList();
|
||||
return SearchResultAdMap();
|
||||
}
|
||||
|
||||
if (property->name == kTypePropertyName) {
|
||||
@@ -215,14 +215,14 @@ absl::optional<SearchResultAdsList> ParseSearchResultAdsListEntityProperties(
|
||||
if (it == kSearchResultAdAttributes.end()) {
|
||||
LOG(ERROR) << "Wrong search result ad attribute specified: "
|
||||
<< ad_property->name;
|
||||
return SearchResultAdsList();
|
||||
return SearchResultAdMap();
|
||||
}
|
||||
found_attributes.insert(*it);
|
||||
|
||||
if (!SetSearchAdProperty(ad_property, search_result_ad.get())) {
|
||||
LOG(ERROR) << "Cannot read search result ad attribute value: "
|
||||
<< ad_property->name;
|
||||
return SearchResultAdsList();
|
||||
return SearchResultAdMap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,31 +237,35 @@ absl::optional<SearchResultAdsList> ParseSearchResultAdsListEntityProperties(
|
||||
LOG(ERROR) << "Some of search result ad attributes were not specified: "
|
||||
<< base::JoinString(absent_attributes, ", ");
|
||||
|
||||
return SearchResultAdsList();
|
||||
return SearchResultAdMap();
|
||||
}
|
||||
|
||||
search_result_ads_list.push_back(std::move(search_result_ad));
|
||||
const std::string creative_instance_id =
|
||||
search_result_ad->creative_instance_id;
|
||||
search_result_ads.emplace(creative_instance_id,
|
||||
std::move(search_result_ad));
|
||||
}
|
||||
|
||||
// Creatives has been parsed.
|
||||
break;
|
||||
}
|
||||
|
||||
return search_result_ads_list;
|
||||
return search_result_ads;
|
||||
}
|
||||
|
||||
void LogSearchResultAdsList(const SearchResultAdsList& search_result_ads_list) {
|
||||
void LogSearchResultAdMap(const SearchResultAdMap& search_result_ads) {
|
||||
if (!VLOG_IS_ON(1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (search_result_ads_list.empty()) {
|
||||
if (search_result_ads.empty()) {
|
||||
VLOG(1) << "Parsed search result ads list is empty.";
|
||||
return;
|
||||
}
|
||||
|
||||
VLOG(1) << "Parsed search result ads list:";
|
||||
for (const auto& search_result_ad : search_result_ads_list) {
|
||||
for (const auto& search_result_ad_pair : search_result_ads) {
|
||||
const auto& search_result_ad = search_result_ad_pair.second;
|
||||
VLOG(1) << "Ad with \"" << kDataPlacementId
|
||||
<< "\": " << search_result_ad->placement_id;
|
||||
VLOG(1) << " \"" << kDataCreativeInstanceId
|
||||
@@ -292,26 +296,24 @@ void LogSearchResultAdsList(const SearchResultAdsList& search_result_ads_list) {
|
||||
|
||||
} // namespace
|
||||
|
||||
SearchResultAdsList ParseWebPageEntities(blink::mojom::WebPagePtr web_page) {
|
||||
DCHECK(web_page);
|
||||
|
||||
SearchResultAdMap ParseWebPageEntities(blink::mojom::WebPagePtr web_page) {
|
||||
for (const auto& entity : web_page->entities) {
|
||||
if (entity->type != kSearchResultAdsListType) {
|
||||
if (entity->type != kProductType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
absl::optional<SearchResultAdsList> search_result_ads_list =
|
||||
ParseSearchResultAdsListEntityProperties(entity);
|
||||
absl::optional<SearchResultAdMap> search_result_ads =
|
||||
ParseSearchResultAdMapEntityProperties(entity);
|
||||
|
||||
if (search_result_ads_list) {
|
||||
LogSearchResultAdsList(*search_result_ads_list);
|
||||
return std::move(*search_result_ads_list);
|
||||
if (search_result_ads) {
|
||||
LogSearchResultAdMap(*search_result_ads);
|
||||
return std::move(*search_result_ads);
|
||||
}
|
||||
}
|
||||
|
||||
VLOG(1) << "No search result ad found.";
|
||||
|
||||
return SearchResultAdsList();
|
||||
return SearchResultAdMap();
|
||||
}
|
||||
|
||||
} // namespace brave_ads
|
||||
|
||||
@@ -6,16 +6,17 @@
|
||||
#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CONTENT_BROWSER_SEARCH_RESULT_AD_SEARCH_RESULT_AD_PARSING_H_
|
||||
#define BRAVE_COMPONENTS_BRAVE_ADS_CONTENT_BROWSER_SEARCH_RESULT_AD_SEARCH_RESULT_AD_PARSING_H_
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "brave/vendor/bat-native-ads/include/bat/ads/public/interfaces/ads.mojom.h"
|
||||
#include "third_party/blink/public/mojom/document_metadata/document_metadata.mojom.h"
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
using SearchResultAdsList = std::vector<ads::mojom::SearchResultAdPtr>;
|
||||
using SearchResultAdMap = std::map<std::string, ads::mojom::SearchResultAdPtr>;
|
||||
|
||||
SearchResultAdsList ParseWebPageEntities(blink::mojom::WebPagePtr web_page);
|
||||
SearchResultAdMap ParseWebPageEntities(blink::mojom::WebPagePtr web_page);
|
||||
|
||||
} // namespace brave_ads
|
||||
|
||||
|
||||
+51
-51
@@ -5,9 +5,10 @@
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "brave/components/brave_ads/content/browser/search_result_ad/search_result_ad_parsing.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
@@ -23,15 +24,15 @@ schema_org::mojom::ValuesPtr CreateVectorValuesPtr(int64_t value) {
|
||||
return schema_org::mojom::Values::NewLongValues({value});
|
||||
}
|
||||
|
||||
class WebPageConstructor final {
|
||||
class TestWebPageConstructor final {
|
||||
public:
|
||||
explicit WebPageConstructor(int attribute_index_to_skip = -1)
|
||||
explicit TestWebPageConstructor(int attribute_index_to_skip = -1)
|
||||
: attribute_index_to_skip_(attribute_index_to_skip) {
|
||||
web_page_ = CreateWebPage();
|
||||
}
|
||||
~WebPageConstructor() = default;
|
||||
WebPageConstructor(const WebPageConstructor&) = delete;
|
||||
WebPageConstructor& operator=(const WebPageConstructor&) = delete;
|
||||
~TestWebPageConstructor() = default;
|
||||
TestWebPageConstructor(const TestWebPageConstructor&) = delete;
|
||||
TestWebPageConstructor& operator=(const TestWebPageConstructor&) = delete;
|
||||
|
||||
blink::mojom::WebPagePtr GetWebPage() { return std::move(web_page_); }
|
||||
|
||||
@@ -56,10 +57,9 @@ class WebPageConstructor final {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void AddStringProperty(
|
||||
std::vector<schema_org::mojom::PropertyPtr>* properties,
|
||||
base::StringPiece name,
|
||||
T value) {
|
||||
void AddProperty(std::vector<schema_org::mojom::PropertyPtr>* properties,
|
||||
base::StringPiece name,
|
||||
T value) {
|
||||
int index = current_attribute_index_++;
|
||||
if (index == attribute_index_to_skip_) {
|
||||
return;
|
||||
@@ -74,8 +74,8 @@ class WebPageConstructor final {
|
||||
|
||||
schema_org::mojom::EntityPtr CreateCreativeEntity() {
|
||||
const char* kSearchResultAdStringAttributes[] = {
|
||||
"data-placement-id",
|
||||
"data-creative-instance-id",
|
||||
"data-placement-id",
|
||||
"data-creative-set-id",
|
||||
"data-campaign-id",
|
||||
"data-advertiser-id",
|
||||
@@ -88,17 +88,16 @@ class WebPageConstructor final {
|
||||
schema_org::mojom::EntityPtr entity = schema_org::mojom::Entity::New();
|
||||
entity->type = "SearchResultAd";
|
||||
|
||||
AddStringProperty<std::string>(&entity->properties, "data-landing-page",
|
||||
"https://target.url");
|
||||
AddStringProperty<std::string>(&entity->properties, "data-rewards-value",
|
||||
"0.5");
|
||||
AddStringProperty<int64_t>(&entity->properties,
|
||||
"data-conversion-observation-window-value", 1);
|
||||
AddProperty<std::string>(&entity->properties, "data-landing-page",
|
||||
"https://target.url");
|
||||
AddProperty<std::string>(&entity->properties, "data-rewards-value", "0.5");
|
||||
AddProperty<int64_t>(&entity->properties,
|
||||
"data-conversion-observation-window-value", 1);
|
||||
|
||||
int index = 0;
|
||||
for (const auto** it = std::begin(kSearchResultAdStringAttributes);
|
||||
it != std::end(kSearchResultAdStringAttributes); ++it, ++index) {
|
||||
AddStringProperty<std::string>(
|
||||
AddProperty<std::string>(
|
||||
&entity->properties, *it,
|
||||
std::string("value") + base::NumberToString(index));
|
||||
}
|
||||
@@ -112,11 +111,12 @@ class WebPageConstructor final {
|
||||
};
|
||||
|
||||
TEST(SearchResultAdParsingTest, ValidWebPage) {
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
ASSERT_EQ(ads.size(), 1u);
|
||||
ads::mojom::SearchResultAdPtr& search_result_ad = ads[0];
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_EQ(ads.size(), 1u);
|
||||
ads::mojom::SearchResultAdPtr& search_result_ad = ads["value0"];
|
||||
ASSERT_TRUE(search_result_ad.get());
|
||||
|
||||
EXPECT_EQ(search_result_ad->target_url, GURL("https://target.url"));
|
||||
EXPECT_EQ(search_result_ad->value, 0.5);
|
||||
@@ -124,8 +124,8 @@ TEST(SearchResultAdParsingTest, ValidWebPage) {
|
||||
static_cast<size_t>(search_result_ad->conversion->observation_window),
|
||||
1u);
|
||||
|
||||
EXPECT_EQ(search_result_ad->placement_id, "value0");
|
||||
EXPECT_EQ(search_result_ad->creative_instance_id, "value1");
|
||||
EXPECT_EQ(search_result_ad->creative_instance_id, "value0");
|
||||
EXPECT_EQ(search_result_ad->placement_id, "value1");
|
||||
EXPECT_EQ(search_result_ad->creative_set_id, "value2");
|
||||
EXPECT_EQ(search_result_ad->campaign_id, "value3");
|
||||
EXPECT_EQ(search_result_ad->advertiser_id, "value4");
|
||||
@@ -139,67 +139,67 @@ TEST(SearchResultAdParsingTest, ValidWebPage) {
|
||||
TEST(SearchResultAdParsingTest, NotValidWebPage) {
|
||||
{
|
||||
blink::mojom::WebPagePtr web_page = blink::mojom::WebPage::New();
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
web_page->entities[0]->type = "Not-Product";
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
web_page->entities[0]->properties.clear();
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
property->name = "not-creatives";
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
property->values = schema_org::mojom::Values::NewEntityValues({});
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
property->values = schema_org::mojom::Values::NewStringValues({"creative"});
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SearchResultAdParsingTest, NotValidAdEntityExtraProperty) {
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
auto& ad_entity = property->values->get_entity_values()[0];
|
||||
ad_entity->type = "Not-SearchResultAd";
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
WebPageConstructor constructor;
|
||||
TestWebPageConstructor constructor;
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
auto& ad_entity = property->values->get_entity_values()[0];
|
||||
@@ -211,7 +211,7 @@ TEST(SearchResultAdParsingTest, NotValidAdEntityExtraProperty) {
|
||||
schema_org::mojom::Values::NewStringValues({"extra-value"});
|
||||
ad_entity->properties.push_back(std::move(extra_property));
|
||||
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
}
|
||||
@@ -219,9 +219,9 @@ TEST(SearchResultAdParsingTest, NotValidAdEntityExtraProperty) {
|
||||
TEST(SearchResultAdParsingTest, NotValidAdEntityPropertySkipped) {
|
||||
constexpr int kSearchResultAdAttributesCount = 12;
|
||||
for (int index = 0; index < kSearchResultAdAttributesCount; ++index) {
|
||||
WebPageConstructor constructor(index);
|
||||
TestWebPageConstructor constructor(index);
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
}
|
||||
@@ -229,7 +229,7 @@ TEST(SearchResultAdParsingTest, NotValidAdEntityPropertySkipped) {
|
||||
TEST(SearchResultAdParsingTest, NotValidAdEntityWrongPropertyType) {
|
||||
{
|
||||
// Skip "data-landing-page".
|
||||
WebPageConstructor constructor(0);
|
||||
TestWebPageConstructor constructor(0);
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
auto& ad_entity = property->values->get_entity_values()[0];
|
||||
@@ -241,13 +241,13 @@ TEST(SearchResultAdParsingTest, NotValidAdEntityWrongPropertyType) {
|
||||
schema_org::mojom::Values::NewStringValues({"http://target.url"});
|
||||
ad_entity->properties.push_back(std::move(extra_property));
|
||||
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
// Skip "data-rewards-value".
|
||||
WebPageConstructor constructor(1);
|
||||
TestWebPageConstructor constructor(1);
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
auto& ad_entity = property->values->get_entity_values()[0];
|
||||
@@ -259,13 +259,13 @@ TEST(SearchResultAdParsingTest, NotValidAdEntityWrongPropertyType) {
|
||||
schema_org::mojom::Values::NewStringValues({"0-5"});
|
||||
ad_entity->properties.push_back(std::move(extra_property));
|
||||
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
// Skip "data-conversion-observation-window-value".
|
||||
WebPageConstructor constructor(2);
|
||||
TestWebPageConstructor constructor(2);
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
auto& ad_entity = property->values->get_entity_values()[0];
|
||||
@@ -276,24 +276,24 @@ TEST(SearchResultAdParsingTest, NotValidAdEntityWrongPropertyType) {
|
||||
extra_property->values = schema_org::mojom::Values::NewStringValues({"1"});
|
||||
ad_entity->properties.push_back(std::move(extra_property));
|
||||
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
|
||||
{
|
||||
// Skip "data-placement-id".
|
||||
WebPageConstructor constructor(3);
|
||||
// Skip "data-creative-instance-id".
|
||||
TestWebPageConstructor constructor(3);
|
||||
blink::mojom::WebPagePtr web_page = constructor.GetWebPage();
|
||||
auto& property = web_page->entities[0]->properties[0];
|
||||
auto& ad_entity = property->values->get_entity_values()[0];
|
||||
|
||||
schema_org::mojom::PropertyPtr extra_property =
|
||||
schema_org::mojom::Property::New();
|
||||
extra_property->name = "data-placement-id";
|
||||
extra_property->name = "data-creative-instance-id";
|
||||
extra_property->values = schema_org::mojom::Values::NewLongValues({101});
|
||||
ad_entity->properties.push_back(std::move(extra_property));
|
||||
|
||||
SearchResultAdsList ads = ParseWebPageEntities(std::move(web_page));
|
||||
SearchResultAdMap ads = ParseWebPageEntities(std::move(web_page));
|
||||
EXPECT_TRUE(ads.empty());
|
||||
}
|
||||
}
|
||||
|
||||
+148
-28
@@ -8,6 +8,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/containers/contains.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "brave/components/brave_ads/browser/ads_service.h"
|
||||
#include "brave/components/brave_ads/common/features.h"
|
||||
@@ -18,23 +19,40 @@
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
SearchResultAdService::AdViewedEventCallbackInfo::AdViewedEventCallbackInfo() =
|
||||
default;
|
||||
SearchResultAdService::AdViewedEventCallbackInfo::AdViewedEventCallbackInfo(
|
||||
AdViewedEventCallbackInfo&& info) = default;
|
||||
SearchResultAdService::AdViewedEventCallbackInfo&
|
||||
SearchResultAdService::AdViewedEventCallbackInfo::operator=(
|
||||
AdViewedEventCallbackInfo&& info) = default;
|
||||
SearchResultAdService::AdViewedEventCallbackInfo::~AdViewedEventCallbackInfo() =
|
||||
default;
|
||||
|
||||
SearchResultAdService::SearchResultAdService(AdsService* ads_service)
|
||||
: ads_service_(ads_service) {}
|
||||
: ads_service_(ads_service) {
|
||||
DCHECK(ads_service_);
|
||||
}
|
||||
|
||||
SearchResultAdService::~SearchResultAdService() = default;
|
||||
|
||||
void SearchResultAdService::MaybeRetrieveSearchResultAd(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
content::RenderFrameHost* render_frame_host,
|
||||
SessionID tab_id,
|
||||
bool should_trigger_viewed_event) {
|
||||
DCHECK(ads_service_);
|
||||
DCHECK(render_frame_host);
|
||||
DCHECK(tab_id.is_valid());
|
||||
|
||||
if (!ads_service_->IsEnabled() ||
|
||||
if (!should_trigger_viewed_event || !ads_service_->IsEnabled() ||
|
||||
!base::FeatureList::IsEnabled(
|
||||
features::kSupportBraveSearchResultAdConfirmationEvents) ||
|
||||
!brave_search::IsAllowedHost(render_frame_host->GetLastCommittedURL())) {
|
||||
if (metadata_request_finished_callback_for_testing_) {
|
||||
std::move(metadata_request_finished_callback_for_testing_).Run();
|
||||
}
|
||||
search_result_ads_[tab_id] = SearchResultAdMap();
|
||||
RunAdViewedEventPendingCallbacks(tab_id, /* ads_fetched */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,12 +60,61 @@ void SearchResultAdService::MaybeRetrieveSearchResultAd(
|
||||
render_frame_host->GetRemoteInterfaces()->GetInterface(
|
||||
document_metadata.BindNewPipeAndPassReceiver());
|
||||
DCHECK(document_metadata.is_bound());
|
||||
document_metadata.reset_on_disconnect();
|
||||
|
||||
blink::mojom::DocumentMetadata* raw_document_metadata =
|
||||
document_metadata.get();
|
||||
raw_document_metadata->GetEntities(
|
||||
base::BindOnce(&SearchResultAdService::OnRetrieveSearchResultAdEntities,
|
||||
weak_factory_.GetWeakPtr(), std::move(document_metadata)));
|
||||
raw_document_metadata->GetEntities(base::BindOnce(
|
||||
&SearchResultAdService::OnRetrieveSearchResultAdEntities,
|
||||
weak_factory_.GetWeakPtr(), std::move(document_metadata), tab_id));
|
||||
}
|
||||
|
||||
void SearchResultAdService::OnDidFinishNavigation(SessionID tab_id) {
|
||||
// Clear the tab state from the previous load.
|
||||
ResetState(tab_id);
|
||||
// Now ad viewed events callbacks will be cached before search result JSON-ld
|
||||
// is loaded and processed.
|
||||
ad_viewed_event_pending_callbacks_[tab_id] =
|
||||
std::vector<AdViewedEventCallbackInfo>();
|
||||
}
|
||||
|
||||
void SearchResultAdService::OnTabClosed(SessionID tab_id) {
|
||||
// Clear the tab state in memory.
|
||||
ResetState(tab_id);
|
||||
}
|
||||
|
||||
void SearchResultAdService::MaybeTriggerSearchResultAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
SessionID tab_id,
|
||||
base::OnceCallback<void(bool)> callback) {
|
||||
DCHECK(ads_service_);
|
||||
DCHECK(!creative_instance_id.empty());
|
||||
DCHECK(tab_id.is_valid());
|
||||
|
||||
if (!ads_service_->IsEnabled()) {
|
||||
std::move(callback).Run(/* event_triggered */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if search result ad JSON-LD wasn't processed yet.
|
||||
if (!base::Contains(search_result_ads_, tab_id)) {
|
||||
// Check if OnDidFinishNavigation was called for tab_id.
|
||||
if (!base::Contains(ad_viewed_event_pending_callbacks_, tab_id)) {
|
||||
std::move(callback).Run(/* event_triggered */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
AdViewedEventCallbackInfo callback_info;
|
||||
callback_info.creative_instance_id = creative_instance_id;
|
||||
callback_info.callback = std::move(callback);
|
||||
ad_viewed_event_pending_callbacks_[tab_id].push_back(
|
||||
std::move(callback_info));
|
||||
return;
|
||||
}
|
||||
|
||||
const bool event_triggered =
|
||||
QueueSearchResultAdViewedEvent(creative_instance_id, tab_id);
|
||||
std::move(callback).Run(event_triggered);
|
||||
}
|
||||
|
||||
void SearchResultAdService::SetMetadataRequestFinishedCallbackForTesting(
|
||||
@@ -62,55 +129,108 @@ AdsService* SearchResultAdService::SetAdsServiceForTesting(
|
||||
return previous_ads_service;
|
||||
}
|
||||
|
||||
void SearchResultAdService::ResetState(SessionID tab_id) {
|
||||
DCHECK(tab_id.is_valid());
|
||||
|
||||
for (auto& callback_info : ad_viewed_event_pending_callbacks_[tab_id]) {
|
||||
std::move(callback_info.callback).Run(false);
|
||||
}
|
||||
ad_viewed_event_pending_callbacks_.erase(tab_id);
|
||||
search_result_ads_.erase(tab_id);
|
||||
}
|
||||
|
||||
void SearchResultAdService::OnRetrieveSearchResultAdEntities(
|
||||
mojo::Remote<blink::mojom::DocumentMetadata> document_metadata,
|
||||
SessionID tab_id,
|
||||
blink::mojom::WebPagePtr web_page) {
|
||||
if (metadata_request_finished_callback_for_testing_) {
|
||||
std::move(metadata_request_finished_callback_for_testing_).Run();
|
||||
}
|
||||
|
||||
if (!web_page) {
|
||||
search_result_ads_[tab_id] = SearchResultAdMap();
|
||||
RunAdViewedEventPendingCallbacks(tab_id, /* ads_fetched */ false);
|
||||
return;
|
||||
}
|
||||
|
||||
SearchResultAdsList search_result_ads =
|
||||
SearchResultAdMap search_result_ads =
|
||||
ParseWebPageEntities(std::move(web_page));
|
||||
|
||||
std::reverse(search_result_ads.begin(), search_result_ads.end());
|
||||
TriggerSearchResultAdViewedEvents(std::move(search_result_ads));
|
||||
search_result_ads_.emplace(tab_id, std::move(search_result_ads));
|
||||
|
||||
RunAdViewedEventPendingCallbacks(tab_id, /* ads_fetched */ true);
|
||||
}
|
||||
|
||||
void SearchResultAdService::TriggerSearchResultAdViewedEvents(
|
||||
SearchResultAdsList search_result_ads) {
|
||||
DCHECK(ads_service_);
|
||||
if (search_result_ads.empty()) {
|
||||
return;
|
||||
void SearchResultAdService::RunAdViewedEventPendingCallbacks(SessionID tab_id,
|
||||
bool ads_fetched) {
|
||||
for (auto& callback_info : ad_viewed_event_pending_callbacks_[tab_id]) {
|
||||
bool event_triggered = false;
|
||||
if (ads_fetched) {
|
||||
event_triggered = QueueSearchResultAdViewedEvent(
|
||||
callback_info.creative_instance_id, tab_id);
|
||||
}
|
||||
if (event_triggered) {
|
||||
VLOG(1) << "Triggered search result ad viewed event for "
|
||||
<< callback_info.creative_instance_id;
|
||||
} else {
|
||||
VLOG(1) << "Failed to trigger search result ad viewed event for "
|
||||
<< callback_info.creative_instance_id;
|
||||
}
|
||||
std::move(callback_info.callback).Run(event_triggered);
|
||||
}
|
||||
ad_viewed_event_pending_callbacks_.erase(tab_id);
|
||||
}
|
||||
|
||||
bool SearchResultAdService::QueueSearchResultAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
SessionID tab_id) {
|
||||
DCHECK(!creative_instance_id.empty());
|
||||
DCHECK(tab_id.is_valid());
|
||||
|
||||
SearchResultAdMap& ad_map = search_result_ads_[tab_id];
|
||||
auto it = ad_map.find(creative_instance_id);
|
||||
if (it == ad_map.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ad_viewed_event_queue_.push_front(std::move(it->second));
|
||||
ad_map.erase(creative_instance_id);
|
||||
TriggerSearchResultAdViewedEventFromQueue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SearchResultAdService::TriggerSearchResultAdViewedEventFromQueue() {
|
||||
DCHECK(ads_service_);
|
||||
DCHECK(!ad_viewed_event_queue_.empty() ||
|
||||
!trigger_ad_viewed_event_in_progress_);
|
||||
|
||||
if (ad_viewed_event_queue_.empty() || trigger_ad_viewed_event_in_progress_) {
|
||||
return;
|
||||
}
|
||||
trigger_ad_viewed_event_in_progress_ = true;
|
||||
|
||||
ads::mojom::SearchResultAdPtr search_result_ad =
|
||||
std::move(search_result_ads.back());
|
||||
search_result_ads.pop_back();
|
||||
std::move(ad_viewed_event_queue_.back());
|
||||
ad_viewed_event_queue_.pop_back();
|
||||
|
||||
ads_service_->TriggerSearchResultAdEvent(
|
||||
std::move(search_result_ad), ads::mojom::SearchResultAdEventType::kViewed,
|
||||
base::BindOnce(
|
||||
&SearchResultAdService::OnTriggerSearchResultAdViewedEvents,
|
||||
weak_factory_.GetWeakPtr(), std::move(search_result_ads)));
|
||||
base::BindOnce(&SearchResultAdService::OnTriggerSearchResultAdViewedEvent,
|
||||
weak_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void SearchResultAdService::OnTriggerSearchResultAdViewedEvents(
|
||||
SearchResultAdsList search_result_ads,
|
||||
void SearchResultAdService::OnTriggerSearchResultAdViewedEvent(
|
||||
const bool success,
|
||||
const std::string& placement_id,
|
||||
ads::mojom::SearchResultAdEventType ad_event_type) {
|
||||
DCHECK_EQ(ad_event_type, ads::mojom::SearchResultAdEventType::kViewed);
|
||||
if (!success) {
|
||||
VLOG(1) << "Failed to trigger search result ad viewed event for "
|
||||
<< placement_id;
|
||||
return;
|
||||
}
|
||||
trigger_ad_viewed_event_in_progress_ = false;
|
||||
TriggerSearchResultAdViewedEventFromQueue();
|
||||
|
||||
TriggerSearchResultAdViewedEvents(std::move(search_result_ads));
|
||||
if (!success) {
|
||||
VLOG(1) << "Error during processing of search result ad event for "
|
||||
<< placement_id;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace brave_ads
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#ifndef BRAVE_COMPONENTS_BRAVE_ADS_CONTENT_BROWSER_SEARCH_RESULT_AD_SEARCH_RESULT_AD_SERVICE_H_
|
||||
#define BRAVE_COMPONENTS_BRAVE_ADS_CONTENT_BROWSER_SEARCH_RESULT_AD_SEARCH_RESULT_AD_SERVICE_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -14,6 +15,7 @@
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "brave/vendor/bat-native-ads/include/bat/ads/public/interfaces/ads.mojom.h"
|
||||
#include "components/keyed_service/core/keyed_service.h"
|
||||
#include "components/sessions/core/session_id.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "third_party/blink/public/mojom/document_metadata/document_metadata.mojom.h"
|
||||
|
||||
@@ -25,6 +27,7 @@ namespace brave_ads {
|
||||
|
||||
class AdsService;
|
||||
|
||||
// Retrieves search result ads from page and handles viewed/clicked events.
|
||||
class SearchResultAdService : public KeyedService {
|
||||
public:
|
||||
explicit SearchResultAdService(AdsService* ads_service);
|
||||
@@ -33,28 +36,71 @@ class SearchResultAdService : public KeyedService {
|
||||
SearchResultAdService(const SearchResultAdService&) = delete;
|
||||
SearchResultAdService& operator=(const SearchResultAdService&) = delete;
|
||||
|
||||
void MaybeRetrieveSearchResultAd(content::RenderFrameHost* render_frame_host);
|
||||
// Retrieves search result ads from the render frame.
|
||||
// If should_trigger_viewed_event value is false, then viewed
|
||||
// events shouldn't be sent to the ads library.
|
||||
void MaybeRetrieveSearchResultAd(content::RenderFrameHost* render_frame_host,
|
||||
SessionID tab_id,
|
||||
bool should_trigger_viewed_event);
|
||||
|
||||
// Removes search result ads from the previous page load.
|
||||
void OnDidFinishNavigation(SessionID tab_id);
|
||||
|
||||
// Removes search result ads when closing the tab.
|
||||
void OnTabClosed(SessionID tab_id);
|
||||
|
||||
// Triggers a search result ad viewed event on a specific tab.
|
||||
void MaybeTriggerSearchResultAdViewedEvent(
|
||||
const std::string& creative_instance_id,
|
||||
SessionID tab_id,
|
||||
base::OnceCallback<void(bool)> callback);
|
||||
|
||||
void SetMetadataRequestFinishedCallbackForTesting(base::OnceClosure callback);
|
||||
|
||||
AdsService* SetAdsServiceForTesting(AdsService* ads_service);
|
||||
|
||||
private:
|
||||
struct AdViewedEventCallbackInfo {
|
||||
AdViewedEventCallbackInfo();
|
||||
AdViewedEventCallbackInfo(AdViewedEventCallbackInfo&& info);
|
||||
AdViewedEventCallbackInfo& operator=(AdViewedEventCallbackInfo&& info);
|
||||
~AdViewedEventCallbackInfo();
|
||||
|
||||
std::string creative_instance_id;
|
||||
base::OnceCallback<void(bool)> callback;
|
||||
};
|
||||
|
||||
void ResetState(SessionID tab_id);
|
||||
|
||||
void OnRetrieveSearchResultAdEntities(
|
||||
mojo::Remote<blink::mojom::DocumentMetadata> document_metadata,
|
||||
SessionID tab_id,
|
||||
blink::mojom::WebPagePtr web_page);
|
||||
|
||||
void TriggerSearchResultAdViewedEvents(
|
||||
std::vector<ads::mojom::SearchResultAdPtr> search_result_ads);
|
||||
void RunAdViewedEventPendingCallbacks(SessionID tab_id, bool ads_fetched);
|
||||
|
||||
void OnTriggerSearchResultAdViewedEvents(
|
||||
std::vector<ads::mojom::SearchResultAdPtr> search_result_ads,
|
||||
bool QueueSearchResultAdViewedEvent(const std::string& creative_instance_id,
|
||||
SessionID tab_id);
|
||||
|
||||
void TriggerSearchResultAdViewedEventFromQueue();
|
||||
|
||||
void OnTriggerSearchResultAdViewedEvent(
|
||||
bool success,
|
||||
const std::string& placement_id,
|
||||
ads::mojom::SearchResultAdEventType ad_event_type);
|
||||
|
||||
raw_ptr<AdsService> ads_service_ = nullptr;
|
||||
|
||||
std::map<SessionID, std::map<std::string, ads::mojom::SearchResultAdPtr>>
|
||||
search_result_ads_;
|
||||
|
||||
std::map<SessionID, std::vector<AdViewedEventCallbackInfo>>
|
||||
ad_viewed_event_pending_callbacks_;
|
||||
|
||||
base::circular_deque<ads::mojom::SearchResultAdPtr> ad_viewed_event_queue_;
|
||||
|
||||
bool trigger_ad_viewed_event_in_progress_ = false;
|
||||
|
||||
base::OnceClosure metadata_request_finished_callback_for_testing_;
|
||||
|
||||
base::WeakPtrFactory<SearchResultAdService> weak_factory_{this};
|
||||
|
||||
@@ -4,11 +4,14 @@ source_set("renderer") {
|
||||
"brave_ads_js_handler.h",
|
||||
"brave_ads_render_frame_observer.cc",
|
||||
"brave_ads_render_frame_observer.h",
|
||||
"search_result_ad_renderer_throttle.cc",
|
||||
"search_result_ad_renderer_throttle.h",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"//base",
|
||||
"//brave/components/brave_ads/common:mojom",
|
||||
"//brave/components/brave_ads/common",
|
||||
"//brave/components/brave_search/common",
|
||||
"//content/public/renderer",
|
||||
"//gin",
|
||||
"//mojo/public/cpp/bindings",
|
||||
@@ -16,4 +19,6 @@ source_set("renderer") {
|
||||
"//third_party/blink/public/common",
|
||||
"//v8",
|
||||
]
|
||||
|
||||
public_deps = [ "//brave/components/brave_ads/common:mojom" ]
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
include_rules = [
|
||||
"+brave/components/brave_ads/common",
|
||||
"+brave/components/brave_search/common",
|
||||
"+content/public/renderer",
|
||||
"+gin",
|
||||
"+mojo/public/cpp/bindings",
|
||||
"+services/network/public/cpp",
|
||||
"+third_party/blink/public",
|
||||
"+v8/include",
|
||||
]
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
/* Copyright 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_ads/renderer/search_result_ad_renderer_throttle.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "brave/components/brave_ads/common/features.h"
|
||||
#include "brave/components/brave_ads/common/search_result_ad_util.h"
|
||||
#include "brave/components/brave_search/common/brave_search_utils.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "services/network/public/cpp/resource_request.h"
|
||||
#include "third_party/blink/public/common/browser_interface_broker_proxy.h"
|
||||
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-shared.h"
|
||||
#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h"
|
||||
#include "third_party/blink/public/platform/web_security_origin.h"
|
||||
#include "third_party/blink/public/platform/web_string.h"
|
||||
#include "third_party/blink/public/platform/web_url.h"
|
||||
#include "third_party/blink/public/platform/web_url_request.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
std::unique_ptr<blink::URLLoaderThrottle>
|
||||
SearchResultAdRendererThrottle::MaybeCreateThrottle(
|
||||
int render_frame_id,
|
||||
const blink::WebURLRequest& request) {
|
||||
if (request.GetRequestContext() != blink::mojom::RequestContextType::FETCH ||
|
||||
!base::FeatureList::IsEnabled(
|
||||
features::kSupportBraveSearchResultAdConfirmationEvents)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
absl::optional<blink::WebSecurityOrigin> top_frame_origin =
|
||||
request.TopFrameOrigin();
|
||||
if (!top_frame_origin) {
|
||||
return nullptr;
|
||||
}
|
||||
const GURL top_frame_origin_url = url::Origin(*top_frame_origin).GetURL();
|
||||
if (!brave_search::IsAllowedHost(top_frame_origin_url)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GURL requestor_origin_url =
|
||||
url::Origin(request.RequestorOrigin()).GetURL();
|
||||
if (!brave_search::IsAllowedHost(requestor_origin_url)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GURL url = static_cast<GURL>(request.Url());
|
||||
std::string creative_instance_id =
|
||||
GetCreativeInstanceIdFromSearchAdsViewedUrl(url);
|
||||
if (creative_instance_id.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
content::RenderFrame* render_frame =
|
||||
content::RenderFrame::FromRoutingID(render_frame_id);
|
||||
if (!render_frame || !render_frame->IsMainFrame()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mojo::PendingRemote<brave_ads::mojom::BraveAdsHost> brave_ads_pending_remote;
|
||||
render_frame->GetBrowserInterfaceBroker()->GetInterface(
|
||||
brave_ads_pending_remote.InitWithNewPipeAndPassReceiver());
|
||||
|
||||
auto throttle = std::make_unique<SearchResultAdRendererThrottle>(
|
||||
std::move(brave_ads_pending_remote), std::move(creative_instance_id));
|
||||
|
||||
return throttle;
|
||||
}
|
||||
|
||||
SearchResultAdRendererThrottle::SearchResultAdRendererThrottle(
|
||||
mojo::PendingRemote<brave_ads::mojom::BraveAdsHost>
|
||||
brave_ads_pending_remote,
|
||||
std::string creative_instance_id)
|
||||
: brave_ads_pending_remote_(std::move(brave_ads_pending_remote)),
|
||||
creative_instance_id_(std::move(creative_instance_id)) {
|
||||
DCHECK(brave_ads_pending_remote_);
|
||||
DCHECK(!creative_instance_id_.empty());
|
||||
}
|
||||
|
||||
SearchResultAdRendererThrottle::~SearchResultAdRendererThrottle() = default;
|
||||
|
||||
void SearchResultAdRendererThrottle::DetachFromCurrentSequence() {}
|
||||
|
||||
void SearchResultAdRendererThrottle::WillStartRequest(
|
||||
network::ResourceRequest* request,
|
||||
bool* defer) {
|
||||
DCHECK(request);
|
||||
DCHECK(request->request_initiator);
|
||||
DCHECK_EQ(creative_instance_id_,
|
||||
GetCreativeInstanceIdFromSearchAdsViewedUrl(request->url));
|
||||
DCHECK(brave_search::IsAllowedHost(request->request_initiator->GetURL()));
|
||||
DCHECK_EQ(request->resource_type,
|
||||
static_cast<int>(blink::mojom::ResourceType::kXhr));
|
||||
DCHECK(request->is_fetch_like_api);
|
||||
|
||||
mojo::Remote<brave_ads::mojom::BraveAdsHost> brave_ads_remote(
|
||||
std::move(brave_ads_pending_remote_));
|
||||
DCHECK(brave_ads_remote.is_bound());
|
||||
brave_ads_remote.reset_on_disconnect();
|
||||
|
||||
brave_ads::mojom::BraveAdsHost* raw_brave_ads_remote = brave_ads_remote.get();
|
||||
raw_brave_ads_remote->MaybeTriggerAdViewedEvent(
|
||||
creative_instance_id_,
|
||||
base::BindOnce(
|
||||
&SearchResultAdRendererThrottle::OnMaybeTriggerAdViewedEvent,
|
||||
weak_factory_.GetWeakPtr(), std::move(brave_ads_remote)));
|
||||
|
||||
*defer = true;
|
||||
}
|
||||
|
||||
void SearchResultAdRendererThrottle::OnMaybeTriggerAdViewedEvent(
|
||||
mojo::Remote<brave_ads::mojom::BraveAdsHost> brave_ads_remote,
|
||||
bool event_triggered) {
|
||||
if (event_triggered) {
|
||||
delegate_->CancelWithError(net::ERR_ABORTED);
|
||||
} else {
|
||||
delegate_->Resume();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace brave_ads
|
||||
@@ -0,0 +1,59 @@
|
||||
/* Copyright 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/. */
|
||||
|
||||
#ifndef BRAVE_COMPONENTS_BRAVE_ADS_RENDERER_SEARCH_RESULT_AD_RENDERER_THROTTLE_H_
|
||||
#define BRAVE_COMPONENTS_BRAVE_ADS_RENDERER_SEARCH_RESULT_AD_RENDERER_THROTTLE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "brave/components/brave_ads/common/brave_ads_host.mojom.h"
|
||||
#include "mojo/public/cpp/bindings/pending_remote.h"
|
||||
#include "mojo/public/cpp/bindings/remote.h"
|
||||
#include "third_party/blink/public/common/loader/url_loader_throttle.h"
|
||||
|
||||
namespace blink {
|
||||
class WebURLRequest;
|
||||
} // namespace blink
|
||||
|
||||
namespace brave_ads {
|
||||
|
||||
// Monitors fetch ad viewed confirmation requests and cancels the request
|
||||
// if the ad viewed event should be processed by the ads library.
|
||||
class SearchResultAdRendererThrottle : public blink::URLLoaderThrottle {
|
||||
public:
|
||||
static std::unique_ptr<blink::URLLoaderThrottle> MaybeCreateThrottle(
|
||||
int render_frame_id,
|
||||
const blink::WebURLRequest& request);
|
||||
|
||||
SearchResultAdRendererThrottle(
|
||||
mojo::PendingRemote<brave_ads::mojom::BraveAdsHost> brave_ads_remote,
|
||||
std::string creative_instance_id);
|
||||
~SearchResultAdRendererThrottle() override;
|
||||
|
||||
SearchResultAdRendererThrottle(const SearchResultAdRendererThrottle&) =
|
||||
delete;
|
||||
SearchResultAdRendererThrottle& operator=(
|
||||
const SearchResultAdRendererThrottle&) = delete;
|
||||
|
||||
// Implements blink::URLLoaderThrottle:
|
||||
void DetachFromCurrentSequence() override;
|
||||
void WillStartRequest(network::ResourceRequest* request,
|
||||
bool* defer) override;
|
||||
|
||||
private:
|
||||
void OnMaybeTriggerAdViewedEvent(
|
||||
mojo::Remote<brave_ads::mojom::BraveAdsHost> brave_ads_remote,
|
||||
bool event_triggered);
|
||||
|
||||
mojo::PendingRemote<brave_ads::mojom::BraveAdsHost> brave_ads_pending_remote_;
|
||||
std::string creative_instance_id_;
|
||||
base::WeakPtrFactory<SearchResultAdRendererThrottle> weak_factory_{this};
|
||||
};
|
||||
|
||||
} // namespace brave_ads
|
||||
|
||||
#endif // BRAVE_COMPONENTS_BRAVE_ADS_RENDERER_SEARCH_RESULT_AD_RENDERER_THROTTLE_H_
|
||||
@@ -0,0 +1,64 @@
|
||||
/* 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/renderer/brave_url_loader_throttle_provider.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "brave/components/brave_ads/renderer/search_result_ad_renderer_throttle.h"
|
||||
#include "chrome/renderer/chrome_content_renderer_client.h"
|
||||
#include "chrome/renderer/url_loader_throttle_provider_impl.h"
|
||||
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
|
||||
|
||||
BraveURLLoaderThrottleProvider::BraveURLLoaderThrottleProvider(
|
||||
blink::ThreadSafeBrowserInterfaceBrokerProxy* broker,
|
||||
blink::URLLoaderThrottleProviderType type,
|
||||
ChromeContentRendererClient* chrome_content_renderer_client)
|
||||
: provider_type_(type),
|
||||
url_loader_throttle_provider_impl_(
|
||||
std::make_unique<URLLoaderThrottleProviderImpl>(
|
||||
broker,
|
||||
type,
|
||||
chrome_content_renderer_client)) {}
|
||||
|
||||
BraveURLLoaderThrottleProvider::~BraveURLLoaderThrottleProvider() {}
|
||||
|
||||
BraveURLLoaderThrottleProvider::BraveURLLoaderThrottleProvider() {}
|
||||
|
||||
std::unique_ptr<blink::URLLoaderThrottleProvider>
|
||||
BraveURLLoaderThrottleProvider::Clone() {
|
||||
auto throttle_provider_clone =
|
||||
base::WrapUnique(new BraveURLLoaderThrottleProvider());
|
||||
|
||||
throttle_provider_clone->provider_type_ = provider_type_;
|
||||
throttle_provider_clone->url_loader_throttle_provider_impl_ =
|
||||
url_loader_throttle_provider_impl_->Clone();
|
||||
return throttle_provider_clone;
|
||||
}
|
||||
|
||||
blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>>
|
||||
BraveURLLoaderThrottleProvider::CreateThrottles(
|
||||
int render_frame_id,
|
||||
const blink::WebURLRequest& request) {
|
||||
blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> throttles =
|
||||
url_loader_throttle_provider_impl_->CreateThrottles(render_frame_id,
|
||||
request);
|
||||
|
||||
if (provider_type_ == blink::URLLoaderThrottleProviderType::kFrame) {
|
||||
auto search_result_ad_throttle =
|
||||
brave_ads::SearchResultAdRendererThrottle::MaybeCreateThrottle(
|
||||
render_frame_id, request);
|
||||
if (search_result_ad_throttle) {
|
||||
throttles.emplace_back(std::move(search_result_ad_throttle));
|
||||
}
|
||||
}
|
||||
|
||||
return throttles;
|
||||
}
|
||||
|
||||
void BraveURLLoaderThrottleProvider::SetOnline(bool is_online) {
|
||||
url_loader_throttle_provider_impl_->SetOnline(is_online);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/* 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/. */
|
||||
|
||||
#ifndef BRAVE_RENDERER_BRAVE_URL_LOADER_THROTTLE_PROVIDER_H_
|
||||
#define BRAVE_RENDERER_BRAVE_URL_LOADER_THROTTLE_PROVIDER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "third_party/blink/public/platform/url_loader_throttle_provider.h"
|
||||
|
||||
class ChromeContentRendererClient;
|
||||
|
||||
namespace blink {
|
||||
class ThreadSafeBrowserInterfaceBrokerProxy;
|
||||
}
|
||||
|
||||
class BraveURLLoaderThrottleProvider : public blink::URLLoaderThrottleProvider {
|
||||
public:
|
||||
BraveURLLoaderThrottleProvider(
|
||||
blink::ThreadSafeBrowserInterfaceBrokerProxy* broker,
|
||||
blink::URLLoaderThrottleProviderType type,
|
||||
ChromeContentRendererClient* chrome_content_renderer_client);
|
||||
|
||||
BraveURLLoaderThrottleProvider(const BraveURLLoaderThrottleProvider& other) =
|
||||
delete;
|
||||
BraveURLLoaderThrottleProvider& operator=(
|
||||
const BraveURLLoaderThrottleProvider&) = delete;
|
||||
|
||||
~BraveURLLoaderThrottleProvider() override;
|
||||
|
||||
// blink::URLLoaderThrottleProvider implementation.
|
||||
std::unique_ptr<blink::URLLoaderThrottleProvider> Clone() override;
|
||||
blink::WebVector<std::unique_ptr<blink::URLLoaderThrottle>> CreateThrottles(
|
||||
int render_frame_id,
|
||||
const blink::WebURLRequest& request) override;
|
||||
void SetOnline(bool is_online) override;
|
||||
|
||||
private:
|
||||
BraveURLLoaderThrottleProvider();
|
||||
|
||||
blink::URLLoaderThrottleProviderType provider_type_ =
|
||||
blink::URLLoaderThrottleProviderType::kFrame;
|
||||
|
||||
std::unique_ptr<blink::URLLoaderThrottleProvider>
|
||||
url_loader_throttle_provider_impl_;
|
||||
};
|
||||
|
||||
#endif // BRAVE_RENDERER_BRAVE_URL_LOADER_THROTTLE_PROVIDER_H_
|
||||
@@ -8,6 +8,8 @@ brave_chrome_renderer_sources = [
|
||||
"//brave/renderer/brave_content_renderer_client.h",
|
||||
"//brave/renderer/brave_render_thread_observer.cc",
|
||||
"//brave/renderer/brave_render_thread_observer.h",
|
||||
"//brave/renderer/brave_url_loader_throttle_provider.cc",
|
||||
"//brave/renderer/brave_url_loader_throttle_provider.h",
|
||||
]
|
||||
|
||||
brave_chrome_renderer_public_deps = [
|
||||
|
||||
@@ -85,6 +85,7 @@ test("brave_unit_tests") {
|
||||
configs += [ "//chrome/test:disable_thinlto_cache_flags" ]
|
||||
|
||||
sources = [
|
||||
"//brave/browser/brave_ads/search_result_ad/search_result_ad_service_unittest.cc",
|
||||
"//brave/browser/brave_content_browser_client_unittest.cc",
|
||||
"//brave/browser/brave_resources_util_unittest.cc",
|
||||
"//brave/browser/browsing_data/brave_browsing_data_remover_delegate_unittest.cc",
|
||||
@@ -191,6 +192,8 @@ test("brave_unit_tests") {
|
||||
"//brave/components/adblock_rust_ffi",
|
||||
"//brave/components/api_request_helper:api_request_helper_unit_tests",
|
||||
"//brave/components/brave_adaptive_captcha/buildflags",
|
||||
"//brave/components/brave_ads/browser:test_support",
|
||||
"//brave/components/brave_ads/common",
|
||||
"//brave/components/brave_ads/content/browser/search_result_ad",
|
||||
"//brave/components/brave_ads/test:brave_ads_unit_tests",
|
||||
"//brave/components/brave_component_updater/browser",
|
||||
|
||||
@@ -24,6 +24,14 @@
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const load = () => {
|
||||
fetch('https://search.anonymous.brave.com/v10/view?creativeInstanceId=data-creative-instance-id-1')
|
||||
.then(response => {})
|
||||
.catch((error) => {});
|
||||
}
|
||||
window.onload = load;
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
||||
@@ -40,6 +40,25 @@
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
const load = () => {
|
||||
fetch('https://search.anonymous.brave.com/v10/view?creativeInstanceId=data-creative-instance-id-1')
|
||||
.then(response => {})
|
||||
.catch((error) => {});
|
||||
fetch('https://search.anonymous.brave.com/v10/view?creativeInstanceId=data-creative-instance-id-2')
|
||||
.then(response => {})
|
||||
.catch((error) => {});
|
||||
// Somthing went wrong and we've sent view confirmation twice for data-creative-instance-id-1.
|
||||
fetch('https://search.anonymous.bravesoftware.com/v10/view?creativeInstanceId=data-creative-instance-id-1')
|
||||
.then(response => {})
|
||||
.catch((error) => {});
|
||||
// Somthing went wrong and we've sent not existant view confirmation.
|
||||
fetch('https://search.anonymous.bravesoftware.com/v10/view?creativeInstanceId=not-existant')
|
||||
.then(response => {})
|
||||
.catch((error) => {});
|
||||
}
|
||||
window.onload = load;
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user