diff --git a/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc b/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc index 86ec091df59f..3303f905ffca 100644 --- a/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc +++ b/browser/ui/webui/webcompat_reporter/webcompat_reporter_ui.cc @@ -52,6 +52,10 @@ namespace { constexpr char kUISourceHistogramName[] = "Brave.Webcompat.UISource"; constexpr int kMaxScreenshotPixelCount = 1280 * 720; +const std::string BoolToString(bool value) { + return value ? "true" : "false"; +} + } // namespace WebcompatReporterDOMHandler::WebcompatReporterDOMHandler(Profile* profile) @@ -76,15 +80,16 @@ void WebcompatReporterDOMHandler::InitAdditionalParameters(Profile* profile) { brave_vpn::BraveVpnService* vpn_service = brave_vpn::BraveVpnServiceFactory::GetForProfile(profile); if (vpn_service != nullptr) { - pending_report_.brave_vpn_connected = vpn_service->IsConnected(); + pending_report_.brave_vpn_connected = + BoolToString(vpn_service->IsConnected()); } #endif PrefService* profile_prefs = profile->GetPrefs(); pending_report_.languages = profile_prefs->GetString(language::prefs::kAcceptLanguages); - pending_report_.language_farbling = - profile_prefs->GetBoolean(brave_shields::prefs::kReduceLanguageEnabled); + pending_report_.language_farbling = BoolToString( + profile_prefs->GetBoolean(brave_shields::prefs::kReduceLanguageEnabled)); pending_report_.channel = brave::GetChannelName(); } @@ -213,8 +218,8 @@ void WebcompatReporterDOMHandler::HandleSubmitReport( submission_args.FindString(kFPBlockSettingField); const base::Value* details_arg = submission_args.Find(kDetailsField); const base::Value* contact_arg = submission_args.Find(kContactField); - pending_report_.shields_enabled = - submission_args.FindBool(kShieldsEnabledField).value_or(false); + pending_report_.shields_enabled = BoolToString( + submission_args.FindBool(kShieldsEnabledField).value_or(false)); const auto ui_source_int = submission_args.FindInt(kUISourceField); if (ui_source_int) { @@ -232,10 +237,10 @@ void WebcompatReporterDOMHandler::HandleSubmitReport( pending_report_.fp_block_setting = *fp_block_setting_arg; } if (details_arg != nullptr) { - pending_report_.details = details_arg->Clone(); + pending_report_.details = details_arg->GetString(); } if (contact_arg != nullptr) { - pending_report_.contact = contact_arg->Clone(); + pending_report_.contact = contact_arg->GetString(); } auto* reporter_service = diff --git a/browser/webcompat_reporter/sources.gni b/browser/webcompat_reporter/sources.gni index d0215a6fe5af..13c35bc21b4c 100644 --- a/browser/webcompat_reporter/sources.gni +++ b/browser/webcompat_reporter/sources.gni @@ -4,6 +4,8 @@ # You can obtain one at https://mozilla.org/MPL/2.0/. */ brave_browser_webcompat_reporter_sources = [ + "//brave/browser/webcompat_reporter/webcompat_reporter_service_delegate.cc", + "//brave/browser/webcompat_reporter/webcompat_reporter_service_delegate.h", "//brave/browser/webcompat_reporter/webcompat_reporter_service_factory.cc", "//brave/browser/webcompat_reporter/webcompat_reporter_service_factory.h", ] diff --git a/browser/webcompat_reporter/webcompat_reporter_service_delegate.cc b/browser/webcompat_reporter/webcompat_reporter_service_delegate.cc new file mode 100644 index 000000000000..a1e8ed4b5b6f --- /dev/null +++ b/browser/webcompat_reporter/webcompat_reporter_service_delegate.cc @@ -0,0 +1,49 @@ +/* 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/webcompat_reporter/webcompat_reporter_service_delegate.h" + +#include "brave/common/brave_channel_info.h" +#include "brave/components/brave_shields/content/browser/ad_block_service.h" +#include "brave/components/brave_shields/core/browser/ad_block_component_service_manager.h" +#include "brave/components/brave_shields/core/browser/filter_list_catalog_entry.h" + +namespace webcompat_reporter { + +WebcompatReporterServiceDelegateImpl::WebcompatReporterServiceDelegateImpl( + brave_shields::AdBlockService* adblock_service) + : adblock_service_(adblock_service) {} + +WebcompatReporterServiceDelegateImpl::~WebcompatReporterServiceDelegateImpl() = + default; + +std::optional> +WebcompatReporterServiceDelegateImpl::GetAdblockFilterListNames() const { + if (!adblock_service_) { + return std::nullopt; + } + + std::vector ad_block_list_names; + brave_shields::AdBlockComponentServiceManager* service_manager = + adblock_service_->component_service_manager(); + CHECK(service_manager); + for (const brave_shields::FilterListCatalogEntry& entry : + service_manager->GetFilterListCatalog()) { + if (service_manager->IsFilterListEnabled(entry.uuid)) { + ad_block_list_names.push_back(entry.title); + } + } + + if (ad_block_list_names.empty()) { + return std::nullopt; + } + + return ad_block_list_names; +} + +std::string WebcompatReporterServiceDelegateImpl::GetChannelName() const { + return brave::GetChannelName(); +} +} // namespace webcompat_reporter diff --git a/browser/webcompat_reporter/webcompat_reporter_service_delegate.h b/browser/webcompat_reporter/webcompat_reporter_service_delegate.h new file mode 100644 index 000000000000..accd928670c6 --- /dev/null +++ b/browser/webcompat_reporter/webcompat_reporter_service_delegate.h @@ -0,0 +1,41 @@ +/* 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_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_DELEGATE_H_ +#define BRAVE_BROWSER_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_DELEGATE_H_ + +#include +#include + +#include "brave/components/webcompat_reporter/browser/webcompat_reporter_service.h" + +namespace brave_shields { +class AdBlockService; +} // namespace brave_shields + +namespace webcompat_reporter { + +class WebcompatReporterServiceDelegateImpl + : public WebcompatReporterService::WebCompatServiceDelegate { + public: + explicit WebcompatReporterServiceDelegateImpl( + brave_shields::AdBlockService* adblock_service); + WebcompatReporterServiceDelegateImpl( + const WebcompatReporterServiceDelegateImpl&) = delete; + WebcompatReporterServiceDelegateImpl& operator=( + const WebcompatReporterServiceDelegateImpl&) = delete; + ~WebcompatReporterServiceDelegateImpl() override; + + std::optional> GetAdblockFilterListNames() + const override; + std::string GetChannelName() const override; + + private: + raw_ptr adblock_service_; +}; + +} // namespace webcompat_reporter + +#endif // BRAVE_BROWSER_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_DELEGATE_H_ diff --git a/browser/webcompat_reporter/webcompat_reporter_service_factory.cc b/browser/webcompat_reporter/webcompat_reporter_service_factory.cc index 85a384464f15..eee4c0ffd37a 100644 --- a/browser/webcompat_reporter/webcompat_reporter_service_factory.cc +++ b/browser/webcompat_reporter/webcompat_reporter_service_factory.cc @@ -5,10 +5,12 @@ #include "brave/browser/webcompat_reporter/webcompat_reporter_service_factory.h" +#include #include #include "base/no_destructor.h" #include "brave/browser/brave_browser_process.h" +#include "brave/browser/webcompat_reporter/webcompat_reporter_service_delegate.h" #include "brave/components/brave_shields/content/browser/ad_block_service.h" #include "brave/components/webcompat_reporter/browser/webcompat_reporter_service.h" #include "chrome/browser/browser_process.h" @@ -51,10 +53,15 @@ WebcompatReporterServiceFactory::~WebcompatReporterServiceFactory() = default; KeyedService* WebcompatReporterServiceFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { auto* default_storage_partition = context->GetDefaultStoragePartition(); + if (!default_storage_partition) { + return nullptr; + } + auto shared_url_loader_factory = default_storage_partition->GetURLLoaderFactoryForBrowserProcess(); return new WebcompatReporterService( - g_brave_browser_process->ad_block_service(), + std::make_unique( + g_brave_browser_process->ad_block_service()), g_browser_process->component_updater(), shared_url_loader_factory); } diff --git a/components/webcompat_reporter/browser/BUILD.gn b/components/webcompat_reporter/browser/BUILD.gn index 8035eefa73d6..d823de78ad82 100644 --- a/components/webcompat_reporter/browser/BUILD.gn +++ b/components/webcompat_reporter/browser/BUILD.gn @@ -17,9 +17,6 @@ static_library("browser") { deps = [ "//base", - "//brave/common", - "//brave/components/brave_referrals/browser", - "//brave/components/brave_shields/content/browser", "//brave/components/brave_shields/core/browser", "//brave/components/brave_shields/core/common", "//brave/components/brave_shields/core/common:mojom", @@ -28,31 +25,41 @@ static_library("browser") { "//brave/components/version_info", "//brave/components/webcompat_reporter/buildflags", "//brave/components/webcompat_reporter/common:mojom", - "//content/public/browser", "//net", "//services/network/public/cpp", ] + + if (!is_ios) { + deps += [ + "//brave/common", + "//brave/components/brave_referrals/browser", + "//brave/components/brave_shields/content/browser", + "//content/public/browser", + ] + } } -source_set("unittests") { - testonly = true +if (!is_ios) { + source_set("unittests") { + testonly = true - sources = [ - "webcompat_report_uploader_unittest.cc", - "webcompat_reporter_service_unittest.cc", - ] + sources = [ + "webcompat_report_uploader_unittest.cc", + "webcompat_reporter_service_unittest.cc", + ] - deps = [ - ":browser", - "//base", - "//base/test:test_support", - "//brave/components/brave_shields/content/browser", - "//brave/components/brave_stats/browser", - "//brave/components/version_info", - "//brave/components/webcompat_reporter/common:mojom", - "//chrome/test:test_support", - "//components/component_updater:test_support", - "//testing/gtest", - "//url", - ] + deps = [ + ":browser", + "//base", + "//base/test:test_support", + "//brave/components/brave_shields/content/browser", + "//brave/components/brave_stats/browser", + "//brave/components/version_info", + "//brave/components/webcompat_reporter/common:mojom", + "//chrome/test:test_support", + "//components/component_updater:test_support", + "//testing/gtest", + "//url", + ] + } } diff --git a/components/webcompat_reporter/browser/webcompat_report_uploader.cc b/components/webcompat_reporter/browser/webcompat_report_uploader.cc index 4d915590d941..936a2eb4b35b 100644 --- a/components/webcompat_reporter/browser/webcompat_report_uploader.cc +++ b/components/webcompat_reporter/browser/webcompat_report_uploader.cc @@ -71,8 +71,8 @@ Report& Report::operator=(const Report& other) { languages = other.languages; language_farbling = other.language_farbling; brave_vpn_connected = other.brave_vpn_connected; - SetOptValue(details, other.details); - SetOptValue(contact, other.contact); + details = other.details; + contact = other.contact; SetOptValue(ad_block_components, other.ad_block_components); screenshot_png = other.screenshot_png; } @@ -100,7 +100,7 @@ void WebcompatReportUploader::SubmitReport(const Report& report) { } if (report.details) { - report_details_dict.Set(kDetailsField, report.details.value().Clone()); + report_details_dict.Set(kDetailsField, report.details.value()); } if (report.ad_block_components) { @@ -109,7 +109,7 @@ void WebcompatReportUploader::SubmitReport(const Report& report) { } if (report.contact) { - report_details_dict.Set(kContactField, report.contact.value().Clone()); + report_details_dict.Set(kContactField, report.contact.value()); } if (report.channel) { @@ -192,7 +192,9 @@ void WebcompatReportUploader::CreateAndStartURLLoader( const GURL& upload_url, const std::string& content_type, const std::string& post_data) { +#if !BUILDFLAG(IS_IOS) DCHECK_CURRENTLY_ON(content::BrowserThread::UI); +#endif // !BUILDFLAG(IS_IOS) auto resource_request = std::make_unique(); // upload_url only includes the origin and path, and not the fragment or @@ -235,7 +237,9 @@ void WebcompatReportUploader::CreateAndStartURLLoader( void WebcompatReportUploader::OnSimpleURLLoaderComplete( std::unique_ptr response_body) { +#if !BUILDFLAG(IS_IOS) DCHECK_CURRENTLY_ON(content::BrowserThread::UI); +#endif // !BUILDFLAG(IS_IOS) bool success = !!response_body; diff --git a/components/webcompat_reporter/browser/webcompat_report_uploader.h b/components/webcompat_reporter/browser/webcompat_report_uploader.h index 93f0b129f279..2a482bdc818a 100644 --- a/components/webcompat_reporter/browser/webcompat_report_uploader.h +++ b/components/webcompat_reporter/browser/webcompat_report_uploader.h @@ -31,15 +31,15 @@ struct Report { std::optional brave_version; std::optional channel; std::optional report_url; - std::optional shields_enabled; + std::optional shields_enabled; std::optional ad_block_setting; std::optional fp_block_setting; std::optional ad_block_list_names; std::optional languages; - std::optional language_farbling; - std::optional brave_vpn_connected; - std::optional details; - std::optional contact; + std::optional language_farbling; + std::optional brave_vpn_connected; + std::optional details; + std::optional contact; std::optional ad_block_components; std::optional> screenshot_png; diff --git a/components/webcompat_reporter/browser/webcompat_report_uploader_unittest.cc b/components/webcompat_reporter/browser/webcompat_report_uploader_unittest.cc index 872cdbe008cc..62a83c6d0a5f 100644 --- a/components/webcompat_reporter/browser/webcompat_report_uploader_unittest.cc +++ b/components/webcompat_reporter/browser/webcompat_report_uploader_unittest.cc @@ -96,9 +96,9 @@ TEST_F(WebcompatReportUploaderUnitTest, GenerateReport) { report.channel = "dev"; report.brave_version = "1.231.45"; - report.shields_enabled = true; - report.language_farbling = true; - report.brave_vpn_connected = true; + report.shields_enabled = "true"; + report.language_farbling = "true"; + report.brave_vpn_connected = "true"; report.ad_block_setting = "ad_block_setting"; report.fp_block_setting = "fp_block_setting"; report.ad_block_list_names = "ad_block_list_names"; @@ -107,8 +107,8 @@ TEST_F(WebcompatReportUploaderUnitTest, GenerateReport) { test_dict.Set("key1", "val1"); test_dict.Set("key1", "val1"); report.ad_block_components = base::Value(test_dict.Clone()); - report.details = base::Value(test_dict.Clone()); - report.contact = base::Value(test_dict.Clone()); + report.details = "details"; + report.contact = "contact"; report.report_url = GURL("https://abc.url/p1/p2"); std::string test_dict_string; @@ -123,24 +123,24 @@ TEST_F(WebcompatReportUploaderUnitTest, GenerateReport) { "adBlockComponentsInfo": %s, "adBlockLists": "%s", "adBlockSetting": "%s", - "additionalDetails": %s, + "additionalDetails": "%s", "api_key": "%s", - "braveVPNEnabled": %s, + "braveVPNEnabled": "%s", "channel": "%s", - "contactInfo": %s, + "contactInfo": "%s", "domain": "%s", "fpBlockSetting": "%s", - "languageFarblingEnabled": %s, + "languageFarblingEnabled": "%s", "languages": "%s", - "shieldsEnabled": %s, + "shieldsEnabled": "%s", "url": "%s", "version": "%s" })", test_dict_string.c_str(), report.ad_block_list_names->c_str(), - report.ad_block_setting->c_str(), test_dict_string.c_str(), + report.ad_block_setting->c_str(), report.details->c_str(), brave_stats::GetAPIKey().c_str(), report.brave_vpn_connected ? "true" : "false", - report.channel->c_str(), test_dict_string.c_str(), + report.channel->c_str(), report.contact->c_str(), url::Origin::Create(report.report_url.value()).Serialize().c_str(), report.fp_block_setting->c_str(), report.language_farbling ? "true" : "false", diff --git a/components/webcompat_reporter/browser/webcompat_reporter_service.cc b/components/webcompat_reporter/browser/webcompat_reporter_service.cc index 444a7928e820..6afe9973b15a 100644 --- a/components/webcompat_reporter/browser/webcompat_reporter_service.cc +++ b/components/webcompat_reporter/browser/webcompat_reporter_service.cc @@ -16,9 +16,6 @@ #include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" -#include "brave/common/brave_channel_info.h" -#include "brave/components/brave_shields/content/browser/ad_block_service.h" -#include "brave/components/brave_shields/core/browser/ad_block_component_service_manager.h" #include "brave/components/brave_shields/core/browser/filter_list_catalog_entry.h" #include "brave/components/brave_shields/core/common/brave_shield_constants.h" #include "brave/components/version_info/version_info.h" @@ -30,18 +27,6 @@ constexpr char kComponentItemName[] = "name"; constexpr char kComponentItemId[] = "id"; constexpr char kComponentItemVersion[] = "version"; -void SetDictVal(std::optional& val_to_set, - const std::optional>& - opt_dict_val) { - if (!opt_dict_val || opt_dict_val->empty()) { - return; - } - auto detail_dict = base::Value::Dict(); - for (const auto& detail_item : opt_dict_val.value()) { - detail_dict.Set(detail_item.first, detail_item.second); - } - val_to_set = base::Value(std::move(detail_dict)); -} void SetListVal( std::optional& val_to_set, const base::flat_map& opt_dict_val) { @@ -54,6 +39,7 @@ void SetListVal( } val_to_set = base::Value(std::move(list)); } + void ConvertCompsToValue( std::optional& val_to_set, const std::vector& @@ -92,28 +78,21 @@ bool NeedsToGetComponentInfo(const std::string& component_id) { void FillReportWithAdblockListNames( webcompat_reporter::Report& report, - raw_ptr& ad_block_service) { + webcompat_reporter::WebcompatReporterService::WebCompatServiceDelegate* + service_delegate) { if (report.ad_block_list_names && !report.ad_block_list_names->empty()) { return; } - if (!ad_block_service) { + if (!service_delegate) { return; } - std::vector ad_block_list_names; - if (ad_block_service != nullptr) { - brave_shields::AdBlockComponentServiceManager* service_manager = - ad_block_service->component_service_manager(); - CHECK(service_manager); - for (const brave_shields::FilterListCatalogEntry& entry : - service_manager->GetFilterListCatalog()) { - if (service_manager->IsFilterListEnabled(entry.uuid)) { - ad_block_list_names.push_back(entry.title); - } - } + auto filter_list = service_delegate->GetAdblockFilterListNames(); + if (!filter_list) { + return; } - report.ad_block_list_names = base::JoinString(ad_block_list_names, ","); + report.ad_block_list_names = base::JoinString(filter_list.value(), ","); } void FillReportWithComponetsInfo( @@ -177,24 +156,26 @@ void FillReportByReportInfo( report.languages = report_info->languages; report.language_farbling = report_info->language_farbling; report.brave_vpn_connected = report_info->brave_vpn_connected; - SetDictVal(report.details, report_info->details); - SetDictVal(report.contact, report_info->contact); + report.details = report_info->details; + report.contact = report_info->contact; report.screenshot_png = report_info->screenshot_png; } void FillReportValues( webcompat_reporter::Report& report, raw_ptr& component_updater, - raw_ptr& ad_block_service) { - if (!report.channel) { - report.channel = brave::GetChannelName(); + webcompat_reporter::WebcompatReporterService::WebCompatServiceDelegate* + service_delegate) { + if (!report.channel && service_delegate) { + report.channel = service_delegate->GetChannelName(); } + if (!report.brave_version) { report.brave_version = version_info::GetBraveVersionWithoutChromiumMajorVersion(); } FillReportWithComponetsInfo(report, component_updater); - FillReportWithAdblockListNames(report, ad_block_service); + FillReportWithAdblockListNames(report, service_delegate); } } // namespace namespace webcompat_reporter { @@ -202,11 +183,11 @@ namespace webcompat_reporter { WebcompatReporterService::WebcompatReporterService() = default; WebcompatReporterService::WebcompatReporterService( - brave_shields::AdBlockService* adblock_service, + std::unique_ptr service_delegate, component_updater::ComponentUpdateService* component_update_service, scoped_refptr url_loader_factory) : component_update_service_(component_update_service), - adblock_service_(adblock_service), + service_delegate_(std::move(service_delegate)), report_uploader_( std::make_unique(url_loader_factory)) {} @@ -232,7 +213,8 @@ void WebcompatReporterService::SubmitWebcompatReport( } void WebcompatReporterService::SubmitWebcompatReport(Report report_data) { - FillReportValues(report_data, component_update_service_, adblock_service_); + FillReportValues(report_data, component_update_service_, + service_delegate_ ? service_delegate_.get() : nullptr); SubmitReportInternal(report_data); } @@ -249,4 +231,8 @@ void WebcompatReporterService::SetUpWebcompatReporterServiceForTest( component_update_service_ = component_update_service; report_uploader_ = std::move(report_uploader); } +void WebcompatReporterService::SetDelegateForTest( + std::unique_ptr service_delegate) { + service_delegate_ = std::move(service_delegate); +} } // namespace webcompat_reporter diff --git a/components/webcompat_reporter/browser/webcompat_reporter_service.h b/components/webcompat_reporter/browser/webcompat_reporter_service.h index d3d19f7a59d8..a11669c95d4c 100644 --- a/components/webcompat_reporter/browser/webcompat_reporter_service.h +++ b/components/webcompat_reporter/browser/webcompat_reporter_service.h @@ -7,6 +7,8 @@ #define BRAVE_COMPONENTS_WEBCOMPAT_REPORTER_BROWSER_WEBCOMPAT_REPORTER_SERVICE_H_ #include +#include +#include #include "base/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" @@ -22,18 +24,21 @@ namespace component_updater { class ComponentUpdateService; } // namespace component_updater -namespace brave_shields { -class AdBlockService; -} // namespace brave_shields - namespace webcompat_reporter { - -// This class is not thread-safe and should have single owner class WebcompatReporterService : public KeyedService, public mojom::WebcompatReporterHandler { public: + class WebCompatServiceDelegate { + public: + virtual ~WebCompatServiceDelegate() = default; + + virtual std::optional> GetAdblockFilterListNames() + const = 0; + virtual std::string GetChannelName() const = 0; + }; + explicit WebcompatReporterService( - brave_shields::AdBlockService* adblock_service, + std::unique_ptr service_delegate, component_updater::ComponentUpdateService* component_update_service, scoped_refptr url_loader_factory); WebcompatReporterService(const WebcompatReporterService&) = delete; @@ -53,10 +58,12 @@ class WebcompatReporterService : public KeyedService, void SetUpWebcompatReporterServiceForTest( std::unique_ptr report_uploader, component_updater::ComponentUpdateService* component_update_service); + void SetDelegateForTest( + std::unique_ptr service_delegate); void SubmitReportInternal(const Report& report_data); raw_ptr component_update_service_; - raw_ptr adblock_service_; + std::unique_ptr service_delegate_; std::unique_ptr report_uploader_; mojo::ReceiverSet receivers_; base::WeakPtrFactory weak_factory_{this}; diff --git a/components/webcompat_reporter/browser/webcompat_reporter_service_unittest.cc b/components/webcompat_reporter/browser/webcompat_reporter_service_unittest.cc index 0a75e13efa27..ce8e6e93bc3f 100644 --- a/components/webcompat_reporter/browser/webcompat_reporter_service_unittest.cc +++ b/components/webcompat_reporter/browser/webcompat_reporter_service_unittest.cc @@ -32,6 +32,10 @@ using testing::_; +namespace { +constexpr char kChannelMockedValue[] = "MockedChannelValue"; +} // namespace + namespace webcompat_reporter { class MockWebcompatReportUploader : public WebcompatReportUploader { @@ -42,6 +46,19 @@ class MockWebcompatReportUploader : public WebcompatReportUploader { MOCK_METHOD(void, SubmitReport, (const Report& report), (override)); }; +class MockWebCompatServiceDelegate + : public WebcompatReporterService::WebCompatServiceDelegate { + public: + MockWebCompatServiceDelegate() = default; + ~MockWebCompatServiceDelegate() override = default; + + MOCK_METHOD(std::optional>, + GetAdblockFilterListNames, + (), + (const)); + MOCK_METHOD(std::string, GetChannelName, (), (const)); +}; + class WebcompatReporterServiceUnitTest : public testing::Test { public: WebcompatReporterServiceUnitTest() @@ -63,6 +80,18 @@ class WebcompatReporterServiceUnitTest : public testing::Test { webcompat_reporter_service_->report_uploader_.get()); } + MockWebCompatServiceDelegate* SetUpWebCompatServiceDelegateMock() { + auto delegate = std::unique_ptr( + new MockWebCompatServiceDelegate()); + auto raw_delegate_ptr = delegate.get(); + webcompat_reporter_service_->SetDelegateForTest(std::move(delegate)); + return raw_delegate_ptr; + } + + void ResetWebCompatServiceDelegateMock() { + webcompat_reporter_service_->SetDelegateForTest(nullptr); + } + protected: std::unique_ptr webcompat_reporter_service_; std::unique_ptr updater_; @@ -73,9 +102,9 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportWithReportPropsOverride) { Report report_source; report_source.channel = "channel"; report_source.brave_version = "1.231.45"; - report_source.shields_enabled = true; - report_source.language_farbling = true; - report_source.brave_vpn_connected = true; + report_source.shields_enabled = "true"; + report_source.language_farbling = "true"; + report_source.brave_vpn_connected = "true"; report_source.ad_block_setting = "ad_block_setting"; report_source.fp_block_setting = "fp_block_setting"; report_source.ad_block_list_names = "ad_block_list_names"; @@ -84,8 +113,8 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportWithReportPropsOverride) { test_dict.Set("key1", "val1"); test_dict.Set("key1", "val1"); report_source.ad_block_components = base::Value(test_dict.Clone()); - report_source.details = base::Value(test_dict.Clone()); - report_source.contact = base::Value(test_dict.Clone()); + report_source.details = "details"; + report_source.contact = "contact"; report_source.report_url = GURL("https://abc.url/p1/p2"); EXPECT_CALL(*static_cast( @@ -122,6 +151,8 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportWithNoPropsOverride) { std::vector get_components_ret_value{ component_updater::ComponentInfo("adcocjohghhfpidemphmcmlmhnfgikei", "fp", u"name", base::Version("1.234"), "c")}; + std::vector get_adblock_list_names_ret_value{"val1", "val2", + "val3"}; EXPECT_CALL(*static_cast( updater_.get()), GetComponents) @@ -132,7 +163,7 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportWithNoPropsOverride) { .WillOnce([&](const Report& report) { // check values, which must be filled by the service EXPECT_TRUE(report.channel.has_value()); - EXPECT_EQ(report.channel, brave::GetChannelName()); + EXPECT_EQ(report.channel, kChannelMockedValue); EXPECT_TRUE(report.brave_version.has_value()); EXPECT_EQ(report.brave_version, version_info::GetBraveVersionWithoutChromiumMajorVersion()); @@ -153,10 +184,20 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportWithNoPropsOverride) { EXPECT_EQ(report_source.language_farbling, report.language_farbling); EXPECT_EQ(report_source.brave_vpn_connected, report.brave_vpn_connected); - EXPECT_EQ(report_source.ad_block_list_names, - report.ad_block_list_names); + + EXPECT_TRUE(report.ad_block_list_names.has_value()); + EXPECT_EQ(base::JoinString(get_adblock_list_names_ret_value, ","), + report.ad_block_list_names.value()); EXPECT_EQ(report_source.languages, report.languages); }); + auto* delegate_mock = SetUpWebCompatServiceDelegateMock(); + EXPECT_CALL(*delegate_mock, GetChannelName) + .Times(1) + .WillOnce(testing::Return(kChannelMockedValue)); + EXPECT_CALL(*delegate_mock, GetAdblockFilterListNames) + .Times(1) + .WillOnce(testing::Return(get_adblock_list_names_ret_value)); + webcompat_reporter_service_->SubmitWebcompatReport(report_source); task_environment_.RunUntilIdle(); } @@ -169,40 +210,32 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportMojo) { webcompat_reporter::mojom::ComponentInfo::New("name", "id", "version")); std::vector screenshot{1, 2, 3, 4}; auto report_info = webcompat_reporter::mojom::ReportInfo::New( - "channel", "brave_version", "https://abc.url/p1/p2", true, + "channel", "brave_version", "https://abc.url/p1/p2", "true", "ad_block_setting", "fp_block_setting", "ad_block_list_names", - "languages", true, true, key_val_data, key_val_data, - std::move(components), screenshot); + "languages", "true", "true", "details", "contact", std::move(components), + screenshot); EXPECT_CALL(*static_cast( updater_.get()), GetComponents) .Times(0); - const auto check_report_dict = - [](const std::optional& dict_to_check) { - EXPECT_TRUE(dict_to_check); - EXPECT_TRUE(dict_to_check->is_dict()); - EXPECT_EQ(*dict_to_check->GetDict().FindString("key1"), "val1"); - EXPECT_EQ(*dict_to_check->GetDict().FindString("key1"), "val1"); - }; - EXPECT_CALL(*GetMockWebcompatReportUploader(), SubmitReport(_)) .Times(1) .WillOnce([&](const Report& report) { EXPECT_EQ(report.channel, "channel"); EXPECT_EQ(report.brave_version, "brave_version"); EXPECT_EQ(report.report_url, "https://abc.url/p1/p2"); - EXPECT_TRUE(report.shields_enabled); + EXPECT_EQ(report.shields_enabled, "true"); EXPECT_EQ(report.ad_block_setting, "ad_block_setting"); EXPECT_EQ(report.fp_block_setting, "fp_block_setting"); EXPECT_EQ(report.ad_block_list_names, "ad_block_list_names"); EXPECT_EQ(report.languages, "languages"); - EXPECT_TRUE(report.language_farbling); - EXPECT_TRUE(report.brave_vpn_connected); + EXPECT_EQ(report.language_farbling, "true"); + EXPECT_EQ(report.brave_vpn_connected, "true"); - check_report_dict(report.details); - check_report_dict(report.contact); + EXPECT_EQ(report.details, "details"); + EXPECT_EQ(report.contact, "contact"); EXPECT_TRUE(report.ad_block_components); EXPECT_TRUE(report.ad_block_components->is_list()); @@ -219,11 +252,17 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportMojo) { EXPECT_TRUE(report.screenshot_png); EXPECT_EQ(report.screenshot_png.value(), screenshot); }); + auto* delegate_mock = SetUpWebCompatServiceDelegateMock(); + EXPECT_CALL(*delegate_mock, GetChannelName).Times(0); + EXPECT_CALL(*delegate_mock, GetAdblockFilterListNames).Times(0); + webcompat_reporter_service_->SubmitWebcompatReport(std::move(report_info)); task_environment_.RunUntilIdle(); } TEST_F(WebcompatReporterServiceUnitTest, SubmitReportMojoWithNoPropsOverride) { + std::vector get_adblock_list_names_ret_value{"val1", "val2", + "val3"}; auto report_info = webcompat_reporter::mojom::ReportInfo::New(); std::vector get_components_ret_value{ component_updater::ComponentInfo("adcocjohghhfpidemphmcmlmhnfgikei", "fp", @@ -238,7 +277,7 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportMojoWithNoPropsOverride) { .WillOnce([&](const Report& report) { // check values, which must be filled by the service EXPECT_TRUE(report.channel.has_value()); - EXPECT_EQ(report.channel, brave::GetChannelName()); + EXPECT_EQ(report.channel, kChannelMockedValue); EXPECT_TRUE(report.brave_version.has_value()); EXPECT_EQ(report.brave_version, version_info::GetBraveVersionWithoutChromiumMajorVersion()); @@ -248,6 +287,9 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportMojoWithNoPropsOverride) { EXPECT_TRUE(ad_block_components.front().is_dict()); EXPECT_EQ(*ad_block_components.front().GetDict().FindString("id"), "adcocjohghhfpidemphmcmlmhnfgikei"); + EXPECT_TRUE(report.ad_block_list_names.has_value()); + EXPECT_EQ(base::JoinString(get_adblock_list_names_ret_value, ","), + report.ad_block_list_names.value()); // check values, which we pass to the service EXPECT_FALSE(report.report_url); @@ -258,9 +300,16 @@ TEST_F(WebcompatReporterServiceUnitTest, SubmitReportMojoWithNoPropsOverride) { EXPECT_FALSE(report.ad_block_setting); EXPECT_FALSE(report.language_farbling); EXPECT_FALSE(report.brave_vpn_connected); - EXPECT_FALSE(report.ad_block_list_names); EXPECT_FALSE(report.languages); }); + auto* delegate_mock = SetUpWebCompatServiceDelegateMock(); + EXPECT_CALL(*delegate_mock, GetChannelName) + .Times(1) + .WillOnce(testing::Return(kChannelMockedValue)); + EXPECT_CALL(*delegate_mock, GetAdblockFilterListNames) + .Times(1) + .WillOnce(testing::Return(get_adblock_list_names_ret_value)); + webcompat_reporter_service_->SubmitWebcompatReport(std::move(report_info)); task_environment_.RunUntilIdle(); } diff --git a/components/webcompat_reporter/common/BUILD.gn b/components/webcompat_reporter/common/BUILD.gn index 6336b8655e24..b805c503701a 100644 --- a/components/webcompat_reporter/common/BUILD.gn +++ b/components/webcompat_reporter/common/BUILD.gn @@ -9,11 +9,5 @@ mojom("mojom") { generate_java = true sources = [ "webcompat_reporter.mojom" ] - # deps = [ "//components/content_settings/core/common:content_settings_types" ] - - public_deps = [ - "//mojo/public/mojom/base", - # "//ui/gfx/geometry/mojom", - # "//url/mojom:url_mojom_gurl", - ] + public_deps = [ "//mojo/public/mojom/base" ] } diff --git a/components/webcompat_reporter/common/webcompat_reporter.mojom b/components/webcompat_reporter/common/webcompat_reporter.mojom index d2de4124526e..44922a8da05b 100644 --- a/components/webcompat_reporter/common/webcompat_reporter.mojom +++ b/components/webcompat_reporter/common/webcompat_reporter.mojom @@ -15,15 +15,15 @@ struct ReportInfo { string? channel; string? brave_version; string? report_url; - bool? shields_enabled; + string? shields_enabled; string? ad_block_setting; string? fp_block_setting; string? ad_block_list_names; string? languages; - bool? language_farbling; - bool? brave_vpn_connected; - map? details; - map? contact; + string? language_farbling; + string? brave_vpn_connected; + string? details; + string? contact; array? ad_block_components_version; array? screenshot_png; }; diff --git a/ios/BUILD.gn b/ios/BUILD.gn index d6a389e50358..22ffbf30b40b 100644 --- a/ios/BUILD.gn +++ b/ios/BUILD.gn @@ -38,6 +38,7 @@ import("//brave/ios/browser/api/url/headers.gni") import("//brave/ios/browser/api/url_sanitizer/headers.gni") import("//brave/ios/browser/api/web/ui/headers.gni") import("//brave/ios/browser/api/web/web_state/headers.gni") +import("//brave/ios/browser/api/webcompat_reporter/headers.gni") import("//brave/ios/browser/debounce/headers.gni") import("//brave/ios/browser/url_sanitizer/headers.gni") import("//build/apple/tweak_info_plist.gni") @@ -124,6 +125,7 @@ brave_core_public_headers += browser_api_features_public_headers brave_core_public_headers += credential_provider_public_headers brave_core_public_headers += developer_options_code_public_headers brave_core_public_headers += browser_api_storekit_receipt_public_headers +brave_core_public_headers += webcompat_reporter_public_headers action("brave_core_umbrella_header") { script = "//build/config/ios/generate_umbrella_header.py" @@ -175,6 +177,7 @@ ios_framework_bundle("brave_core_ios_framework") { deps += rewards_public_deps deps += skus_public_deps deps += credential_provider_public_deps + deps += webcompat_reporter_public_deps sources = brave_core_public_headers diff --git a/ios/app/BUILD.gn b/ios/app/BUILD.gn index 464f9f7683a1..df02a3704916 100644 --- a/ios/app/BUILD.gn +++ b/ios/app/BUILD.gn @@ -11,6 +11,7 @@ import("//ios/public/provider/chrome/browser/build_config.gni") source_set("app") { configs += [ "//brave/ios/browser/api/ai_chat:mojom_header_config", + "//brave/ios/browser/api/webcompat_reporter:mojom_header_config", "//brave/ios/browser/api/brave_wallet:mojom_header_config", "//brave/ios/browser/api/skus:mojom_header_config", ] @@ -60,6 +61,8 @@ source_set("app") { "//brave/ios/browser/api/skus:skus_mojom_wrappers", "//brave/ios/browser/api/sync/driver", "//brave/ios/browser/api/web_image", + "//brave/ios/browser/api/webcompat_reporter", + "//brave/ios/browser/api/webcompat_reporter:webcompat_reporter_mojom_wrappers", "//brave/ios/browser/skus", "//brave/ios/browser/ui/webui", "//components/browser_sync", diff --git a/ios/brave-ios/App/iOS/Delegates/AppDelegate.swift b/ios/brave-ios/App/iOS/Delegates/AppDelegate.swift index c68e560a8fe4..3f98ea56b7da 100644 --- a/ios/brave-ios/App/iOS/Delegates/AppDelegate.swift +++ b/ios/brave-ios/App/iOS/Delegates/AppDelegate.swift @@ -362,8 +362,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate { SDWebImageDownloader.shared.setValue(userAgent, forHTTPHeaderField: "User-Agent") - WebcompatReporter.userAgent = userAgent - // Record the user agent for use by search suggestion clients. SearchViewController.userAgent = userAgent } diff --git a/ios/brave-ios/Sources/Brave/Frontend/Shields/SubmitReportView.swift b/ios/brave-ios/Sources/Brave/Frontend/Shields/SubmitReportView.swift index fea27af4ab90..66c45e158148 100644 --- a/ios/brave-ios/Sources/Brave/Frontend/Shields/SubmitReportView.swift +++ b/ios/brave-ios/Sources/Brave/Frontend/Shields/SubmitReportView.swift @@ -3,11 +3,13 @@ // 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 BraveCore import BraveShields import BraveUI import BraveVPN import Data import DesignSystem +import Shared import Strings import SwiftUI @@ -109,20 +111,27 @@ struct SubmitReportView: View { @MainActor func createAndSubmitReport() async { let domain = Domain.getOrCreate(forUrl: url, persistent: !isPrivateBrowsing) - let report = WebcompatReporter.Report( - cleanedURL: url, + await WebcompatReporterUploader.send( + braveVersion: String( + format: "%@ (%@)", + AppInfo.appVersion, + AppInfo.buildNumber + ), + reportUrl: url.absoluteString, + fpBlockSetting: domain.finterprintProtectionLevel, + shieldsEnabled: !domain.areAllShieldsOff, + adBlockSetting: domain.globalBlockAdsAndTrackingLevel, + adBlockListNames: FilterListStorage.shared.filterLists + .compactMap({ return $0.isEnabled ? $0.entry.title : nil }) + .joined(separator: ","), + languages: Locale.current.language.languageCode?.identifier, + languageFarbling: true, + braveVpnConnected: BraveVPN.isConnected, additionalDetails: additionalDetails, contactInfo: contactDetails, - areShieldsEnabled: !domain.areAllShieldsOff, - adBlockLevel: domain.globalBlockAdsAndTrackingLevel, - fingerprintProtectionLevel: domain.finterprintProtectionLevel, - adBlockListTitles: FilterListStorage.shared.filterLists.compactMap({ - return $0.isEnabled ? $0.entry.title : nil - }), - isVPNEnabled: BraveVPN.isConnected + adBlockComponentsVersion: nil, + screenshotPng: nil ) - - await WebcompatReporter.send(report: report) } } diff --git a/ios/brave-ios/Sources/BraveShields/WebcompatReporter.swift b/ios/brave-ios/Sources/BraveShields/WebcompatReporter.swift index 9afab7ba2e8a..d4946aa279f0 100644 --- a/ios/brave-ios/Sources/BraveShields/WebcompatReporter.swift +++ b/ios/brave-ios/Sources/BraveShields/WebcompatReporter.swift @@ -8,179 +8,58 @@ import Foundation import Shared import os.log -public class WebcompatReporter { - static let log = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "WebcompatReporter") - - /// The raw values of the web-report. - public struct Report { - /// The URL of the broken site. - /// - Note: This needs to be the cleaned up version with query params and fragments removed (as seen in the UI) - let cleanedURL: URL - /// Any user input details - let additionalDetails: String? - /// Any user input contact details that may be provided - let contactInfo: String? - /// A bool indicating if shields are enabled for that site - let areShieldsEnabled: Bool - /// The level of adblocking currently set for the page - let adBlockLevel: ShieldLevel - /// The level of fingerprinting protection currently set for this page - let fingerprintProtectionLevel: ShieldLevel - /// Titles of all enabled filter lists - let adBlockListTitles: [String] - /// If VPN is currently enabled - let isVPNEnabled: Bool - - var domain: String? { - return cleanedURL.normalizedHost() != nil - ? cleanedURL.domainURL.absoluteString : cleanedURL.baseDomain - } - - public init( - cleanedURL: URL, - additionalDetails: String? = nil, - contactInfo: String? = nil, - areShieldsEnabled: Bool, - adBlockLevel: ShieldLevel, - fingerprintProtectionLevel: ShieldLevel, - adBlockListTitles: [String], - isVPNEnabled: Bool - ) { - self.cleanedURL = cleanedURL - self.additionalDetails = additionalDetails - self.contactInfo = contactInfo - self.areShieldsEnabled = areShieldsEnabled - self.adBlockLevel = adBlockLevel - self.fingerprintProtectionLevel = fingerprintProtectionLevel - self.adBlockListTitles = adBlockListTitles - self.isVPNEnabled = isVPNEnabled - } - } - - private struct Payload: Encodable { - let report: Report - let apiKey: String? - let languageCode: String? - - enum CodingKeys: String, CodingKey { - case url - case domain - case additionalDetails - case contactInfo - case apiKey = "api_key" - - case fpBlockSetting - case adBlockSetting - case adBlockLists - case shieldsEnabled - case languages - case languageFarblingEnabled - case braveVPNEnabled - case channel - case version - } - - public func encode(to encoder: Encoder) throws { - // We want to ensure that the URL _can_ be normalized, since `domainURL` will return itself - // (the full URL) if the URL can't be normalized. If the URL can't be normalized, send only - // the base domain without scheme. - guard let domain = report.domain else { - throw EncodingError.invalidValue( - CodingKeys.domain, - EncodingError.Context( - codingPath: encoder.codingPath, - debugDescription: "Cannot extract `domain` from url" - ) - ) - } - - guard let apiKey = apiKey else { - throw EncodingError.invalidValue( - CodingKeys.apiKey, - EncodingError.Context( - codingPath: encoder.codingPath, - debugDescription: "Missing api_key" - ) - ) - } - - var container: KeyedEncodingContainer = encoder.container( - keyedBy: CodingKeys.self - ) - let version = String(format: "%@ (%@)", AppInfo.appVersion, AppInfo.buildNumber) - try container.encode(domain, forKey: .domain) - try container.encode(version, forKey: .version) - try container.encode(report.cleanedURL.absoluteString, forKey: .url) - try container.encodeIfPresent(report.additionalDetails, forKey: .additionalDetails) - try container.encodeIfPresent(report.contactInfo, forKey: .contactInfo) - try container.encodeIfPresent(languageCode, forKey: .languages) - // languageFarblingEnabled is always enabled in iOS WebKit - try container.encode(true, forKey: .languageFarblingEnabled) - try container.encode(report.areShieldsEnabled, forKey: .shieldsEnabled) - try container.encode(report.isVPNEnabled, forKey: .braveVPNEnabled) - try container.encode(report.adBlockListTitles.joined(separator: ","), forKey: .adBlockLists) - try container.encode(report.fingerprintProtectionLevel.reportLabel, forKey: .fpBlockSetting) - try container.encode(report.adBlockLevel.reportLabel, forKey: .adBlockSetting) - try container.encode(AppConstants.buildChannel.webCompatReportName, forKey: .channel) - try container.encode(apiKey, forKey: .apiKey) - } - } - - /// A custom user agent to send along with reports - public static var userAgent: String? - - /// Get the user's language code - private static var currentLanguageCode: String? { - return Locale.current.language.languageCode?.identifier - } +public class WebcompatReporterUploader { + static let log = Logger( + subsystem: Bundle.main.bundleIdentifier!, + category: "WebcompatReporterUploader" + ) /// Report a webcompat issue on a given website - /// - /// - Returns: A deferred boolean on whether or not it reported successfully (default queue: main) - @discardableResult - public static func send(report: Report) async -> Bool { - let apiKey = kBraveStatsAPIKey - let payload = Payload(report: report, apiKey: apiKey, languageCode: currentLanguageCode) - - guard !kWebcompatReportEndpoint.isEmpty, let endpoint = URL(string: kWebcompatReportEndpoint) - else { - Self.log.error( - "Failed to setup webcompat request: Invalid endpoint `\(kWebcompatReportEndpoint)`" - ) - return false - } - - do { - let encoder = JSONEncoder() - var request = URLRequest(url: endpoint) - request.httpMethod = "POST" - request.addValue("application/json", forHTTPHeaderField: "Content-Type") - request.httpBody = try encoder.encode(payload) - - if let userAgent = userAgent { - request.setValue(userAgent, forHTTPHeaderField: "User-Agent") - } - - let session = URLSession(configuration: .ephemeral) - let result = try await session.data(for: request) - - if let response = result.1 as? HTTPURLResponse { - let success = response.statusCode >= 200 && response.statusCode < 300 - - if !success { - log.error("Failed to report webcompat issue: Status Code \(response.statusCode)") - } - - return success - } else { - return false - } - } catch { - Logger.module.error( - "Failed to setup webcompat request payload: \(error.localizedDescription)" + public static func send( + braveVersion: String?, + reportUrl: String?, + fpBlockSetting: ShieldLevel?, + shieldsEnabled: Bool?, + adBlockSetting: ShieldLevel?, + adBlockListNames: String?, + languages: String?, + languageFarbling: Bool?, + braveVpnConnected: Bool?, + additionalDetails: String?, + contactInfo: String?, + adBlockComponentsVersion: [WebcompatReporter.ComponentInfo]?, + screenshotPng: [NSNumber]? + ) async { + + let webcompatReporterAPI = await WebcompatReporter.ServiceFactory.get( + privateMode: false + )! + webcompatReporterAPI.submitWebcompatReport( + reportInfo: .init( + channel: AppConstants.buildChannel.webCompatReportName, + braveVersion: braveVersion, + reportUrl: reportUrl, + shieldsEnabled: shieldsEnabled != nil + ? String( + shieldsEnabled! + ) : "", + adBlockSetting: adBlockSetting != nil ? adBlockSetting!.reportLabel : "", + fpBlockSetting: fpBlockSetting != nil ? fpBlockSetting!.reportLabel : "", + adBlockListNames: adBlockListNames, + languages: Locale.current.language.languageCode?.identifier, + languageFarbling: String( + languageFarbling! + ), + braveVpnConnected: braveVpnConnected != nil + ? String( + braveVpnConnected! + ) : "", + details: additionalDetails, + contact: contactInfo, + adBlockComponentsVersion: nil, + screenshotPng: screenshotPng ) - return false - } + ) } } diff --git a/ios/browser/api/webcompat_reporter/BUILD.gn b/ios/browser/api/webcompat_reporter/BUILD.gn new file mode 100644 index 000000000000..c72f5908ca11 --- /dev/null +++ b/ios/browser/api/webcompat_reporter/BUILD.gn @@ -0,0 +1,49 @@ +# 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/build/ios/mojom/mojom_wrappers.gni") +import("//build/config/ios/rules.gni") +import("//ios/build/config.gni") + +config("mojom_header_config") { + visibility = [ + ":*", + "//brave/ios/app", + ] + include_dirs = + [ "$root_gen_dir/brave/components/webcompat_reporter/common/ios" ] +} + +source_set("webcompat_reporter") { + configs += [ ":mojom_header_config" ] + sources = [ + "webcompat_reporter_service_factory.h", + "webcompat_reporter_service_factory.mm", + "webcompat_reporter_service_factory_wrapper.h", + "webcompat_reporter_service_factory_wrapper.mm", + ] + + deps = [ + ":webcompat_reporter_mojom_wrappers", + "//base", + "//brave/base/mac", + "//brave/components/webcompat_reporter/browser:browser", + "//brave/ios/browser/keyed_service", + "//components/keyed_service/core", + "//components/keyed_service/ios:ios", + "//ios/chrome/browser/shared/model/application_context", + "//mojo/public/cpp/bindings", + ] + + frameworks = [ "Foundation.framework" ] +} + +ios_objc_mojom_wrappers("webcompat_reporter_mojom_wrappers") { + mojom_target = "//brave/components/webcompat_reporter/common:mojom" + sources = [ + "//brave/components/webcompat_reporter/common/webcompat_reporter.mojom", + ] + output_dir = "$root_gen_dir/brave/components/webcompat_reporter/common/ios" +} diff --git a/ios/browser/api/webcompat_reporter/headers.gni b/ios/browser/api/webcompat_reporter/headers.gni new file mode 100644 index 000000000000..149ce65c12c3 --- /dev/null +++ b/ios/browser/api/webcompat_reporter/headers.gni @@ -0,0 +1,11 @@ +# 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/. + +webcompat_reporter_public_headers = [ + "//brave/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.h", + "$root_gen_dir/brave/components/webcompat_reporter/common/ios/webcompat_reporter.mojom.objc.h", +] + +webcompat_reporter_public_deps = [ "//brave/ios/browser/api/webcompat_reporter:webcompat_reporter_mojom_wrappers" ] diff --git a/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.h b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.h new file mode 100644 index 000000000000..e1477b0dc59a --- /dev/null +++ b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.h @@ -0,0 +1,48 @@ +// 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_IOS_BROWSER_API_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_FACTORY_H_ +#define BRAVE_IOS_BROWSER_API_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_FACTORY_H_ + +#include + +#include "base/no_destructor.h" +#include "brave/components/webcompat_reporter/common/webcompat_reporter.mojom.h" +#include "components/keyed_service/ios/browser_state_keyed_service_factory.h" + +class ChromeBrowserState; + +namespace base { +template +class NoDestructor; +} // namespace base + +namespace webcompat_reporter { + +class WebcompatReporterServiceFactory : public BrowserStateKeyedServiceFactory { + public: + static mojo::PendingRemote + GetForBrowserState(ChromeBrowserState* browser_state); + static WebcompatReporterServiceFactory* GetInstance(); + + WebcompatReporterServiceFactory(const WebcompatReporterServiceFactory&) = + delete; + WebcompatReporterServiceFactory& operator=( + const WebcompatReporterServiceFactory&) = delete; + + private: + friend class base::NoDestructor; + + WebcompatReporterServiceFactory(); + ~WebcompatReporterServiceFactory() override; + + // BrowserStateKeyedServiceFactory implementation. + std::unique_ptr BuildServiceInstanceFor( + web::BrowserState* context) const override; +}; + +} // namespace webcompat_reporter + +#endif // BRAVE_IOS_BROWSER_API_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_FACTORY_H_ diff --git a/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.mm b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.mm new file mode 100644 index 000000000000..7d79009c4724 --- /dev/null +++ b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.mm @@ -0,0 +1,53 @@ +// 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/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.h" + +#include "brave/components/webcompat_reporter/browser/webcompat_reporter_service.h" +#include "components/keyed_service/ios/browser_state_dependency_manager.h" +#include "ios/chrome/browser/shared/model/application_context/application_context.h" +#include "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" + +namespace webcompat_reporter { +// static +mojo::PendingRemote +WebcompatReporterServiceFactory::GetForBrowserState( + ChromeBrowserState* browser_state) { + auto* service = GetInstance()->GetServiceForBrowserState(browser_state, true); + if (!service) { + return mojo::PendingRemote(); + } + return static_cast(service)->MakeRemote(); +} + +// static +WebcompatReporterServiceFactory* +WebcompatReporterServiceFactory::GetInstance() { + static base::NoDestructor instance; + return instance.get(); +} + +WebcompatReporterServiceFactory::WebcompatReporterServiceFactory() + : BrowserStateKeyedServiceFactory( + "WebcompatReporterService", + BrowserStateDependencyManager::GetInstance()) {} + +WebcompatReporterServiceFactory::~WebcompatReporterServiceFactory() {} + +std::unique_ptr +WebcompatReporterServiceFactory::BuildServiceInstanceFor( + web::BrowserState* context) const { + auto* browser_state = ChromeBrowserState::FromBrowserState(context); + if (browser_state->IsOffTheRecord()) { + return nullptr; + } + + component_updater::ComponentUpdateService* cus = + GetApplicationContext()->GetComponentUpdateService(); + return std::make_unique( + nullptr, cus, context->GetSharedURLLoaderFactory()); +} + +} // namespace webcompat_reporter diff --git a/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.h b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.h new file mode 100644 index 000000000000..700e2f63ba56 --- /dev/null +++ b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.h @@ -0,0 +1,22 @@ +/* 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_IOS_BROWSER_API_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_FACTORY_WRAPPER_H_ +#define BRAVE_IOS_BROWSER_API_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_FACTORY_WRAPPER_H_ + +#import + +#include "keyed_service_factory_wrapper.h" // NOLINT + +@protocol WebcompatReporterWebcompatReporterHandler; + +OBJC_EXPORT +NS_SWIFT_NAME(WebcompatReporter.ServiceFactory) +@interface WebcompatReporterServiceFactory + : KeyedServiceFactoryWrapper < + id > +@end + +#endif // BRAVE_IOS_BROWSER_API_WEBCOMPAT_REPORTER_WEBCOMPAT_REPORTER_SERVICE_FACTORY_WRAPPER_H_ diff --git a/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.mm b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.mm new file mode 100644 index 000000000000..99fac662910f --- /dev/null +++ b/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.mm @@ -0,0 +1,24 @@ +/* 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/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory_wrapper.h" + +#include "brave/components/webcompat_reporter/common/ios/webcompat_reporter.mojom.objc+private.h" +#include "brave/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.h" +#include "brave/ios/browser/keyed_service/keyed_service_factory_wrapper+private.h" +#include "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h" + +@implementation WebcompatReporterServiceFactory ++ (nullable id)serviceForBrowserState:(ChromeBrowserState*)browserState { + auto service = + webcompat_reporter::WebcompatReporterServiceFactory::GetForBrowserState( + browserState); + if (!service) { + return nil; + } + return [[WebcompatReporterWebcompatReporterHandlerMojoImpl alloc] + initWithWebcompatReporterHandler:std::move(service)]; +} +@end diff --git a/ios/browser/profile/model/brave_keyed_service_factories.mm b/ios/browser/profile/model/brave_keyed_service_factories.mm index 01199fdb0a52..3926c6d790c1 100644 --- a/ios/browser/profile/model/brave_keyed_service_factories.mm +++ b/ios/browser/profile/model/brave_keyed_service_factories.mm @@ -7,6 +7,7 @@ #include "brave/ios/browser/api/ai_chat/ai_chat_service_factory.h" #include "brave/ios/browser/api/ai_chat/model_service_factory.h" +#include "brave/ios/browser/api/webcompat_reporter/webcompat_reporter_service_factory.h" #include "brave/ios/browser/brave_wallet/asset_ratio_service_factory.h" #include "brave/ios/browser/brave_wallet/brave_wallet_ipfs_service_factory.h" #include "brave/ios/browser/brave_wallet/brave_wallet_service_factory.h" @@ -31,6 +32,7 @@ void EnsureProfileKeyedServiceFactoriesBuilt() { skus::SkusServiceFactory::GetInstance(); brave::URLSanitizerServiceFactory::GetInstance(); debounce::DebounceServiceFactory::GetInstance(); + webcompat_reporter::WebcompatReporterServiceFactory::GetInstance(); } } // namespace brave