diff --git a/android/java/org/chromium/chrome/browser/billing/InAppPurchaseWrapper.java b/android/java/org/chromium/chrome/browser/billing/InAppPurchaseWrapper.java index b10d56d6f35..b1664d1c230 100644 --- a/android/java/org/chromium/chrome/browser/billing/InAppPurchaseWrapper.java +++ b/android/java/org/chromium/chrome/browser/billing/InAppPurchaseWrapper.java @@ -355,6 +355,7 @@ public class InAppPurchaseWrapper { if (isVPNProduct) { BraveVpnPrefUtils.setSubscriptionPurchase(true); } else if (isLeoProduct) { + BraveLeoPrefUtils.setIsSubscriptionActive(true); } } } diff --git a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoCMHelper.java b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoCMHelper.java index 55fc4995396..6f4100cdeac 100644 --- a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoCMHelper.java +++ b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoCMHelper.java @@ -9,6 +9,7 @@ import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; import org.chromium.ai_chat.mojom.CredentialManagerHelper; +import org.chromium.ai_chat.mojom.PremiumStatus; import org.chromium.content_public.browser.BrowserContextHandle; import org.chromium.mojo.bindings.ConnectionErrorHandler; import org.chromium.mojo.bindings.Interface; @@ -62,6 +63,7 @@ public class BraveLeoCMHelper implements ConnectionErrorHandler { public void getPremiumStatus(CredentialManagerHelper.GetPremiumStatus_Response callback) { if (mCredentialManagerHelper == null) { + callback.call(PremiumStatus.INACTIVE); return; } mCredentialManagerHelper.getPremiumStatus(callback); diff --git a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java index 3d01605a64d..60a405ec53e 100644 --- a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java +++ b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoPrefUtils.java @@ -5,6 +5,7 @@ package org.chromium.chrome.browser.brave_leo; +import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.chrome.browser.app.BraveActivity; import org.chromium.chrome.browser.preferences.BravePref; @@ -30,12 +31,52 @@ public class BraveLeoPrefUtils { } public static void setIsSubscriptionActive(boolean value) { - UserPrefs.get(BraveLeoPrefUtils.getProfile()) + Profile profileToUse = BraveLeoPrefUtils.getProfile(); + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.setIsSubscriptionActive profile is null"); + return; + } + UserPrefs.get(profileToUse) .setBoolean(BravePref.BRAVE_CHAT_SUBSCRIPTION_ACTIVE_ANDROID, value); } public static boolean getIsSubscriptionActive(Profile profile) { - return UserPrefs.get(profile == null ? BraveLeoPrefUtils.getProfile() : profile) + Profile profileToUse = profile == null ? BraveLeoPrefUtils.getProfile() : profile; + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.getIsSubscriptionActive profile is null"); + return false; + } + return UserPrefs.get(profileToUse) .getBoolean(BravePref.BRAVE_CHAT_SUBSCRIPTION_ACTIVE_ANDROID); } + + public static void setChatPurchaseToken(String token) { + Profile profileToUse = BraveLeoPrefUtils.getProfile(); + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.setChatPurchaseToken profile is null"); + return; + } + UserPrefs.get(profileToUse).setString(BravePref.BRAVE_CHAT_PURCHASE_TOKEN_ANDROID, token); + } + + public static void setChatPackageName() { + Profile profileToUse = BraveLeoPrefUtils.getProfile(); + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.setChatPackageName profile is null"); + return; + } + UserPrefs.get(profileToUse) + .setString( + BravePref.BRAVE_CHAT_PACKAGE_NAME_ANDROID, + ContextUtils.getApplicationContext().getPackageName()); + } + + public static void setChatProductId(String productId) { + Profile profileToUse = BraveLeoPrefUtils.getProfile(); + if (profileToUse == null) { + Log.e(TAG, "BraveLeoPrefUtils.setChatProductId profile is null"); + return; + } + UserPrefs.get(profileToUse).setString(BravePref.BRAVE_CHAT_PRODUCT_ID_ANDROID, productId); + } } diff --git a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java index d400f224ce0..24d93ae9039 100644 --- a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java +++ b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoSettingsLauncherHelper.java @@ -5,9 +5,7 @@ package org.chromium.chrome.browser.brave_leo; -import android.app.Activity; import android.content.Context; -import android.content.Intent; import org.jni_zero.CalledByNative; @@ -31,11 +29,14 @@ public class BraveLeoSettingsLauncherHelper { @CalledByNative private static void goPremium(WebContents webContents) { - Activity activity = webContents.getTopLevelNativeWindow().getActivity().get(); - Intent braveLeoPlansIntent = new Intent(activity, BraveLeoPlansActivity.class); - braveLeoPlansIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - braveLeoPlansIntent.setAction(Intent.ACTION_VIEW); - activity.startActivity(braveLeoPlansIntent); + // TODO(sergz): We need to uncomment that section when our backend can handle + // mobile subscription. It's commented to avoid Purchase happens on Google Play Store + + // Activity activity = webContents.getTopLevelNativeWindow().getActivity().get(); + // Intent braveLeoPlansIntent = new Intent(activity, BraveLeoPlansActivity.class); + // braveLeoPlansIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + // braveLeoPlansIntent.setAction(Intent.ACTION_VIEW); + // activity.startActivity(braveLeoPlansIntent); } private static SettingsLauncher getLauncher() { diff --git a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoUtils.java b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoUtils.java index 9d46d5f94ca..68af7b4e08e 100644 --- a/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoUtils.java +++ b/android/java/org/chromium/chrome/browser/brave_leo/BraveLeoUtils.java @@ -24,7 +24,12 @@ public class BraveLeoUtils { activePurchaseModel -> { BraveLeoPrefUtils.setIsSubscriptionActive(activePurchaseModel != null); if (activePurchaseModel != null) { + BraveLeoPrefUtils.setChatPurchaseToken( + activePurchaseModel.getPurchaseToken()); + BraveLeoPrefUtils.setChatPackageName(); + BraveLeoPrefUtils.setChatProductId(activePurchaseModel.getProductId()); } else { + // TODO(sergz): showRestoreMenu? } if (callback != null) { callback.onResult(null); diff --git a/browser/brave_content_browser_client.cc b/browser/brave_content_browser_client.cc index 5fad33c9641..2c8ad5b4a0e 100644 --- a/browser/brave_content_browser_client.cc +++ b/browser/brave_content_browser_client.cc @@ -154,6 +154,9 @@ using extensions::ChromeContentBrowserClientExtensionsPart; #include "brave/components/ai_chat/content/browser/ai_chat_throttle.h" #include "brave/components/ai_chat/core/common/features.h" #include "brave/components/ai_chat/core/common/mojom/ai_chat.mojom.h" +#if BUILDFLAG(IS_ANDROID) +#include "brave/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.h" +#endif #endif #if BUILDFLAG(ENABLE_BRAVE_WEBTORRENT) @@ -449,6 +452,18 @@ void BindBraveSearchDefaultHost( } } +#if BUILDFLAG(IS_ANDROID) +void BindIAPSubscription( + content::RenderFrameHost* const frame_host, + mojo::PendingReceiver receiver) { + auto* context = frame_host->GetBrowserContext(); + auto* profile = Profile::FromBrowserContext(context); + mojo::MakeSelfOwnedReceiver( + std::make_unique(profile->GetPrefs()), + std::move(receiver)); +} +#endif + #if BUILDFLAG(ENABLE_BRAVE_VPN) void MaybeBindBraveVpnImpl( content::RenderFrameHost* const frame_host, @@ -743,6 +758,12 @@ void BraveContentBrowserClient::RegisterBrowserInterfaceBindersForFrame( content::RegisterWebUIControllerInterfaceBinder(map); } +#if BUILDFLAG(IS_ANDROID) + if (ai_chat::features::IsAIChatEnabled()) { + map->Add( + base::BindRepeating(&BindIAPSubscription)); + } +#endif #endif // Brave News diff --git a/browser/ui/android/strings/android_brave_strings.grd b/browser/ui/android/strings/android_brave_strings.grd index 75c5c911c95..c1ca9f18cf9 100644 --- a/browser/ui/android/strings/android_brave_strings.grd +++ b/browser/ui/android/strings/android_brave_strings.grd @@ -4110,7 +4110,7 @@ If you don't accept this request, VPN will not reconnect and your internet conne Manage Subscription - Leo Premium + Leo Premium Monthly subscription diff --git a/components/ai_chat/core/browser/BUILD.gn b/components/ai_chat/core/browser/BUILD.gn index d89adeeacd8..4aa008c4b6f 100644 --- a/components/ai_chat/core/browser/BUILD.gn +++ b/components/ai_chat/core/browser/BUILD.gn @@ -11,14 +11,14 @@ static_library("browser") { sources = [ "ai_chat_credential_manager.cc", "ai_chat_credential_manager.h", - "conversation_driver.cc", - "conversation_driver.h", "ai_chat_feedback_api.cc", "ai_chat_feedback_api.h", "ai_chat_metrics.cc", "ai_chat_metrics.h", "constants.cc", "constants.h", + "conversation_driver.cc", + "conversation_driver.h", "engine/engine_consumer.h", "engine/engine_consumer_claude.cc", "engine/engine_consumer_claude.h", @@ -30,6 +30,13 @@ static_library("browser") { "models.h", ] + if (is_android) { + sources += [ + "android/ai_chat_iap_subscription_android.cc", + "android/ai_chat_iap_subscription_android.h", + ] + } + deps = [ "//base", "//brave/brave_domains", diff --git a/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.cc b/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.cc new file mode 100644 index 00000000000..ea94cf49c78 --- /dev/null +++ b/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.cc @@ -0,0 +1,69 @@ +/* Copyright (c) 2023 The Brave Authors. All rights reserved. + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "brave/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.h" + +#include +#include + +#include "base/base64.h" +#include "base/json/json_writer.h" +#include "brave/components/ai_chat/core/common/pref_names.h" +#include "components/prefs/pref_service.h" + +namespace { + +inline constexpr char kDefaultPackage[] = "com.brave.browser"; +inline constexpr char kProductId[] = "brave.leo.monthly"; + +} // namespace + +namespace ai_chat { + +AIChatIAPSubscription::AIChatIAPSubscription(PrefService* prefs) + : prefs_(prefs) {} + +AIChatIAPSubscription::~AIChatIAPSubscription() = default; + +void AIChatIAPSubscription::GetPurchaseToken( + GetPurchaseTokenCallback callback) { + std::string purchase_token_string = ""; + std::string package_string = kDefaultPackage; + std::string product_id_string = kProductId; + + // Get the Android purchase token (for Google Play Store). + // The value for this is validated on the account.brave.com side + auto* purchase_token = + prefs_->FindPreference(prefs::kBraveChatPurchaseTokenAndroid); + if (purchase_token && !purchase_token->IsDefaultValue()) { + purchase_token_string = + prefs_->GetString(prefs::kBraveChatPurchaseTokenAndroid); + } + + auto* package = prefs_->FindPreference(prefs::kBraveChatPackageNameAndroid); + if (package && !package->IsDefaultValue()) { + package_string = prefs_->GetString(prefs::kBraveChatPackageNameAndroid); + } + + auto* product_id = prefs_->FindPreference(prefs::kBraveChatProductIdAndroid); + if (product_id && !product_id->IsDefaultValue()) { + product_id_string = prefs_->GetString(prefs::kBraveChatProductIdAndroid); + } + + base::Value::Dict response; + response.Set("type", "android"); + response.Set("raw_receipt", purchase_token_string); + response.Set("package", package_string); + response.Set("subscription_id", product_id_string); + + std::string response_json; + base::JSONWriter::Write(response, &response_json); + + std::string encoded_response_json; + base::Base64Encode(response_json, &encoded_response_json); + std::move(callback).Run(encoded_response_json); +} + +} // namespace ai_chat diff --git a/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.h b/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.h new file mode 100644 index 00000000000..bfc26384b0c --- /dev/null +++ b/components/ai_chat/core/browser/android/ai_chat_iap_subscription_android.h @@ -0,0 +1,35 @@ +/* Copyright (c) 2023 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_COMPONENTS_AI_CHAT_CORE_BROWSER_ANDROID_AI_CHAT_IAP_SUBSCRIPTION_ANDROID_H_ +#define BRAVE_COMPONENTS_AI_CHAT_CORE_BROWSER_ANDROID_AI_CHAT_IAP_SUBSCRIPTION_ANDROID_H_ + +#include "brave/components/ai_chat/core/common/mojom/ai_chat.mojom.h" + +class PrefService; + +namespace ai_chat { + +// AIChatIAPSubscription is a class that is responsible for interaction +// between SubscriptionRenderFrameObserver class that lives inside renderer +// process. + +class AIChatIAPSubscription final : public ai_chat::mojom::IAPSubscription { + public: + AIChatIAPSubscription(const AIChatIAPSubscription&) = delete; + AIChatIAPSubscription& operator=(const AIChatIAPSubscription&) = delete; + explicit AIChatIAPSubscription(PrefService* prefs); + ~AIChatIAPSubscription() override; + + // ai_chat::mojom::IAPSubscription + void GetPurchaseToken(GetPurchaseTokenCallback callback) override; + + private: + raw_ptr prefs_ = nullptr; +}; + +} // namespace ai_chat + +#endif // BRAVE_COMPONENTS_AI_CHAT_CORE_BROWSER_ANDROID_AI_CHAT_IAP_SUBSCRIPTION_ANDROID_H_ diff --git a/components/ai_chat/core/common/mojom/ai_chat.mojom b/components/ai_chat/core/common/mojom/ai_chat.mojom index 249053195e4..999ed6853b4 100644 --- a/components/ai_chat/core/common/mojom/ai_chat.mojom +++ b/components/ai_chat/core/common/mojom/ai_chat.mojom @@ -164,3 +164,8 @@ interface ChatUIPage { interface CredentialManagerHelper { GetPremiumStatus() => (PremiumStatus result); }; + +[EnableIf=is_android] +interface IAPSubscription { + GetPurchaseToken() => (string token); +}; diff --git a/components/ai_chat/core/common/pref_names.cc b/components/ai_chat/core/common/pref_names.cc index 04604a2fcd9..5892b6cfc74 100644 --- a/components/ai_chat/core/common/pref_names.cc +++ b/components/ai_chat/core/common/pref_names.cc @@ -24,6 +24,9 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) { registry->RegisterStringPref(kDefaultModelKey, "chat-default"); #if BUILDFLAG(IS_ANDROID) registry->RegisterBooleanPref(kBraveChatSubscriptionActiveAndroid, false); + registry->RegisterStringPref(kBraveChatPurchaseTokenAndroid, ""); + registry->RegisterStringPref(kBraveChatPackageNameAndroid, ""); + registry->RegisterStringPref(kBraveChatProductIdAndroid, ""); #endif } diff --git a/components/ai_chat/core/common/pref_names.h b/components/ai_chat/core/common/pref_names.h index f01535d7861..ae70f295946 100644 --- a/components/ai_chat/core/common/pref_names.h +++ b/components/ai_chat/core/common/pref_names.h @@ -39,6 +39,12 @@ inline constexpr char kBraveChatP3ALastPremiumStatus[] = #if BUILDFLAG(IS_ANDROID) inline constexpr char kBraveChatSubscriptionActiveAndroid[] = "brave.ai_chat.subscription_active_android"; +inline constexpr char kBraveChatPurchaseTokenAndroid[] = + "brave.ai_chat.purchase_token_android"; +inline constexpr char kBraveChatPackageNameAndroid[] = + "brave.ai_chat.package_name_android"; +inline constexpr char kBraveChatProductIdAndroid[] = + "brave.ai_chat.product_id_android"; #endif void RegisterProfilePrefs(PrefRegistrySimple* registry); diff --git a/components/brave_mobile_subscription/renderer/android/BUILD.gn b/components/brave_mobile_subscription/renderer/android/BUILD.gn index f4845c4f490..3016cba3bf3 100644 --- a/components/brave_mobile_subscription/renderer/android/BUILD.gn +++ b/components/brave_mobile_subscription/renderer/android/BUILD.gn @@ -17,9 +17,8 @@ source_set("android") { deps = [ "//base", - "//brave/components/brave_vpn/common", + "//brave/components/ai_chat/core/common/buildflags", "//brave/components/brave_vpn/common/buildflags", - "//brave/components/brave_vpn/common/mojom", "//brave/components/skus/renderer", "//content/public/renderer", "//gin", @@ -29,6 +28,19 @@ source_set("android") { "//third_party/blink/public/strings", "//v8", ] + + if (enable_ai_chat) { + deps += [ + "//brave/components/ai_chat/core/common", + "//brave/components/ai_chat/core/common/mojom", + ] + } + if (enable_brave_vpn) { + deps += [ + "//brave/components/brave_vpn/common", + "//brave/components/brave_vpn/common/mojom", + ] + } } source_set("browser_tests") { @@ -39,6 +51,8 @@ source_set("browser_tests") { defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ] deps = [ + "//brave/components/ai_chat/core/common", + "//brave/components/ai_chat/core/common/buildflags", "//brave/components/brave_vpn/common", "//brave/components/brave_vpn/common/buildflags:buildflags", "//brave/components/brave_mobile_subscription/renderer/android", diff --git a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.cc b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.cc index 5b153fac6cb..71fdb3d3a3f 100644 --- a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.cc +++ b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.cc @@ -25,15 +25,20 @@ #include "brave/components/brave_vpn/common/brave_vpn_utils.h" #endif +#if BUILDFLAG(ENABLE_AI_CHAT) +#include "brave/components/ai_chat/core/common/features.h" +#endif + namespace brave_subscription { namespace { -char kIntentParamName[] = "intent"; -char kIntentParamValue[] = "connect-receipt"; -char kIntentParamTestValue[] = "connect-receipt-test"; -char kProductParamName[] = "product"; -char kProductParamValue[] = "vpn"; +inline constexpr char kIntentParamName[] = "intent"; +inline constexpr char kIntentParamValue[] = "connect-receipt"; +inline constexpr char kIntentParamTestValue[] = "connect-receipt-test"; +inline constexpr char kProductParamName[] = "product"; +inline constexpr char kProductVPNParamValue[] = "vpn"; +inline constexpr char kProductLeoParamValue[] = "leo"; } // namespace @@ -47,11 +52,23 @@ SubscriptionRenderFrameObserver::~SubscriptionRenderFrameObserver() = default; bool SubscriptionRenderFrameObserver::EnsureConnected() { bool bound = false; #if BUILDFLAG(ENABLE_BRAVE_VPN) - if (!vpn_service_.is_bound()) { - render_frame()->GetBrowserInterfaceBroker()->GetInterface( - vpn_service_.BindNewPipeAndPassReceiver()); + if (brave_vpn::IsBraveVPNFeatureEnabled() && product_ == Product::kVPN) { + if (!vpn_service_.is_bound()) { + render_frame()->GetBrowserInterfaceBroker()->GetInterface( + vpn_service_.BindNewPipeAndPassReceiver()); + } + bound |= vpn_service_.is_bound(); + } +#endif +#if BUILDFLAG(ENABLE_AI_CHAT) + if (ai_chat::features::IsAIChatHistoryEnabled() && + product_ == Product::kLeo) { + if (!ai_chat_subscription_.is_bound()) { + render_frame()->GetBrowserInterfaceBroker()->GetInterface( + ai_chat_subscription_.BindNewPipeAndPassReceiver()); + } + bound |= ai_chat_subscription_.is_bound(); } - bound = vpn_service_.is_bound(); #endif return bound; } @@ -67,17 +84,33 @@ void SubscriptionRenderFrameObserver::DidCreateScriptContext( return; } + DCHECK(product_.has_value()); + if (!product_.has_value()) { + return; + } auto connected = EnsureConnected(); if (!connected) { - LOG(ERROR) << "Failed to establish connection to BraveVpnService"; + LOG(ERROR) << "Failed to establish connection to a mojo channel"; return; } + if (product_ == Product::kVPN) { #if BUILDFLAG(ENABLE_BRAVE_VPN) - vpn_service_->GetPurchaseToken(base::BindOnce( - &SubscriptionRenderFrameObserver::OnGetPurchaseToken, - weak_factory_.GetWeakPtr())); + if (vpn_service_.is_bound()) { + vpn_service_->GetPurchaseToken( + base::BindOnce(&SubscriptionRenderFrameObserver::OnGetPurchaseToken, + weak_factory_.GetWeakPtr())); + } #endif + } else if (product_ == Product::kLeo) { +#if BUILDFLAG(ENABLE_AI_CHAT) + if (ai_chat_subscription_.is_bound()) { + ai_chat_subscription_->GetPurchaseToken( + base::BindOnce(&SubscriptionRenderFrameObserver::OnGetPurchaseToken, + weak_factory_.GetWeakPtr())); + } +#endif + } } void SubscriptionRenderFrameObserver::OnGetPurchaseToken( @@ -88,11 +121,15 @@ void SubscriptionRenderFrameObserver::OnGetPurchaseToken( auto* frame = render_frame(); if (frame) { if (IsValueAllowed(purchase_token)) { -#if BUILDFLAG(ENABLE_BRAVE_VPN) - std::u16string set_local_storage = - base::StrCat({u"window.localStorage.setItem(\"braveVpn.receipt\", \"", - base::UTF8ToUTF16(purchase_token), u"\");"}); -#endif + std::string_view receipt_var_name; + if (product_ == Product::kVPN) { + receipt_var_name = "braveVpn.receipt"; + } else if (product_ == Product::kLeo) { + receipt_var_name = "braveLeo.receipt"; + } + std::u16string set_local_storage = base::UTF8ToUTF16( + base::StrCat({"window.localStorage.setItem(\"", receipt_var_name, + "\", \"", purchase_token, "\");"})); frame->ExecuteJavaScript(set_local_storage); } } @@ -129,10 +166,6 @@ bool SubscriptionRenderFrameObserver::IsValueAllowed( } bool SubscriptionRenderFrameObserver::IsAllowed() { -#if BUILDFLAG(ENABLE_BRAVE_VPN) - DCHECK(brave_vpn::IsBraveVPNFeatureEnabled()); -#endif - if (!skus::IsSafeOrigin(render_frame()->GetWebFrame()->GetSecurityOrigin())) { return false; } @@ -142,8 +175,15 @@ bool SubscriptionRenderFrameObserver::IsAllowed() { std::string intent = ExtractParam(current_url, kIntentParamName); std::string product = ExtractParam(current_url, kProductParamName); + if (product == kProductVPNParamValue) { + product_ = Product::kVPN; + } else if (product == kProductLeoParamValue) { + product_ = Product::kLeo; + } else { + product_ = std::nullopt; + } return (intent == kIntentParamValue || intent == kIntentParamTestValue) && - product == kProductParamValue; + product_.has_value(); } void SubscriptionRenderFrameObserver::OnDestruct() { diff --git a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.h b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.h index b7a70957e8e..5ea1ee63fed 100644 --- a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.h +++ b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.h @@ -7,10 +7,12 @@ #define BRAVE_COMPONENTS_BRAVE_MOBILE_SUBSCRIPTION_RENDERER_ANDROID_SUBSCRIPTION_RENDER_FRAME_OBSERVER_H_ #include +#include #include #include "base/gtest_prod_util.h" #include "base/memory/weak_ptr.h" +#include "brave/components/ai_chat/core/common/buildflags/buildflags.h" #include "brave/components/brave_vpn/common/buildflags/buildflags.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_frame_observer.h" @@ -20,6 +22,9 @@ #if BUILDFLAG(ENABLE_BRAVE_VPN) #include "brave/components/brave_vpn/common/mojom/brave_vpn.mojom.h" #endif +#if BUILDFLAG(ENABLE_AI_CHAT) +#include "brave/components/ai_chat/core/common/mojom/ai_chat.mojom.h" +#endif namespace brave_subscription { @@ -34,11 +39,11 @@ namespace brave_subscription { class SubscriptionRenderFrameObserver : public content::RenderFrameObserver { public: explicit SubscriptionRenderFrameObserver(content::RenderFrame* render_frame, - int32_t world_id); - SubscriptionRenderFrameObserver( - const SubscriptionRenderFrameObserver&) = delete; + int32_t world_id); + SubscriptionRenderFrameObserver(const SubscriptionRenderFrameObserver&) = + delete; SubscriptionRenderFrameObserver& operator=( - const SubscriptionRenderFrameObserver&) = delete; + const SubscriptionRenderFrameObserver&) = delete; ~SubscriptionRenderFrameObserver() override; // RenderFrameObserver implementation. @@ -47,9 +52,10 @@ class SubscriptionRenderFrameObserver : public content::RenderFrameObserver { private: FRIEND_TEST_ALL_PREFIXES(SubscriptionRenderFrameObserverBrowserTest, - IsAllowed); + IsAllowed); FRIEND_TEST_ALL_PREFIXES(SubscriptionRenderFrameObserverTest, ExtractParam); FRIEND_TEST_ALL_PREFIXES(SubscriptionRenderFrameObserverTest, IsValueAllowed); + enum class Product { kVPN = 0, kLeo = 1 }; bool EnsureConnected(); void OnGetPurchaseToken(const std::string& purchase_token); @@ -61,9 +67,13 @@ class SubscriptionRenderFrameObserver : public content::RenderFrameObserver { bool IsAllowed(); - int32_t world_id_; + const int32_t world_id_; + std::optional product_ = std::nullopt; #if BUILDFLAG(ENABLE_BRAVE_VPN) mojo::Remote vpn_service_; +#endif +#if BUILDFLAG(ENABLE_AI_CHAT) + mojo::Remote ai_chat_subscription_; #endif base::WeakPtrFactory weak_factory_{this}; }; diff --git a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_browsertest.cc b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_browsertest.cc index f981c4ddadb..5733c1bcaf2 100644 --- a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_browsertest.cc +++ b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_browsertest.cc @@ -6,8 +6,10 @@ #include "brave/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer.h" #include "base/test/scoped_feature_list.h" -#include "brave/components/brave_vpn/common/buildflags/buildflags.h" +#include "brave/components/ai_chat/core/common/features.h" +#include "brave/components/ai_chat/core/common/buildflags/buildflags.h" #include "brave/components/brave_vpn/common/features.h" +#include "brave/components/brave_vpn/common/buildflags/buildflags.h" #include "brave/components/skus/common/features.h" #include "content/public/common/isolated_world_ids.h" #include "content/public/test/content_mock_cert_verifier.h" @@ -21,10 +23,8 @@ class SubscriptionRenderFrameObserverBrowserTest : public: SubscriptionRenderFrameObserverBrowserTest() { scoped_feature_list_.InitWithFeatures( - {skus::features::kSkusFeature -#if BUILDFLAG(ENABLE_BRAVE_VPN) - , brave_vpn::features::kBraveVPN -#endif + {skus::features::kSkusFeature, brave_vpn::features::kBraveVPN, + ai_chat::features::kAIChatHistory }, {}); } ~SubscriptionRenderFrameObserverBrowserTest() override = default; @@ -36,10 +36,18 @@ class SubscriptionRenderFrameObserverBrowserTest : TEST_F(SubscriptionRenderFrameObserverBrowserTest, IsAllowed) { SubscriptionRenderFrameObserver observer(GetMainRenderFrame(), content::ISOLATED_WORLD_ID_GLOBAL); + // VPN LoadHTMLWithUrlOverride( R"()", "https://account.brave.com/?intent=connect-receipt&product=vpn"); + EXPECT_TRUE(observer.IsAllowed()); + + // Leo + LoadHTMLWithUrlOverride( + R"()", + "https://account.brave.com/?intent=connect-receipt&product=leo"); + EXPECT_TRUE(observer.IsAllowed()); // http LoadHTMLWithUrlOverride( @@ -62,20 +70,20 @@ TEST_F(SubscriptionRenderFrameObserverBrowserTest, IsAllowed) { EXPECT_TRUE(observer.IsAllowed()); - // no recepit + // no receipt LoadHTMLWithUrlOverride( R"()", "https://account.brave.software/?intent=&product=vpn"); EXPECT_FALSE(observer.IsAllowed()); - // wrong recepit + // wrong receipt LoadHTMLWithUrlOverride(R"()", "https://account.brave.software/?product=vpn"); EXPECT_FALSE(observer.IsAllowed()); - // wrong recepit + // wrong receipt LoadHTMLWithUrlOverride( R"()", "https://account.brave.software/?intent=wrong&product=vpn"); diff --git a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_unittest.cc b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_unittest.cc index a9613f5d1d4..256ac40755c 100644 --- a/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_unittest.cc +++ b/components/brave_mobile_subscription/renderer/android/subscription_render_frame_observer_unittest.cc @@ -27,6 +27,11 @@ TEST(SubscriptionRenderFrameObserverTest, ExtractParam) { GURL("https://account.brave.com/?intent=connect-receipt&product=vpn"), "product"), "vpn"); + EXPECT_EQ( + observer.ExtractParam( + GURL("https://account.brave.com/?intent=connect-receipt&product=leo"), + "product"), + "leo"); EXPECT_TRUE(observer .ExtractParam(GURL("https://account.brave.com/" "?intent=connect-receipt&product=vpn"), diff --git a/renderer/brave_content_renderer_client.cc b/renderer/brave_content_renderer_client.cc index 93165b54696..27556bcb227 100644 --- a/renderer/brave_content_renderer_client.cc +++ b/renderer/brave_content_renderer_client.cc @@ -9,6 +9,7 @@ #include "base/feature_list.h" #include "base/ranges/algorithm.h" +#include "brave/components/ai_chat/core/common/buildflags/buildflags.h" #include "brave/components/brave_search/common/brave_search_utils.h" #include "brave/components/brave_search/renderer/brave_search_render_frame_observer.h" #include "brave/components/brave_shields/common/features.h" @@ -56,6 +57,10 @@ #include "third_party/widevine/cdm/widevine_cdm_common.h" #endif +#if BUILDFLAG(ENABLE_AI_CHAT) && BUILDFLAG(IS_ANDROID) +#include "brave/components/ai_chat/core/common/features.h" +#endif + namespace { void MaybeRemoveWidevineSupport(media::GetSupportedKeySystemsCB cb, media::KeySystemInfos key_systems) { @@ -153,7 +158,11 @@ void BraveContentRendererClient::RenderFrameCreated( } #if BUILDFLAG(IS_ANDROID) - if (brave_vpn::IsBraveVPNFeatureEnabled()) { + if (brave_vpn::IsBraveVPNFeatureEnabled() +#if BUILDFLAG(ENABLE_AI_CHAT) + || ai_chat::features::IsAIChatHistoryEnabled() +#endif + ) { new brave_subscription::SubscriptionRenderFrameObserver( render_frame, content::ISOLATED_WORLD_ID_GLOBAL); } diff --git a/renderer/sources.gni b/renderer/sources.gni index 06bf3096ad4..0ad7ba4582d 100644 --- a/renderer/sources.gni +++ b/renderer/sources.gni @@ -54,7 +54,10 @@ if (enable_speedreader) { } if (enable_ai_chat) { - brave_chrome_renderer_deps += [ "//brave/components/ai_chat/renderer" ] + brave_chrome_renderer_deps += [ + "//brave/components/ai_chat/core/common:common", + "//brave/components/ai_chat/renderer", + ] } if (enable_playlist) {