Wire up BraveOrigin startup dialog to browser launch (#34555)
* Add BraveOrigin startup dialog view Adds BraveOriginStartupView, a standalone modal window that hosts the startup WebUI dialog. Manages system profile creation, web contents lifecycle, and buy window navigation. * Address review feedback for BraveOrigin startup view - Use WidgetDelegate instead of deprecated WidgetDelegateView - Use WebView(profile) + LoadInitialURL instead of manual WebContents - Remove redundant GetAccessibleWindowTitle/CalculatePreferredSize overrides - Simplify SetTitle to use message ID directly - Fix g_startup_view leak with unique_ptr + cleanup in WindowClosing - Remove chromium_src WidgetDelegateView friend class override * Wire up BraveOrigin startup dialog to browser launch Intercepts StartupBrowserCreator::Start to show the purchase validation dialog before any browser window opens. Blocks Mac app commands while the dialog is showing. * Address review feedback and add test coverage for startup view Refactor BraveOriginStartupView to use a Delegate interface instead of raw callbacks, making the code more testable and readable. Move the --test-type guard into ShouldShowDialog() and add SetShouldShowDialogForTesting() for browser test support. Use __VA_ARGS__ in the startup_browser_creator.h macros. Add unit tests covering ShouldShowDialog with various pref/SKU credential states. * Add browser tests for BraveOrigin startup dialog * Add integration tests for StartupBrowserCreator::Start override Exercise the real startup interception logic that shows the BraveOrigin dialog, covering both validation and close paths.
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
#include "brave/app/brave_command_ids.h"
|
||||
#include "brave/browser/brave_browser_features.h"
|
||||
#include "brave/browser/ui/browser_commands.h"
|
||||
#include "brave/components/brave_origin/buildflags/buildflags.h"
|
||||
#include "brave/components/tor/buildflags/buildflags.h"
|
||||
#include "chrome/app/chrome_command_ids.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
@@ -23,6 +24,10 @@
|
||||
#include "components/prefs/pref_member.h"
|
||||
#include "ui/base/l10n/l10n_util_mac.h"
|
||||
|
||||
#if BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
#include "brave/browser/ui/views/brave_origin/brave_origin_startup_view.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(ENABLE_TOR)
|
||||
#include "brave/browser/tor/tor_profile_service_factory.h"
|
||||
#include "brave/components/tor/pref_names.h"
|
||||
@@ -73,8 +78,9 @@ class TorPrefObserver : public BooleanPrefMember {
|
||||
} // namespace
|
||||
|
||||
@interface AppController (Brave)
|
||||
// Expose method in chrome/..app_controller_mac.mm
|
||||
// Expose methods in chrome/..app_controller_mac.mm
|
||||
- (BOOL)canOpenNewBrowser;
|
||||
- (void)commandDispatchUsingKeyModifiers:(id)sender;
|
||||
@end
|
||||
|
||||
@interface BraveAppController () {
|
||||
@@ -91,6 +97,43 @@ class TorPrefObserver : public BooleanPrefMember {
|
||||
@end
|
||||
|
||||
@implementation BraveAppController
|
||||
|
||||
#if BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
- (BOOL)canOpenNewBrowser {
|
||||
if (BraveOriginStartupView::IsShowing()) {
|
||||
return NO;
|
||||
}
|
||||
return [super canOpenNewBrowser];
|
||||
}
|
||||
|
||||
- (void)commandDispatch:(id)sender {
|
||||
if (BraveOriginStartupView::IsShowing()) {
|
||||
// Allow Quit through so the user can exit from the startup dialog.
|
||||
if ([sender tag] == IDC_EXIT) {
|
||||
BraveOriginStartupView::Hide();
|
||||
}
|
||||
return;
|
||||
}
|
||||
[super commandDispatch:sender];
|
||||
}
|
||||
|
||||
- (void)commandDispatchUsingKeyModifiers:(id)sender {
|
||||
if (BraveOriginStartupView::IsShowing()) {
|
||||
return;
|
||||
}
|
||||
[super commandDispatchUsingKeyModifiers:sender];
|
||||
}
|
||||
|
||||
- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
|
||||
hasVisibleWindows:(BOOL)hasVisibleWindows {
|
||||
if (BraveOriginStartupView::IsShowing()) {
|
||||
return NO;
|
||||
}
|
||||
return [super applicationShouldHandleReopen:theApplication
|
||||
hasVisibleWindows:hasVisibleWindows];
|
||||
}
|
||||
#endif // BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
|
||||
- (void)mainMenuCreated {
|
||||
[super mainMenuCreated];
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<html dir="$i18n{textdirection}" lang="$i18n{language}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>$i18n{braveOriginStartupTitle}</title>
|
||||
<link rel="stylesheet" href="chrome://resources/brave/css/nala.css">
|
||||
<title>$i18n{BRAVE_ORIGIN_STARTUP_TITLE}</title>
|
||||
<link rel="stylesheet" href="chrome://resources/brave/leo/css/variables.css">
|
||||
<link rel="stylesheet" href="chrome://resources/brave/css/reset.css">
|
||||
<link rel="stylesheet" href="chrome://resources/css/text_defaults.css">
|
||||
<link rel="stylesheet" href="/components/app.css">
|
||||
|
||||
+4
-2
@@ -387,8 +387,10 @@ if (enable_brave_education) {
|
||||
}
|
||||
|
||||
if (is_brave_origin_branded) {
|
||||
brave_chrome_browser_deps +=
|
||||
[ "//brave/browser/ui/webui/brave_origin_startup" ]
|
||||
brave_chrome_browser_deps += [
|
||||
"//brave/browser/ui/views/brave_origin",
|
||||
"//brave/browser/ui/webui/brave_origin_startup",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_ai_chat) {
|
||||
|
||||
@@ -33,3 +33,45 @@ source_set("brave_origin") {
|
||||
"//url",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("browser_tests") {
|
||||
testonly = true
|
||||
|
||||
defines = [ "HAS_OUT_OF_PROC_TEST_RUNNER" ]
|
||||
|
||||
sources = [ "brave_origin_startup_view_browsertest.cc" ]
|
||||
|
||||
deps = [
|
||||
":brave_origin",
|
||||
"//base",
|
||||
"//brave/components/brave_origin:pref_names",
|
||||
"//brave/components/skus/browser",
|
||||
"//chrome/browser",
|
||||
"//chrome/browser:browser_process",
|
||||
"//chrome/browser/profiles:profile",
|
||||
"//chrome/browser/ui",
|
||||
"//chrome/browser/ui/startup",
|
||||
"//chrome/test:test_support_ui",
|
||||
"//components/prefs",
|
||||
"//content/public/browser",
|
||||
"//content/test:test_support",
|
||||
"//testing/gtest",
|
||||
"//ui/views",
|
||||
"//url",
|
||||
]
|
||||
}
|
||||
|
||||
source_set("unit_tests") {
|
||||
testonly = true
|
||||
|
||||
sources = [ "brave_origin_startup_view_unittest.cc" ]
|
||||
|
||||
deps = [
|
||||
":brave_origin",
|
||||
"//base",
|
||||
"//brave/components/brave_origin:pref_names",
|
||||
"//brave/components/skus/browser",
|
||||
"//components/prefs:test_support",
|
||||
"//testing/gtest",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/json/json_reader.h"
|
||||
#include "base/strings/strcat.h"
|
||||
@@ -25,6 +26,7 @@
|
||||
#include "content/public/browser/render_frame_host.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_ui.h"
|
||||
#include "content/public/common/content_switches.h"
|
||||
#include "ui/gfx/geometry/size.h"
|
||||
#include "ui/views/controls/webview/webview.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
@@ -34,9 +36,10 @@
|
||||
namespace {
|
||||
|
||||
constexpr int kDialogWidth = 500;
|
||||
constexpr int kDialogHeight = 600;
|
||||
constexpr int kDialogHeight = 450;
|
||||
|
||||
BraveOriginStartupView* g_startup_view = nullptr;
|
||||
std::optional<bool> g_should_show_dialog_override;
|
||||
|
||||
bool HasOriginSkuCredentials(PrefService* local_state) {
|
||||
const auto& skus_state = local_state->GetDict(skus::prefs::kSkusState);
|
||||
@@ -65,23 +68,33 @@ bool HasOriginSkuCredentials(PrefService* local_state) {
|
||||
|
||||
// static
|
||||
bool BraveOriginStartupView::ShouldShowDialog(PrefService* local_state) {
|
||||
if (g_should_show_dialog_override.has_value()) {
|
||||
return *g_should_show_dialog_override;
|
||||
}
|
||||
|
||||
// Skip the dialog when running under test infrastructure.
|
||||
if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kTestType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !local_state->GetBoolean(brave_origin::kOriginPurchaseValidated) ||
|
||||
!HasOriginSkuCredentials(local_state);
|
||||
}
|
||||
|
||||
// static
|
||||
void BraveOriginStartupView::Show(
|
||||
base::OnceClosure on_complete,
|
||||
OpenExternalCallback open_external,
|
||||
base::RepeatingClosure attempt_exit,
|
||||
CreateProfilesCallback create_system_profile,
|
||||
CreateProfilesCallback create_default_profile) {
|
||||
void BraveOriginStartupView::SetShouldShowDialogForTesting( // IN-TEST
|
||||
std::optional<bool> override) {
|
||||
g_should_show_dialog_override = override;
|
||||
}
|
||||
|
||||
// static
|
||||
void BraveOriginStartupView::Show(base::OnceClosure on_complete,
|
||||
std::unique_ptr<Delegate> delegate) {
|
||||
if (g_startup_view) {
|
||||
return;
|
||||
}
|
||||
g_startup_view = new BraveOriginStartupView(
|
||||
std::move(on_complete), std::move(open_external), std::move(attempt_exit),
|
||||
std::move(create_system_profile), std::move(create_default_profile));
|
||||
g_startup_view =
|
||||
new BraveOriginStartupView(std::move(on_complete), std::move(delegate));
|
||||
g_startup_view->Display();
|
||||
}
|
||||
|
||||
@@ -97,20 +110,21 @@ bool BraveOriginStartupView::IsShowing() {
|
||||
return g_startup_view != nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
void BraveOriginStartupView::ValidateForTesting() { // IN-TEST
|
||||
if (g_startup_view && g_startup_view->GetWidget()) {
|
||||
g_startup_view->CloseAndProceed();
|
||||
}
|
||||
}
|
||||
|
||||
BraveOriginStartupView::BraveOriginStartupView(
|
||||
base::OnceClosure on_complete,
|
||||
OpenExternalCallback open_external,
|
||||
base::RepeatingClosure attempt_exit,
|
||||
CreateProfilesCallback create_system_profile,
|
||||
CreateProfilesCallback create_default_profile)
|
||||
std::unique_ptr<Delegate> delegate)
|
||||
: keep_alive_(
|
||||
std::make_unique<ScopedKeepAlive>(KeepAliveOrigin::USER_MANAGER_VIEW,
|
||||
KeepAliveRestartOption::DISABLED)),
|
||||
on_complete_(std::move(on_complete)),
|
||||
open_external_(std::move(open_external)),
|
||||
attempt_exit_(std::move(attempt_exit)),
|
||||
create_system_profile_(std::move(create_system_profile)),
|
||||
create_default_profile_(std::move(create_default_profile)) {
|
||||
delegate_(std::move(delegate)),
|
||||
on_complete_(std::move(on_complete)) {
|
||||
SetHasWindowSizeControls(false);
|
||||
SetTitle(IDS_BRAVE_ORIGIN_STARTUP_TITLE);
|
||||
}
|
||||
@@ -124,10 +138,10 @@ BraveOriginStartupView::~BraveOriginStartupView() {
|
||||
void BraveOriginStartupView::Display() {
|
||||
// Load both the system profile (for WebUI) and the default user profile
|
||||
// (for SKU service, which requires a regular profile).
|
||||
create_system_profile_.Run(
|
||||
delegate_->CreateSystemProfile(
|
||||
base::BindOnce(&BraveOriginStartupView::OnSystemProfileCreated,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
create_default_profile_.Run(
|
||||
delegate_->CreateDefaultProfile(
|
||||
base::BindOnce(&BraveOriginStartupView::OnDefaultProfileCreated,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
@@ -239,8 +253,8 @@ void BraveOriginStartupView::OpenBuyWindow() {
|
||||
"account", brave_domains::ServicesEnvironment::STAGING),
|
||||
"/?intent=checkout&product=origin"});
|
||||
GURL gurl(url);
|
||||
if (gurl.is_valid() && open_external_) {
|
||||
open_external_.Run(gurl);
|
||||
if (gurl.is_valid() && delegate_) {
|
||||
delegate_->OpenExternal(gurl);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,8 +317,14 @@ void BraveOriginStartupView::WindowClosing() {
|
||||
std::unique_ptr<ScopedProfileKeepAlive>) {},
|
||||
std::move(keep_alive_), std::move(profile_keep_alive_)));
|
||||
|
||||
if (!validated_) {
|
||||
task_runner->PostTask(FROM_HERE, attempt_exit_);
|
||||
if (!validated_ && delegate_) {
|
||||
// Move delegate_ into the closure so it outlives `this` (which is deleted
|
||||
// in WidgetIsZombie right after WindowClosing returns).
|
||||
task_runner->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(
|
||||
[](std::unique_ptr<Delegate> delegate) { delegate->AttemptExit(); },
|
||||
std::move(delegate_)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
static_assert(BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED));
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "base/functional/callback.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
@@ -42,39 +43,56 @@ class BraveOriginStartupView : public views::WidgetDelegate,
|
||||
public content::WebContentsDelegate,
|
||||
public content::WebContentsObserver {
|
||||
public:
|
||||
using ProfileCallback = base::OnceCallback<void(Profile*)>;
|
||||
|
||||
// Delegate interface for external dependencies. Implemented by the browser
|
||||
// startup code and easily mockable in tests.
|
||||
class Delegate {
|
||||
public:
|
||||
virtual ~Delegate() = default;
|
||||
|
||||
// Opens |url| in the user's default external browser.
|
||||
virtual void OpenExternal(const GURL& url) = 0;
|
||||
|
||||
// Exits the browser process.
|
||||
virtual void AttemptExit() = 0;
|
||||
|
||||
// Asynchronously creates the system profile and passes it to |callback|.
|
||||
virtual void CreateSystemProfile(ProfileCallback callback) = 0;
|
||||
|
||||
// Asynchronously creates the default user profile and passes it to
|
||||
// |callback|.
|
||||
virtual void CreateDefaultProfile(ProfileCallback callback) = 0;
|
||||
};
|
||||
|
||||
// Returns true if the startup dialog should be shown (purchase not yet
|
||||
// validated or SKU credentials missing).
|
||||
// validated or SKU credentials missing). Also returns false when running
|
||||
// under test infrastructure (--test-type flag).
|
||||
static bool ShouldShowDialog(PrefService* local_state);
|
||||
|
||||
// Override ShouldShowDialog() result for testing. Pass std::nullopt to
|
||||
// remove the override and restore normal behavior.
|
||||
static void SetShouldShowDialogForTesting(std::optional<bool> override);
|
||||
|
||||
// Shows the startup dialog. |on_complete| is called when the user has
|
||||
// been validated and the dialog closes, to continue the startup flow.
|
||||
// |open_external| is called to open URLs in the default browser.
|
||||
// |attempt_exit| is called to terminate the browser when the dialog is
|
||||
// closed without validation.
|
||||
using OpenExternalCallback = base::RepeatingCallback<void(const GURL&)>;
|
||||
using ProfileCallback = base::OnceCallback<void(Profile*)>;
|
||||
using CreateProfilesCallback = base::RepeatingCallback<void(ProfileCallback)>;
|
||||
|
||||
// |on_complete| runs when the user validates; |open_external| opens URLs
|
||||
// externally; |attempt_exit| exits the browser; |create_system_profile| and
|
||||
// |create_default_profile| load the required profiles asynchronously.
|
||||
// |delegate| provides external operations (opening URLs, exiting, creating
|
||||
// profiles).
|
||||
static void Show(base::OnceClosure on_complete,
|
||||
OpenExternalCallback open_external,
|
||||
base::RepeatingClosure attempt_exit,
|
||||
CreateProfilesCallback create_system_profile,
|
||||
CreateProfilesCallback create_default_profile);
|
||||
std::unique_ptr<Delegate> delegate);
|
||||
static void Hide();
|
||||
static bool IsShowing();
|
||||
|
||||
// Simulates a successful validation for testing. Calls CloseAndProceed() on
|
||||
// the current instance, triggering the on_complete callback.
|
||||
static void ValidateForTesting(); // IN-TEST
|
||||
|
||||
BraveOriginStartupView(const BraveOriginStartupView&) = delete;
|
||||
BraveOriginStartupView& operator=(const BraveOriginStartupView&) = delete;
|
||||
|
||||
private:
|
||||
BraveOriginStartupView(base::OnceClosure on_complete,
|
||||
OpenExternalCallback open_external,
|
||||
base::RepeatingClosure attempt_exit,
|
||||
CreateProfilesCallback create_system_profile,
|
||||
CreateProfilesCallback create_default_profile);
|
||||
std::unique_ptr<Delegate> delegate);
|
||||
~BraveOriginStartupView() override;
|
||||
|
||||
void Display();
|
||||
@@ -120,15 +138,12 @@ class BraveOriginStartupView : public views::WidgetDelegate,
|
||||
std::unique_ptr<ScopedKeepAlive> keep_alive_;
|
||||
std::unique_ptr<ScopedProfileKeepAlive> profile_keep_alive_;
|
||||
std::unique_ptr<views::WebView> web_view_;
|
||||
std::unique_ptr<Delegate> delegate_;
|
||||
|
||||
int profiles_loaded_count_ = 0;
|
||||
|
||||
bool validated_ = false;
|
||||
base::OnceClosure on_complete_;
|
||||
OpenExternalCallback open_external_;
|
||||
base::RepeatingClosure attempt_exit_;
|
||||
CreateProfilesCallback create_system_profile_;
|
||||
CreateProfilesCallback create_default_profile_;
|
||||
|
||||
views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_;
|
||||
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
/* 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/browser/ui/views/brave_origin/brave_origin_startup_view.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/run_until.h"
|
||||
#include "brave/components/brave_origin/pref_names.h"
|
||||
#include "brave/components/skus/browser/pref_names.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/profiles/profile_manager.h"
|
||||
#include "chrome/browser/ui/browser.h"
|
||||
#include "chrome/browser/ui/browser_finder.h"
|
||||
#include "chrome/browser/ui/startup/startup_browser_creator.h"
|
||||
#include "chrome/test/base/in_process_browser_test.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#include "content/public/test/browser_test.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Test delegate that uses real profile infrastructure but records calls to
|
||||
// OpenExternal and AttemptExit via shared flags (the delegate is moved into
|
||||
// an async closure during WindowClosing, so raw pointers to it can dangle).
|
||||
class TestDelegate : public BraveOriginStartupView::Delegate {
|
||||
public:
|
||||
explicit TestDelegate(bool* attempt_exit_flag)
|
||||
: attempt_exit_flag_(attempt_exit_flag) {}
|
||||
|
||||
void OpenExternal(const GURL& url) override {}
|
||||
|
||||
void AttemptExit() override {
|
||||
if (attempt_exit_flag_) {
|
||||
*attempt_exit_flag_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CreateSystemProfile(
|
||||
BraveOriginStartupView::ProfileCallback callback) override {
|
||||
g_browser_process->profile_manager()->CreateProfileAsync(
|
||||
ProfileManager::GetSystemProfilePath(), std::move(callback));
|
||||
}
|
||||
|
||||
void CreateDefaultProfile(
|
||||
BraveOriginStartupView::ProfileCallback callback) override {
|
||||
g_browser_process->profile_manager()->CreateProfileAsync(
|
||||
g_browser_process->profile_manager()->GetLastUsedProfileDir(),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
private:
|
||||
raw_ptr<bool> attempt_exit_flag_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
class BraveOriginStartupViewBrowserTest : public InProcessBrowserTest {
|
||||
public:
|
||||
void SetUp() override {
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
InProcessBrowserTest::SetUp();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
InProcessBrowserTest::TearDown();
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
}
|
||||
|
||||
void TearDownOnMainThread() override {
|
||||
if (BraveOriginStartupView::IsShowing()) {
|
||||
HideAndWaitForClose();
|
||||
}
|
||||
InProcessBrowserTest::TearDownOnMainThread();
|
||||
}
|
||||
|
||||
// Hide() is a no-op until the widget exists (async profile creation may
|
||||
// still be in progress). Retry Hide() inside RunUntil so that pending tasks
|
||||
// are processed between attempts, and the widget is closed as soon as it
|
||||
// becomes available.
|
||||
void HideAndWaitForClose() {
|
||||
ASSERT_TRUE(base::test::RunUntil([] {
|
||||
BraveOriginStartupView::Hide();
|
||||
return !BraveOriginStartupView::IsShowing();
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Verifies the dialog can be shown and IsShowing() reflects its state.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest, ShowAndHide) {
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
bool on_complete_called = false;
|
||||
BraveOriginStartupView::Show(
|
||||
base::BindLambdaForTesting([&]() { on_complete_called = true; }),
|
||||
std::make_unique<TestDelegate>(nullptr));
|
||||
|
||||
EXPECT_TRUE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
HideAndWaitForClose();
|
||||
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
// Closing without validation should not call on_complete.
|
||||
EXPECT_FALSE(on_complete_called);
|
||||
}
|
||||
|
||||
// Verifies that calling Show() twice does not create a second dialog.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest, ShowTwiceIsNoOp) {
|
||||
BraveOriginStartupView::Show(base::DoNothing(),
|
||||
std::make_unique<TestDelegate>(nullptr));
|
||||
EXPECT_TRUE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
// Second Show should be a no-op.
|
||||
BraveOriginStartupView::Show(base::DoNothing(),
|
||||
std::make_unique<TestDelegate>(nullptr));
|
||||
EXPECT_TRUE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
HideAndWaitForClose();
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
}
|
||||
|
||||
// Verifies that Hide() is safe to call when dialog is not showing.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
HideWhenNotShowingIsNoOp) {
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
BraveOriginStartupView::Hide(); // Should not crash.
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
}
|
||||
|
||||
// Verifies that the testing override controls ShouldShowDialog().
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
ShouldShowDialogTestingOverride) {
|
||||
PrefService* local_state = g_browser_process->local_state();
|
||||
|
||||
// Default in tests: --test-type flag makes it return false.
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(local_state));
|
||||
|
||||
// Override to force show.
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(true);
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(local_state));
|
||||
|
||||
// Override to force hide.
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(false);
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(local_state));
|
||||
|
||||
// Reset override.
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(local_state));
|
||||
}
|
||||
|
||||
// Verifies closing without validation calls AttemptExit on the delegate.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
CloseWithoutValidationCallsAttemptExit) {
|
||||
bool attempt_exit_called = false;
|
||||
|
||||
BraveOriginStartupView::Show(
|
||||
base::DoNothing(), std::make_unique<TestDelegate>(&attempt_exit_called));
|
||||
EXPECT_FALSE(attempt_exit_called);
|
||||
|
||||
HideAndWaitForClose();
|
||||
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
EXPECT_TRUE(attempt_exit_called);
|
||||
}
|
||||
|
||||
// Verifies that when the purchase is validated and SKU credentials exist,
|
||||
// the dialog does not appear and the browser window opens normally.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
PaidUserSkipsDialogAndGetsBrowser) {
|
||||
PrefService* local_state = g_browser_process->local_state();
|
||||
|
||||
// Simulate a paid user: set validated pref and add SKU credentials.
|
||||
local_state->SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
base::DictValue skus_state;
|
||||
skus_state.Set("production", R"({
|
||||
"credentials": {
|
||||
"items": {
|
||||
"origin": "some-credential-value"
|
||||
}
|
||||
}
|
||||
})");
|
||||
local_state->SetDict(skus::prefs::kSkusState, std::move(skus_state));
|
||||
|
||||
// With valid purchase state, ShouldShowDialog returns false (even without
|
||||
// the --test-type override).
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(local_state));
|
||||
|
||||
// The dialog is not showing, and we have a browser window (provided by the
|
||||
// test framework via the normal startup path).
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
EXPECT_TRUE(browser() != nullptr);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Integration tests for the StartupBrowserCreator::Start override.
|
||||
// These call Start() directly to exercise the real startup interception logic
|
||||
// in chromium_src/chrome/browser/ui/startup/startup_browser_creator.cc.
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
// Verifies that StartupBrowserCreator::Start() shows the dialog when
|
||||
// ShouldShowDialog is true, and that closing without validation does not crash.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
StartOverrideShowsDialogOnClose) {
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(true);
|
||||
|
||||
// Call the real Start() override. It should intercept and show the dialog.
|
||||
StartupBrowserCreator browser_creator;
|
||||
StartupProfileInfo profile_info{browser()->profile(),
|
||||
StartupProfileMode::kBrowserWindow};
|
||||
bool result = browser_creator.Start(*base::CommandLine::ForCurrentProcess(),
|
||||
base::FilePath(), profile_info,
|
||||
{browser()->profile()});
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_TRUE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
// Close without validation — should not crash.
|
||||
HideAndWaitForClose();
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
}
|
||||
|
||||
// Verifies that StartupBrowserCreator::Start() shows the dialog and that
|
||||
// simulating validation fires the on_complete callback (which calls
|
||||
// Start_ChromiumImpl), opening a new browser window without crashing.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
StartOverrideShowsDialogAndProceeds) {
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
size_t browser_count_before = chrome::GetTotalBrowserCount();
|
||||
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(true);
|
||||
|
||||
StartupBrowserCreator browser_creator;
|
||||
StartupProfileInfo profile_info{browser()->profile(),
|
||||
StartupProfileMode::kBrowserWindow};
|
||||
bool result = browser_creator.Start(*base::CommandLine::ForCurrentProcess(),
|
||||
base::FilePath(), profile_info,
|
||||
{browser()->profile()});
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_TRUE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
// ValidateForTesting() is a no-op until the widget is ready (async profile
|
||||
// creation). Retry inside RunUntil so pending tasks are processed between
|
||||
// attempts.
|
||||
ASSERT_TRUE(base::test::RunUntil([] {
|
||||
BraveOriginStartupView::ValidateForTesting();
|
||||
return !BraveOriginStartupView::IsShowing();
|
||||
}));
|
||||
|
||||
// A new browser window should have been created by Start_ChromiumImpl.
|
||||
EXPECT_GT(chrome::GetTotalBrowserCount(), browser_count_before);
|
||||
}
|
||||
|
||||
// Verifies that StartupBrowserCreator::Start() proceeds normally (no dialog)
|
||||
// when ShouldShowDialog is false.
|
||||
IN_PROC_BROWSER_TEST_F(BraveOriginStartupViewBrowserTest,
|
||||
StartOverrideSkipsDialogWhenNotNeeded) {
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(false);
|
||||
|
||||
size_t browser_count_before = chrome::GetTotalBrowserCount();
|
||||
|
||||
StartupBrowserCreator browser_creator;
|
||||
StartupProfileInfo profile_info{browser()->profile(),
|
||||
StartupProfileMode::kBrowserWindow};
|
||||
bool result = browser_creator.Start(*base::CommandLine::ForCurrentProcess(),
|
||||
base::FilePath(), profile_info,
|
||||
{browser()->profile()});
|
||||
EXPECT_TRUE(result);
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
|
||||
// Start_ChromiumImpl should have run directly, creating a new browser.
|
||||
EXPECT_GT(chrome::GetTotalBrowserCount(), browser_count_before);
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/* 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/browser/ui/views/brave_origin/brave_origin_startup_view.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/values.h"
|
||||
#include "brave/components/brave_origin/pref_names.h"
|
||||
#include "brave/components/skus/browser/pref_names.h"
|
||||
#include "components/prefs/pref_registry_simple.h"
|
||||
#include "components/prefs/testing_pref_service.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
class BraveOriginStartupViewTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
local_state_.registry()->RegisterBooleanPref(
|
||||
brave_origin::kOriginPurchaseValidated, false);
|
||||
local_state_.registry()->RegisterDictionaryPref(skus::prefs::kSkusState);
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
}
|
||||
|
||||
// Builds a JSON string representing an SKU state entry with credentials.
|
||||
// If |has_items| is true, the credentials dict contains a non-empty "items"
|
||||
// dict; otherwise it is empty.
|
||||
std::string BuildSkuStateJson(bool has_items) {
|
||||
if (has_items) {
|
||||
return R"({
|
||||
"credentials": {
|
||||
"items": {
|
||||
"origin": "some-credential-value"
|
||||
}
|
||||
}
|
||||
})";
|
||||
}
|
||||
return R"({
|
||||
"credentials": {
|
||||
"items": {}
|
||||
}
|
||||
})";
|
||||
}
|
||||
|
||||
void SetSkuCredentials(const std::string& env_key, const std::string& json) {
|
||||
base::DictValue skus_state;
|
||||
skus_state.Set(env_key, json);
|
||||
local_state_.SetDict(skus::prefs::kSkusState, std::move(skus_state));
|
||||
}
|
||||
|
||||
protected:
|
||||
TestingPrefServiceSimple local_state_;
|
||||
};
|
||||
|
||||
// --- ShouldShowDialog tests ---
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldShowDialogWhenPurchaseNotValidatedAndNoCredentials) {
|
||||
// Default state: pref is false, no SKU credentials -> should show.
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldNotShowDialogWhenPurchaseValidatedAndHasCredentials) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
SetSkuCredentials("production", BuildSkuStateJson(/*has_items=*/true));
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldShowDialogWhenPurchaseValidatedButNoCredentials) {
|
||||
// Pref validated but SKU credentials missing -> should show.
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldShowDialogWhenNotValidatedButHasCredentials) {
|
||||
// Has credentials but pref not set -> should show.
|
||||
SetSkuCredentials("production", BuildSkuStateJson(/*has_items=*/true));
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldNotShowDialogWhenValidatedAndMultipleEnvCredentials) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
base::DictValue skus_state;
|
||||
skus_state.Set("staging", BuildSkuStateJson(/*has_items=*/false));
|
||||
skus_state.Set("production", BuildSkuStateJson(/*has_items=*/true));
|
||||
local_state_.SetDict(skus::prefs::kSkusState, std::move(skus_state));
|
||||
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldShowDialogWhenCredentialsHaveEmptyItems) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
SetSkuCredentials("production", BuildSkuStateJson(/*has_items=*/false));
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, ShouldShowDialogWhenSkuStateHasInvalidJson) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
SetSkuCredentials("production", "not-valid-json{{{");
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest,
|
||||
ShouldShowDialogWhenSkuStateHasNonStringValue) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
base::DictValue skus_state;
|
||||
skus_state.Set("production", 42); // Not a string.
|
||||
local_state_.SetDict(skus::prefs::kSkusState, std::move(skus_state));
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, ShouldShowDialogWhenCredentialsDictMissing) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
SetSkuCredentials("production", R"({"some_other_key": "value"})");
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, ShouldShowDialogWhenItemsDictMissing) {
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
SetSkuCredentials("production", R"({"credentials": {"not_items": "value"}})");
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
// --- SetShouldShowDialogForTesting tests ---
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, TestOverrideForceShow) {
|
||||
// Even with valid state, the override forces show.
|
||||
local_state_.SetBoolean(brave_origin::kOriginPurchaseValidated, true);
|
||||
SetSkuCredentials("production", BuildSkuStateJson(/*has_items=*/true));
|
||||
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(true);
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, TestOverrideForceHide) {
|
||||
// Even with invalid state, the override forces hide.
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(false);
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, TestOverrideResetsToNormal) {
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(false);
|
||||
EXPECT_FALSE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
|
||||
BraveOriginStartupView::SetShouldShowDialogForTesting(std::nullopt);
|
||||
// Back to normal: default state should show.
|
||||
EXPECT_TRUE(BraveOriginStartupView::ShouldShowDialog(&local_state_));
|
||||
}
|
||||
|
||||
// --- IsShowing tests (without needing a widget) ---
|
||||
|
||||
TEST_F(BraveOriginStartupViewTest, IsShowingReturnsFalseByDefault) {
|
||||
EXPECT_FALSE(BraveOriginStartupView::IsShowing());
|
||||
}
|
||||
@@ -9,11 +9,12 @@
|
||||
|
||||
#include "brave/browser/resources/brave_origin_startup/grit/brave_origin_startup_generated_map.h"
|
||||
#include "brave/browser/resources/brave_origin_startup/grit/brave_origin_startup_static_resources.h"
|
||||
#include "brave/browser/resources/brave_origin_startup/grit/brave_origin_startup_static_resources_map.h"
|
||||
#include "brave/browser/skus/skus_service_factory.h"
|
||||
#include "brave/browser/ui/webui/brave_origin_startup/brave_origin_startup_handler.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/profiles/profile.h"
|
||||
#include "components/grit/brave_components_strings.h"
|
||||
#include "components/grit/brave_components_webui_strings.h"
|
||||
#include "content/public/browser/browser_context.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/browser/web_ui.h"
|
||||
@@ -28,28 +29,9 @@ void CreateAndAddDataSource(content::WebUI* web_ui, Profile* profile) {
|
||||
webui::SetupWebUIDataSource(
|
||||
source, kBraveOriginStartupGenerated,
|
||||
IDR_BRAVE_ORIGIN_STARTUP_STATIC_BRAVE_ORIGIN_STARTUP_HTML);
|
||||
source->AddResourcePaths(kBraveOriginStartupStaticResources);
|
||||
|
||||
static constexpr webui::LocalizedString kStrings[] = {
|
||||
{"braveOriginStartupTitle", IDS_BRAVE_ORIGIN_STARTUP_TITLE},
|
||||
{"braveOriginStartupDescription", IDS_BRAVE_ORIGIN_STARTUP_DESCRIPTION},
|
||||
{"braveOriginStartupDescription2", IDS_BRAVE_ORIGIN_STARTUP_DESCRIPTION2},
|
||||
{"braveOriginStartupRestoreButton",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_RESTORE_BUTTON},
|
||||
{"braveOriginStartupBuyButton", IDS_BRAVE_ORIGIN_STARTUP_BUY_BUTTON},
|
||||
{"braveOriginStartupRestoreTitle",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_RESTORE_TITLE},
|
||||
{"braveOriginStartupRestoreDescription",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_RESTORE_DESCRIPTION},
|
||||
{"braveOriginStartupPurchaseIdLabel",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_PURCHASE_ID_LABEL},
|
||||
{"braveOriginStartupPurchaseIdPlaceholder",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_PURCHASE_ID_PLACEHOLDER},
|
||||
{"braveOriginStartupVerifyButton",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_VERIFY_BUTTON},
|
||||
{"braveOriginStartupVerifyingMessage",
|
||||
IDS_BRAVE_ORIGIN_STARTUP_VERIFYING_MESSAGE},
|
||||
};
|
||||
source->AddLocalizedStrings(kStrings);
|
||||
source->AddLocalizedStrings(webui::kBraveOriginStartupStrings);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -286,6 +286,7 @@ void BraveSettingsUI::AddResources(content::WebUIDataSource* html_source,
|
||||
brave_origin::IsBraveOriginEnabled());
|
||||
html_source->AddBoolean("isBraveOriginBrandedBuild",
|
||||
BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED));
|
||||
// STAGING for unofficial builds; official builds always resolve to prod.
|
||||
html_source->AddString(
|
||||
"braveOriginBuyUrl",
|
||||
base::StrCat(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include_rules = [
|
||||
"+brave/browser/ui/views/brave_origin",
|
||||
"+brave/components/brave_origin/buildflags",
|
||||
"+brave/components/tor/buildflags",
|
||||
]
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/logging.h"
|
||||
#include "brave/components/brave_origin/buildflags/buildflags.h"
|
||||
#include "brave/components/constants/brave_switches.h"
|
||||
#include "brave/components/tor/buildflags/buildflags.h"
|
||||
#include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
|
||||
@@ -15,6 +16,15 @@
|
||||
#include "brave/browser/tor/tor_profile_manager.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
#include "brave/browser/ui/views/brave_origin/brave_origin_startup_view.h"
|
||||
#include "chrome/browser/browser_process.h"
|
||||
#include "chrome/browser/lifetime/application_lifetime.h"
|
||||
#include "chrome/browser/platform_util.h"
|
||||
#include "chrome/browser/profiles/profile_manager.h"
|
||||
#include "components/prefs/pref_service.h"
|
||||
#endif
|
||||
|
||||
#ifdef LaunchModeRecorder
|
||||
static_assert(false,
|
||||
"Replace the use of OldLaunchModeRecorder with "
|
||||
@@ -85,5 +95,95 @@ void BraveStartupBrowserCreatorImpl::Launch(
|
||||
}
|
||||
|
||||
#define StartupBrowserCreatorImpl BraveStartupBrowserCreatorImpl
|
||||
#define Start(...) Start_ChromiumImpl(__VA_ARGS__)
|
||||
#define ProcessCommandLineAlreadyRunning(...) \
|
||||
ProcessCommandLineAlreadyRunning_ChromiumImpl(__VA_ARGS__)
|
||||
#include <chrome/browser/ui/startup/startup_browser_creator.cc>
|
||||
#undef ProcessCommandLineAlreadyRunning
|
||||
#undef Start
|
||||
#undef StartupBrowserCreatorImpl
|
||||
|
||||
#if BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
namespace {
|
||||
|
||||
// Concrete delegate that wires up the startup dialog to real browser services.
|
||||
class StartupDialogDelegate : public BraveOriginStartupView::Delegate {
|
||||
public:
|
||||
void OpenExternal(const GURL& url) override {
|
||||
platform_util::OpenExternal(url);
|
||||
}
|
||||
|
||||
void AttemptExit() override { chrome::AttemptExit(); }
|
||||
|
||||
void CreateSystemProfile(
|
||||
BraveOriginStartupView::ProfileCallback callback) override {
|
||||
g_browser_process->profile_manager()->CreateProfileAsync(
|
||||
ProfileManager::GetSystemProfilePath(), std::move(callback));
|
||||
}
|
||||
|
||||
void CreateDefaultProfile(
|
||||
BraveOriginStartupView::ProfileCallback callback) override {
|
||||
g_browser_process->profile_manager()->CreateProfileAsync(
|
||||
g_browser_process->profile_manager()->GetLastUsedProfileDir(),
|
||||
std::move(callback));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
#endif // BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
|
||||
// For Brave Origin branded builds, intercept Start() to show a purchase
|
||||
// validation dialog before any browser window or profile picker opens. Start()
|
||||
// is only called externally from chrome_browser_main.cc, so the #define above
|
||||
// only renames the definition (not external callers). When the dialog closes
|
||||
// with a successful validation, the callback invokes Start_ChromiumImpl() to
|
||||
// continue the normal startup flow.
|
||||
bool StartupBrowserCreator::Start(const base::CommandLine& cmd_line,
|
||||
const base::FilePath& cur_dir,
|
||||
StartupProfileInfo profile_info,
|
||||
const Profiles& last_opened_profiles) {
|
||||
#if BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
if (BraveOriginStartupView::ShouldShowDialog(
|
||||
g_browser_process->local_state())) {
|
||||
// Capture first_run_tabs_ by value because `this` (the
|
||||
// StartupBrowserCreator) is destroyed by chrome_browser_main.cc
|
||||
// (browser_creator_.reset()) right after Start() returns.
|
||||
BraveOriginStartupView::Show(
|
||||
base::BindOnce(
|
||||
[](std::vector<GURL> first_run_tabs,
|
||||
const base::CommandLine& cmd_line, const base::FilePath& cur_dir,
|
||||
StartupProfileInfo profile_info,
|
||||
const Profiles& last_opened_profiles) {
|
||||
StartupBrowserCreator browser_creator;
|
||||
browser_creator.AddFirstRunTabs(first_run_tabs);
|
||||
browser_creator.Start_ChromiumImpl(cmd_line, cur_dir,
|
||||
std::move(profile_info),
|
||||
last_opened_profiles);
|
||||
},
|
||||
std::move(first_run_tabs_), cmd_line, cur_dir,
|
||||
std::move(profile_info), last_opened_profiles),
|
||||
std::make_unique<StartupDialogDelegate>());
|
||||
return true;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
return Start_ChromiumImpl(cmd_line, cur_dir, std::move(profile_info),
|
||||
last_opened_profiles);
|
||||
}
|
||||
|
||||
// For Brave Origin branded builds, block second-launch attempts while the
|
||||
// startup dialog is showing. Without this, a second launch goes through
|
||||
// ProcessCommandLineAlreadyRunning (not Start), bypassing the dialog guard
|
||||
// and opening a browser window directly.
|
||||
// static
|
||||
void StartupBrowserCreator::ProcessCommandLineAlreadyRunning(
|
||||
const base::CommandLine& command_line,
|
||||
const base::FilePath& cur_dir,
|
||||
const StartupProfilePathInfo& profile_path_info) {
|
||||
#if BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
if (BraveOriginStartupView::IsShowing()) {
|
||||
return;
|
||||
}
|
||||
#endif // BUILDFLAG(IS_BRAVE_ORIGIN_BRANDED)
|
||||
ProcessCommandLineAlreadyRunning_ChromiumImpl(command_line, cur_dir,
|
||||
profile_path_info);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* 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_CHROMIUM_SRC_CHROME_BROWSER_UI_STARTUP_STARTUP_BROWSER_CREATOR_H_
|
||||
#define BRAVE_CHROMIUM_SRC_CHROME_BROWSER_UI_STARTUP_STARTUP_BROWSER_CREATOR_H_
|
||||
|
||||
// Adds _ChromiumImpl method declarations alongside the originals, so the Brave
|
||||
// Origin startup dialog can intercept the startup flow in the .cc override.
|
||||
// Start() is only called from chrome_browser_main.cc (a different translation
|
||||
// unit) so the #define in the .cc does not affect its external callers.
|
||||
#define Start(...) \
|
||||
Start_ChromiumImpl(__VA_ARGS__); \
|
||||
bool Start(__VA_ARGS__)
|
||||
|
||||
#define ProcessCommandLineAlreadyRunning(...) \
|
||||
ProcessCommandLineAlreadyRunning_ChromiumImpl(__VA_ARGS__); \
|
||||
static void ProcessCommandLineAlreadyRunning(__VA_ARGS__)
|
||||
|
||||
#include <chrome/browser/ui/startup/startup_browser_creator.h> // IWYU pragma: export
|
||||
#undef ProcessCommandLineAlreadyRunning
|
||||
#undef Start
|
||||
|
||||
#endif // BRAVE_CHROMIUM_SRC_CHROME_BROWSER_UI_STARTUP_STARTUP_BROWSER_CREATOR_H_
|
||||
+8
-1
@@ -191,7 +191,10 @@ test("brave_unit_tests") {
|
||||
]
|
||||
|
||||
if (is_brave_origin_branded) {
|
||||
deps += [ "//brave/browser/ui/webui/brave_origin_startup:unit_tests" ]
|
||||
deps += [
|
||||
"//brave/browser/ui/views/brave_origin:unit_tests",
|
||||
"//brave/browser/ui/webui/brave_origin_startup:unit_tests",
|
||||
]
|
||||
}
|
||||
|
||||
if (enable_brave_news) {
|
||||
@@ -1183,6 +1186,10 @@ test("brave_browser_tests") {
|
||||
sources += [ "//brave/browser/ui/views/crash_report_permission_ask_dialog_browsertest.cc" ]
|
||||
}
|
||||
|
||||
if (is_brave_origin_branded) {
|
||||
deps += [ "//brave/browser/ui/views/brave_origin:browser_tests" ]
|
||||
}
|
||||
|
||||
if (enable_brave_wayback_machine) {
|
||||
sources += [ "//brave/components/brave_wayback_machine/brave_wayback_machine_browsertest.cc" ]
|
||||
deps += [ "//brave/components/brave_wayback_machine" ]
|
||||
|
||||
Reference in New Issue
Block a user