[iOS] Add Brave Talk JavaScriptFeature (#34799)
This introduces a Chromium JavaScriptFeature based on `BraveTalkScript.js` The script itself deviates from its original in that it is now sandboxed to the isolated content world
This commit is contained in:
@@ -126,6 +126,7 @@ brave_core_public_headers = [
|
||||
"//ui/base/l10n/l10n_util_mac_bridge.h",
|
||||
"$root_gen_dir/components/grit/brave_components_swift_strings.h",
|
||||
"//brave/ios/browser/web/logins/logins_tab_helper_bridge.h",
|
||||
"//brave/ios/browser/brave_talk/brave_talk_tab_helper_bridge.h",
|
||||
]
|
||||
|
||||
brave_core_public_headers += ads_public_headers
|
||||
|
||||
@@ -646,7 +646,7 @@ var package = Package(
|
||||
.target(
|
||||
name: "BraveTalk",
|
||||
dependencies: [
|
||||
"Shared", "Preferences", "JitsiMeet", "BraveCore",
|
||||
"Shared", "Preferences", "JitsiMeet", "BraveCore", "Web",
|
||||
.product(name: "Collections", package: "swift-collections"),
|
||||
],
|
||||
plugins: ["LoggerPlugin"]
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
// 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 https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import Foundation
|
||||
import Shared
|
||||
import UIKit
|
||||
import Web
|
||||
|
||||
extension BrowserViewController {
|
||||
func launchNativeBraveTalk(tab: (any TabState)?, room: String, token: String) {
|
||||
guard let host = tab?.lastCommittedURL?.host else { return }
|
||||
braveTalkJitsiCoordinator.launchNativeBraveTalk(
|
||||
for: room,
|
||||
token: token,
|
||||
host: host,
|
||||
onEnterCall: { [weak tab] in
|
||||
tab?.stopLoading()
|
||||
},
|
||||
onExitCall: { [weak self] in
|
||||
guard let self = self else { return }
|
||||
// When we close the call, redirect to Brave Talk home page if the selected tab is still the original
|
||||
// talk URL
|
||||
if let url = self.tabManager.selectedTab?.visibleURL,
|
||||
let currentHost = url.host,
|
||||
DomainUserScript.braveTalkHelper.associatedDomains.contains(currentHost)
|
||||
{
|
||||
var components = URLComponents()
|
||||
components.host = currentHost
|
||||
components.scheme = url.scheme
|
||||
self.select(url: components.url!, isUserDefinedURLNavigation: false)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
+16
@@ -83,6 +83,22 @@ extension BrowserViewController: TabManagerDelegate {
|
||||
if FeatureList.kUseProfileWebViewConfiguration.enabled {
|
||||
tab.readerMode = .init(tab: tab)
|
||||
}
|
||||
|
||||
tab.braveTalk = .init(tab: tab, coordinator: braveTalkJitsiCoordinator)
|
||||
tab.braveTalk?.onExitCall = { [weak self] in
|
||||
guard let self = self else { return }
|
||||
// When we close the call, redirect to Brave Talk home page if the selected tab is still the
|
||||
// original talk URL
|
||||
if let url = self.tabManager.selectedTab?.visibleURL,
|
||||
let currentHost = url.host,
|
||||
DomainUserScript.braveTalkHelper.associatedDomains.contains(currentHost)
|
||||
{
|
||||
var components = URLComponents()
|
||||
components.host = currentHost
|
||||
components.scheme = url.scheme
|
||||
self.select(url: components.url!, isUserDefinedURLNavigation: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tabManager(
|
||||
|
||||
+1
-8
@@ -431,14 +431,7 @@ extension BrowserViewController {
|
||||
}
|
||||
|
||||
if tab.profile.prefs.isBraveTalkAvailable {
|
||||
injectedScripts.append(
|
||||
BraveTalkScriptHandler(
|
||||
rewards: rewards,
|
||||
launchNativeBraveTalk: { [weak self] tab, room, token in
|
||||
self?.launchNativeBraveTalk(tab: tab, room: room, token: token)
|
||||
}
|
||||
)
|
||||
)
|
||||
injectedScripts.append(BraveTalkScriptHandler())
|
||||
}
|
||||
|
||||
if profileController.braveWalletAPI.isAllowed {
|
||||
|
||||
+2
-13
@@ -13,18 +13,7 @@ import WebKit
|
||||
import os.log
|
||||
|
||||
class BraveTalkScriptHandler: TabContentScript {
|
||||
private weak var rewards: BraveRewards?
|
||||
private let launchNativeBraveTalk:
|
||||
(_ tab: (any TabState)?, _ room: String, _ token: String) -> Void
|
||||
|
||||
required init(
|
||||
rewards: BraveRewards,
|
||||
launchNativeBraveTalk: @escaping (_ tab: (any TabState)?, _ room: String, _ token: String) ->
|
||||
Void
|
||||
) {
|
||||
self.rewards = rewards
|
||||
self.launchNativeBraveTalk = launchNativeBraveTalk
|
||||
}
|
||||
init() {}
|
||||
|
||||
static let scriptName = "BraveTalkScript"
|
||||
static let scriptId = UUID().uuidString
|
||||
@@ -109,7 +98,7 @@ class BraveTalkScriptHandler: TabContentScript {
|
||||
else {
|
||||
return
|
||||
}
|
||||
launchNativeBraveTalk(tab, room, jwt)
|
||||
tab.braveTalk?.launchBraveTalk(withRoom: room, jwtKey: jwt)
|
||||
replyHandler(nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2026 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 BraveCore
|
||||
@_spi(ChromiumWebViewAccess) import Web
|
||||
|
||||
extension TabDataValues {
|
||||
private struct BraveTalkTabHelperKey: TabDataKey {
|
||||
static var defaultValue: BraveTalkTabHelper?
|
||||
}
|
||||
public var braveTalk: BraveTalkTabHelper? {
|
||||
get { self[BraveTalkTabHelperKey.self] }
|
||||
set { self[BraveTalkTabHelperKey.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
public class BraveTalkTabHelper: TabObserver, @preconcurrency BraveTalkTabHelperBridge {
|
||||
private weak var tab: (any TabState)?
|
||||
private let coordinator: BraveTalkJitsiCoordinator
|
||||
public var onExitCall: (() -> Void)?
|
||||
|
||||
public init(tab: some TabState, coordinator: BraveTalkJitsiCoordinator) {
|
||||
self.tab = tab
|
||||
self.coordinator = coordinator
|
||||
|
||||
tab.addObserver(self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
tab?.removeObserver(self)
|
||||
}
|
||||
|
||||
public func tabDidCreateWebView(_ tab: some TabState) {
|
||||
if FeatureList.kUseProfileWebViewConfiguration.enabled {
|
||||
BraveWebView.from(tab: tab)?.setBraveTalkHelper(self)
|
||||
}
|
||||
}
|
||||
|
||||
public func tabWillBeDestroyed(_ tab: some TabState) {
|
||||
tab.removeObserver(self)
|
||||
}
|
||||
|
||||
public func launchBraveTalk(withRoom room: String, jwtKey: String) {
|
||||
guard let tab, let host = tab.lastCommittedURL?.host else { return }
|
||||
coordinator.launchNativeBraveTalk(
|
||||
for: room,
|
||||
token: jwtKey,
|
||||
host: host,
|
||||
onEnterCall: { [weak tab] in
|
||||
tab?.stopLoading()
|
||||
},
|
||||
onExitCall: { [weak self] in
|
||||
self?.onExitCall?()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import("//brave/components/brave_talk/buildflags/buildflags.gni")
|
||||
|
||||
config("config") {
|
||||
# For our own public headers to be able to import the official public headers
|
||||
include_dirs = [
|
||||
@@ -27,6 +29,7 @@ source_set("web_view") {
|
||||
"//base",
|
||||
"//brave/components/ai_chat/core/common",
|
||||
"//brave/components/ai_chat/ios/browser",
|
||||
"//brave/components/brave_talk/buildflags",
|
||||
"//brave/ios/browser/ai_chat",
|
||||
"//brave/ios/browser/api/profile",
|
||||
"//brave/ios/browser/api/web_view/autofill",
|
||||
@@ -53,6 +56,9 @@ source_set("web_view") {
|
||||
"//ios/web_view/framework:web_view_sources",
|
||||
"//net",
|
||||
]
|
||||
if (enable_brave_talk) {
|
||||
deps += [ "//brave/ios/browser/brave_talk" ]
|
||||
}
|
||||
frameworks = [
|
||||
"Foundation.framework",
|
||||
"UIKit.framework",
|
||||
|
||||
@@ -24,6 +24,7 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@protocol AIChatAssociatedContentPageFetcher;
|
||||
@protocol ProfileBridge;
|
||||
@protocol LoginsTabHelperBridge;
|
||||
@protocol BraveTalkTabHelperBridge;
|
||||
|
||||
typedef void (^ResetConfigurationCallback)(id<ProfileBridge>,
|
||||
WKWebViewConfiguration*);
|
||||
@@ -196,6 +197,12 @@ CWV_EXPORT
|
||||
fontSize:(NSInteger)fontSize;
|
||||
@end
|
||||
|
||||
CWV_EXPORT
|
||||
@interface BraveWebView (BraveTalk)
|
||||
/// A bridge for handling Brave Talk tab features
|
||||
- (void)setBraveTalkHelper:(id<BraveTalkTabHelperBridge>)braveTalkHelper;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif // BRAVE_IOS_BROWSER_API_WEB_VIEW_BRAVE_WEB_VIEW_H_
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "brave/components/ai_chat/core/common/features.h"
|
||||
#include "brave/components/ai_chat/ios/browser/ai_chat_associated_content_page_fetcher.h"
|
||||
#include "brave/components/ai_chat/ios/browser/ai_chat_tab_helper.h"
|
||||
#include "brave/components/brave_talk/buildflags/buildflags.h"
|
||||
#include "brave/ios/browser/ai_chat/ai_chat_distiller_javascript_feature.h"
|
||||
#include "brave/ios/browser/ai_chat/ai_chat_ui_handler_bridge_holder.h"
|
||||
#include "brave/ios/browser/ai_chat/tab_data_web_state_observer.h"
|
||||
@@ -22,6 +23,7 @@
|
||||
#include "brave/ios/browser/api/web_view/autofill/brave_web_view_autofill_client.h"
|
||||
#include "brave/ios/browser/api/web_view/passwords/brave_web_view_password_manager_client.h"
|
||||
#include "brave/ios/browser/brave_ads/ads_tab_helper.h"
|
||||
#include "brave/ios/browser/brave_talk/brave_talk_tab_helper_bridge.h"
|
||||
#include "brave/ios/browser/ui/web_view/features.h"
|
||||
#include "brave/ios/browser/ui/webui/brave_wallet/wallet_page_handler_bridge_holder.h"
|
||||
#include "brave/ios/browser/web/document_fetch/document_fetch_javascript_feature.h"
|
||||
@@ -65,6 +67,10 @@
|
||||
#include "net/base/apple/url_conversions.h"
|
||||
#include "net/http/http_status_code.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
#include "brave/ios/browser/brave_talk/brave_talk_tab_helper.h"
|
||||
#endif
|
||||
|
||||
@interface BraveNavigationAction ()
|
||||
- (instancetype)initWithRequest:(NSURLRequest*)request
|
||||
requestInfo:
|
||||
@@ -203,6 +209,9 @@ class BraveWebViewHolder : public web::WebStateUserData<BraveWebViewHolder> {
|
||||
aiChatUIHandler;
|
||||
@property(nonatomic, weak) id<WalletPageHandlerBridge> walletPageHandler;
|
||||
@property(nonatomic, weak) id<LoginsTabHelperBridge> loginsHelper;
|
||||
#if BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
@property(nonatomic, weak) id<BraveTalkTabHelperBridge> braveTalkHelper;
|
||||
#endif
|
||||
@end
|
||||
|
||||
@implementation BraveWebView {
|
||||
@@ -281,6 +290,11 @@ class BraveWebViewHolder : public web::WebStateUserData<BraveWebViewHolder> {
|
||||
}
|
||||
|
||||
brave_ads::AdsTabHelper::MaybeCreateForWebState(self.webState);
|
||||
#if BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
BraveTalkTabHelper::CreateForWebState(self.webState);
|
||||
BraveTalkTabHelper::FromWebState(self.webState)
|
||||
->SetBridge(self.braveTalkHelper);
|
||||
#endif
|
||||
|
||||
LoginsTabHelper::MaybeCreateForWebState(self.webState, _loginsHelper);
|
||||
}
|
||||
@@ -544,3 +558,19 @@ class BraveWebViewHolder : public web::WebStateUserData<BraveWebViewHolder> {
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation BraveWebView (BraveTalk)
|
||||
|
||||
/// A bridge for handling Brave Talk tab features
|
||||
- (void)setBraveTalkHelper:(id<BraveTalkTabHelperBridge>)braveTalkHelper {
|
||||
#if BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
_braveTalkHelper = braveTalkHelper;
|
||||
BraveTalkTabHelper* tab_helper =
|
||||
BraveTalkTabHelper::FromWebState(self.webState);
|
||||
if (tab_helper) {
|
||||
tab_helper->SetBridge(braveTalkHelper);
|
||||
}
|
||||
#endif // BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -4,16 +4,39 @@
|
||||
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import("//brave/components/brave_talk/buildflags/buildflags.gni")
|
||||
import("//ios/web/public/js_messaging/optimize_ts.gni")
|
||||
|
||||
assert(enable_brave_talk)
|
||||
|
||||
source_set("brave_talk") {
|
||||
sources = [
|
||||
"brave_talk_launcher_javascript_feature.h",
|
||||
"brave_talk_launcher_javascript_feature.mm",
|
||||
"brave_talk_pref_names_bridge.h",
|
||||
"brave_talk_pref_names_bridge.mm",
|
||||
"brave_talk_tab_helper.h",
|
||||
"brave_talk_tab_helper.mm",
|
||||
"brave_talk_tab_helper_bridge.h",
|
||||
]
|
||||
deps = [
|
||||
"//base",
|
||||
":brave_talk_launcher_js",
|
||||
"//brave/components/brave_talk",
|
||||
"//components/prefs",
|
||||
"//ios/chrome/browser/shared/model/profile",
|
||||
"//net",
|
||||
"//url",
|
||||
]
|
||||
public_deps = [
|
||||
"//base",
|
||||
"//ios/web/public",
|
||||
"//ios/web/public/js_messaging",
|
||||
]
|
||||
}
|
||||
|
||||
optimize_ts("brave_talk_launcher_js") {
|
||||
visibility = [ ":brave_talk" ]
|
||||
|
||||
sources = [ "resources/brave_talk_launcher.ts" ]
|
||||
|
||||
deps = [ "//ios/web/public/js_messaging:util_scripts" ]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2026 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_BRAVE_TALK_BRAVE_TALK_LAUNCHER_JAVASCRIPT_FEATURE_H_
|
||||
#define BRAVE_IOS_BROWSER_BRAVE_TALK_BRAVE_TALK_LAUNCHER_JAVASCRIPT_FEATURE_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "base/no_destructor.h"
|
||||
#include "ios/web/public/js_messaging/java_script_feature.h"
|
||||
|
||||
namespace web {
|
||||
class WebState;
|
||||
} // namespace web
|
||||
|
||||
// A JavaScriptFeature that observes iframe additions on Brave Talk pages and
|
||||
// notifies the BraveTalkTabHelper attached to the WebState to launch the
|
||||
// native Brave Talk experience.
|
||||
class BraveTalkLauncherJavaScriptFeature : public web::JavaScriptFeature {
|
||||
public:
|
||||
// This feature holds no state, so only a single static instance is ever
|
||||
// needed.
|
||||
static BraveTalkLauncherJavaScriptFeature* GetInstance();
|
||||
|
||||
// JavaScriptFeature:
|
||||
std::optional<std::string> GetScriptMessageHandlerName() const override;
|
||||
void ScriptMessageReceived(web::WebState* web_state,
|
||||
const web::ScriptMessage& message) override;
|
||||
|
||||
private:
|
||||
friend class base::NoDestructor<BraveTalkLauncherJavaScriptFeature>;
|
||||
|
||||
BraveTalkLauncherJavaScriptFeature();
|
||||
~BraveTalkLauncherJavaScriptFeature() override;
|
||||
|
||||
BraveTalkLauncherJavaScriptFeature(
|
||||
const BraveTalkLauncherJavaScriptFeature&) = delete;
|
||||
BraveTalkLauncherJavaScriptFeature& operator=(
|
||||
const BraveTalkLauncherJavaScriptFeature&) = delete;
|
||||
};
|
||||
|
||||
#endif // BRAVE_IOS_BROWSER_BRAVE_TALK_BRAVE_TALK_LAUNCHER_JAVASCRIPT_FEATURE_H_
|
||||
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2026 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/brave_talk/brave_talk_launcher_javascript_feature.h"
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "base/containers/fixed_flat_set.h"
|
||||
#include "base/values.h"
|
||||
#include "brave/ios/browser/brave_talk/brave_talk_tab_helper.h"
|
||||
#include "ios/web/public/js_messaging/script_message.h"
|
||||
#include "ios/web/public/web_state.h"
|
||||
#include "net/base/url_util.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr char kScriptName[] = "brave_talk_launcher";
|
||||
constexpr char kScriptHandlerName[] = "BraveTalkLauncherMessageHandler";
|
||||
constexpr char kMessageURLKey[] = "url";
|
||||
constexpr auto kAllowedUrlHosts = base::MakeFixedFlatSet<std::string_view>({
|
||||
"talk.brave.com",
|
||||
"talk.bravesoftware.com",
|
||||
"talk.brave.software",
|
||||
});
|
||||
|
||||
} // namespace
|
||||
|
||||
BraveTalkLauncherJavaScriptFeature::BraveTalkLauncherJavaScriptFeature()
|
||||
: JavaScriptFeature(web::ContentWorld::kIsolatedWorld,
|
||||
{FeatureScript::CreateWithFilename(
|
||||
kScriptName,
|
||||
FeatureScript::InjectionTime::kDocumentStart,
|
||||
FeatureScript::TargetFrames::kMainFrame,
|
||||
FeatureScript::ReinjectionBehavior::
|
||||
kReinjectOnDocumentRecreation)}) {}
|
||||
|
||||
BraveTalkLauncherJavaScriptFeature::~BraveTalkLauncherJavaScriptFeature() =
|
||||
default;
|
||||
|
||||
// static
|
||||
BraveTalkLauncherJavaScriptFeature*
|
||||
BraveTalkLauncherJavaScriptFeature::GetInstance() {
|
||||
static base::NoDestructor<BraveTalkLauncherJavaScriptFeature> instance;
|
||||
return instance.get();
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
BraveTalkLauncherJavaScriptFeature::GetScriptMessageHandlerName() const {
|
||||
return kScriptHandlerName;
|
||||
}
|
||||
|
||||
void BraveTalkLauncherJavaScriptFeature::ScriptMessageReceived(
|
||||
web::WebState* web_state,
|
||||
const web::ScriptMessage& message) {
|
||||
GURL request_url = message.request_url().value_or(GURL());
|
||||
|
||||
if (!message.is_main_frame() || !request_url.is_valid() ||
|
||||
!kAllowedUrlHosts.contains(request_url.host())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const base::DictValue* body =
|
||||
message.body() ? message.body()->GetIfDict() : nullptr;
|
||||
if (!body) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string* url_string = body->FindString(kMessageURLKey);
|
||||
if (!url_string) {
|
||||
return;
|
||||
}
|
||||
|
||||
const GURL jitsi_url(*url_string);
|
||||
if (!jitsi_url.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The room name is the first path component of jitsi frame url (stripping the
|
||||
// the leading '/').
|
||||
const std::string_view room =
|
||||
jitsi_url.path().empty() ? "" : jitsi_url.path().substr(1);
|
||||
if (room.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string jwt;
|
||||
if (!net::GetValueForKeyInQuery(jitsi_url, "jwt", &jwt) || jwt.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
BraveTalkTabHelper* tab_helper = BraveTalkTabHelper::FromWebState(web_state);
|
||||
if (!tab_helper) {
|
||||
return;
|
||||
}
|
||||
tab_helper->LaunchBraveTalk(room, jwt);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2026 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_BRAVE_TALK_BRAVE_TALK_TAB_HELPER_H_
|
||||
#define BRAVE_IOS_BROWSER_BRAVE_TALK_BRAVE_TALK_TAB_HELPER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "ios/web/public/web_state_user_data.h"
|
||||
|
||||
@protocol BraveTalkTabHelperBridge;
|
||||
|
||||
namespace web {
|
||||
class WebState;
|
||||
} // namespace web
|
||||
|
||||
class BraveTalkTabHelper : public web::WebStateUserData<BraveTalkTabHelper> {
|
||||
public:
|
||||
~BraveTalkTabHelper() override;
|
||||
|
||||
void SetBridge(id<BraveTalkTabHelperBridge> bridge);
|
||||
|
||||
// Forwards a launch request to the registered delegate, if any.
|
||||
void LaunchBraveTalk(const std::string_view room, const std::string_view jwt);
|
||||
|
||||
private:
|
||||
friend class web::WebStateUserData<BraveTalkTabHelper>;
|
||||
|
||||
explicit BraveTalkTabHelper(web::WebState* web_state);
|
||||
|
||||
raw_ptr<web::WebState> web_state_ = nullptr; // not owned
|
||||
__weak id<BraveTalkTabHelperBridge> bridge_;
|
||||
};
|
||||
|
||||
#endif // BRAVE_IOS_BROWSER_BRAVE_TALK_BRAVE_TALK_TAB_HELPER_H_
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2026 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/brave_talk/brave_talk_tab_helper.h"
|
||||
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "brave/components/brave_talk/pref_names.h"
|
||||
#include "brave/ios/browser/brave_talk/brave_talk_tab_helper_bridge.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "ios/chrome/browser/shared/model/profile/profile_ios.h"
|
||||
|
||||
namespace {
|
||||
|
||||
bool IsBraveTalkDisabledByPolicy(PrefService* prefs) {
|
||||
return prefs->IsManagedPreference(brave_talk::prefs::kDisabledByPolicy) &&
|
||||
prefs->GetBoolean(brave_talk::prefs::kDisabledByPolicy);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BraveTalkTabHelper::BraveTalkTabHelper(web::WebState* web_state)
|
||||
: web_state_(web_state) {}
|
||||
|
||||
BraveTalkTabHelper::~BraveTalkTabHelper() = default;
|
||||
|
||||
void BraveTalkTabHelper::SetBridge(id<BraveTalkTabHelperBridge> bridge) {
|
||||
bridge_ = bridge;
|
||||
}
|
||||
|
||||
void BraveTalkTabHelper::LaunchBraveTalk(const std::string_view room,
|
||||
const std::string_view jwt) {
|
||||
PrefService* prefs =
|
||||
ProfileIOS::FromBrowserState(web_state_->GetBrowserState())->GetPrefs();
|
||||
if (IsBraveTalkDisabledByPolicy(prefs) || !bridge_) {
|
||||
return;
|
||||
}
|
||||
[bridge_ launchBraveTalkWithRoom:base::SysUTF8ToNSString(room)
|
||||
jwtKey:base::SysUTF8ToNSString(jwt)];
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2026 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_BRAVE_TALK_BRAVE_TALK_TAB_HELPER_BRIDGE_H_
|
||||
#define BRAVE_IOS_BROWSER_BRAVE_TALK_BRAVE_TALK_TAB_HELPER_BRIDGE_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@protocol BraveTalkTabHelperBridge
|
||||
@required
|
||||
|
||||
- (void)launchBraveTalkWithRoom:(NSString*)room jwtKey:(NSString*)jwtKey;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif // BRAVE_IOS_BROWSER_BRAVE_TALK_BRAVE_TALK_TAB_HELPER_BRIDGE_H_
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright (c) 2026 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 {sendWebKitMessage} from
|
||||
'//ios/web/public/js_messaging/resources/utils.js';
|
||||
|
||||
const allowedOrigins = [
|
||||
'https://talk.brave.com',
|
||||
'https://talk.bravesoftware.com',
|
||||
'https://talk.brave.software',
|
||||
];
|
||||
|
||||
if (allowedOrigins.includes(window.location.origin)) {
|
||||
const observer = new MutationObserver((mutations: MutationRecord[]) => {
|
||||
for (const mutation of mutations) {
|
||||
for (const node of mutation.addedNodes) {
|
||||
if (node instanceof HTMLIFrameElement) {
|
||||
sendWebKitMessage(
|
||||
'BraveTalkLauncherMessageHandler', {url: node.src});
|
||||
observer.disconnect();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document, {childList: true, subtree: true});
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
import("//brave/components/brave_talk/buildflags/buildflags.gni")
|
||||
import("//brave/components/brave_wallet/common/buildflags/buildflags.gni")
|
||||
|
||||
source_set("web") {
|
||||
@@ -16,6 +17,7 @@ source_set("web") {
|
||||
"//base",
|
||||
"//brave/components/ai_chat/core/browser",
|
||||
"//brave/components/brave_component_updater/browser:public",
|
||||
"//brave/components/brave_talk/buildflags",
|
||||
"//brave/components/brave_user_agent/browser",
|
||||
"//brave/components/constants",
|
||||
"//brave/ios/browser/ai_chat",
|
||||
@@ -47,6 +49,9 @@ source_set("web") {
|
||||
"//ios/public/provider/chrome/browser/url_rewriters:url_rewriters_api",
|
||||
"//ios/web_view/framework:web_view_sources",
|
||||
]
|
||||
if (enable_brave_talk) {
|
||||
deps += [ "//brave/ios/browser/brave_talk" ]
|
||||
}
|
||||
if (enable_brave_wallet) {
|
||||
deps += [ "//brave/components/brave_wallet/browser" ]
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "base/ios/ns_error_util.h"
|
||||
#include "base/notimplemented.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "brave/components/brave_talk/buildflags/buildflags.h"
|
||||
#include "brave/components/constants/url_constants.h"
|
||||
#include "brave/ios/browser/ai_chat/ai_chat_distiller_javascript_feature.h"
|
||||
#include "brave/ios/browser/api/profile/profile_bridge_impl.h"
|
||||
@@ -47,6 +48,10 @@
|
||||
#import "net/base/apple/url_conversions.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
#if BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
#include "brave/ios/browser/brave_talk/brave_talk_launcher_javascript_feature.h"
|
||||
#endif
|
||||
|
||||
BraveWebClient::BraveWebClient() {}
|
||||
|
||||
BraveWebClient::~BraveWebClient() {}
|
||||
@@ -113,12 +118,14 @@ std::vector<web::JavaScriptFeature*> BraveWebClient::GetJavaScriptFeatures(
|
||||
features.push_back(
|
||||
brave_ads::AdsMediaReportingJavaScriptFeature::GetInstance());
|
||||
features.push_back(AIChatDistillerJavaScriptFeature::GetInstance());
|
||||
#if BUILDFLAG(ENABLE_BRAVE_TALK)
|
||||
features.push_back(BraveTalkLauncherJavaScriptFeature::GetInstance());
|
||||
#endif
|
||||
features.push_back(DeAmpJavaScriptFeature::GetInstance());
|
||||
features.push_back(DocumentFetchJavaScriptFeature::GetInstance());
|
||||
features.push_back(ForcePasteJavaScriptFeature::GetInstance());
|
||||
features.push_back(PageMetadataJavaScriptFeature::GetInstance());
|
||||
features.push_back(brave::ReaderModeJavaScriptFeature::GetInstance());
|
||||
|
||||
if (!base::FeatureList::IsEnabled(
|
||||
brave::features::kUseChromiumWebViewsAutofill)) {
|
||||
features.push_back(LoginsJavaScriptFeature::GetInstance());
|
||||
|
||||
Reference in New Issue
Block a user