diff --git a/packages/browseros/build/build.py b/packages/browseros/build/build.py index cf9cef56..1c8239e8 100755 --- a/packages/browseros/build/build.py +++ b/packages/browseros/build/build.py @@ -307,12 +307,16 @@ def build_main( ctx, interactive=patch_interactive, commit_each=patch_commit ) - # Copy resources + if slack_notifications: + notify_build_step("Completed applying patches") + + # Copy resources for each architecture (YAML filters by arch) + if apply_patches_flag: copy_resources(ctx, commit_each=patch_commit) if slack_notifications: notify_build_step( - "Completed applying patches and copying resources" + f"Completed copying resources for {arch_name}" ) # Build for this architecture diff --git a/packages/browseros/build/config/NXTSCAPE_VERSION b/packages/browseros/build/config/NXTSCAPE_VERSION index b5489e5e..8e14edce 100644 --- a/packages/browseros/build/config/NXTSCAPE_VERSION +++ b/packages/browseros/build/config/NXTSCAPE_VERSION @@ -1 +1 @@ -69 +78 diff --git a/packages/browseros/build/config/package.linux.yaml b/packages/browseros/build/config/package.linux.yaml new file mode 100644 index 00000000..5bb2e7c6 --- /dev/null +++ b/packages/browseros/build/config/package.linux.yaml @@ -0,0 +1,35 @@ +# BrowserOS Linux Release Build Configuration +build: + type: release + architecture: x64 # Linux x64 only + # architectures: [x64] # Single architecture for Linux + universal: false # Linux doesn't support universal binaries + +gn_flags: + file: build/config/gn/flags.linux.release.gn + +steps: + clean: false + git_setup: false + apply_patches: false + build: false + sign: false # Linux doesn't require code signing + package: true + +paths: + root_dir: . + # chromium_src: ../chromium-src + +# Environment-specific settings +env: + PYTHONPATH: scripts + +# Linux-specific settings +linux: + appimage: + compression: gzip # Compression type for AppImage + architecture: x86_64 # AppImage architecture designation + +# Notification settings +notifications: + slack: true # Enable Slack notifications for release builds diff --git a/packages/browseros/build/modules/package_windows.py b/packages/browseros/build/modules/package_windows.py index 2fab6383..86a7379e 100644 --- a/packages/browseros/build/modules/package_windows.py +++ b/packages/browseros/build/modules/package_windows.py @@ -31,7 +31,7 @@ def get_browseros_server_binary_paths(build_output_dir: Path) -> List[Path]: """Return absolute paths to BrowserOS Server binaries for signing.""" - server_dir = build_output_dir / "BrowserOSServer" / "default" + server_dir = build_output_dir / "BrowserOSServer" / "default" / "resources" / "bin" return [server_dir / binary for binary in BROWSEROS_SERVER_BINARIES] @@ -69,12 +69,18 @@ def build_mini_installer(ctx: BuildContext) -> bool: # Get paths build_output_dir = join_paths(ctx.chromium_src, ctx.out_dir) mini_installer_path = build_output_dir / "mini_installer.exe" + setup_exe_path = build_output_dir / "setup.exe" - if mini_installer_path.exists(): - log_info("mini_installer.exe already exists") - return True + if mini_installer_path.exists() and setup_exe_path.exists(): + log_info( + "mini_installer.exe and setup.exe already exist; rebuilding to ensure freshness" + ) + elif setup_exe_path.exists() and not mini_installer_path.exists(): + log_info("setup.exe exists but mini_installer.exe missing") + elif mini_installer_path.exists() and not setup_exe_path.exists(): + log_info("mini_installer.exe exists but setup.exe missing") - log_info("Building mini_installer target...") + log_info("Building setup and mini_installer targets...") # Build mini_installer using autoninja try: @@ -86,6 +92,7 @@ def build_mini_installer(ctx: BuildContext) -> bool: autoninja_cmd, "-C", ctx.out_dir, # Use relative path like in compile.py + "setup", "mini_installer", ] @@ -101,15 +108,24 @@ def build_mini_installer(ctx: BuildContext) -> bool: os.chdir(old_cwd) # Verify the file was created - if mini_installer_path.exists(): - log_success("mini_installer built successfully") + missing_artifacts = [] + if not setup_exe_path.exists(): + missing_artifacts.append("setup.exe") + if not mini_installer_path.exists(): + missing_artifacts.append("mini_installer.exe") + + if not missing_artifacts: + log_success("mini_installer and setup built successfully") return True - else: - log_error("mini_installer build completed but file not found") - return False + + log_error( + "Build completed but missing artifacts: " + + ", ".join(missing_artifacts) + ) + return False except Exception as e: - log_error(f"Failed to build mini_installer: {e}") + log_error(f"Failed to build setup/mini_installer: {e}") return False diff --git a/packages/browseros/chromium_patches/base/threading/thread_restrictions.h b/packages/browseros/chromium_patches/base/threading/thread_restrictions.h new file mode 100644 index 00000000..1d8683f1 --- /dev/null +++ b/packages/browseros/chromium_patches/base/threading/thread_restrictions.h @@ -0,0 +1,30 @@ +diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h +index 59d6e6e4d899f..9f2737ff17c6f 100644 +--- a/base/threading/thread_restrictions.h ++++ b/base/threading/thread_restrictions.h +@@ -200,6 +200,9 @@ namespace scheduler { + class NonMainThreadImpl; + } + } // namespace blink ++namespace browseros { ++class BrowserOSServerManager; ++} // namespace browseros + namespace cc { + class CategorizedWorkerPoolJob; + class CategorizedWorkerPool; +@@ -595,6 +598,7 @@ class BASE_EXPORT ScopedAllowBlocking { + friend class base::subtle::PlatformSharedMemoryRegion; + friend class base::win::ScopedAllowBlockingForUserAccountControl; + friend class blink::DiskDataAllocator; ++ friend class browseros::BrowserOSServerManager; + friend class chromecast::CrashUtil; + friend class content::BrowserProcessIOThread; + friend class content::DWriteFontProxyImpl; +@@ -743,6 +747,7 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitives { + friend class base::SimpleThread; + friend class base::internal::GetAppOutputScopedAllowBaseSyncPrimitives; + friend class blink::SourceStream; ++ friend class browseros::BrowserOSServerManager; + friend class blink::VideoTrackRecorderImplContextProvider; + friend class blink::WorkerThread; + friend class blink::scheduler::NonMainThreadImpl; diff --git a/packages/browseros/chromium_patches/chrome/BUILD.gn b/packages/browseros/chromium_patches/chrome/BUILD.gn index 70aa642b..d462eacd 100644 --- a/packages/browseros/chromium_patches/chrome/BUILD.gn +++ b/packages/browseros/chromium_patches/chrome/BUILD.gn @@ -1,5 +1,5 @@ diff --git a/chrome/BUILD.gn b/chrome/BUILD.gn -index 97f843f8133c4..b7af2f2d94579 100644 +index 97f843f8133c4..0acbe29f11806 100644 --- a/chrome/BUILD.gn +++ b/chrome/BUILD.gn @@ -18,6 +18,7 @@ import("//build/config/win/manifest.gni") @@ -37,11 +37,3 @@ index 97f843f8133c4..b7af2f2d94579 100644 configs += [ ":chrome_dll_symbol_order" ] if (!is_component_build && !using_sanitizer) { configs += [ ":chrome_dll_symbol_exports" ] -@@ -1493,6 +1500,7 @@ copy("visual_elements_resources") { - "//chrome/app/theme/$branding_path_component/win/tiles/Logo.png", - "//chrome/app/theme/$branding_path_component/win/tiles/SmallLogo.png", - "app/visual_elements_resources/chrome.VisualElementsManifest.xml", -+ "app/visual_elements_resources/browseros.VisualElementsManifest.xml", - ] - - if (is_chrome_branded) { diff --git a/packages/browseros/chromium_patches/chrome/app/visual_elements_resources/browseros.VisualElementsManifest.xml b/packages/browseros/chromium_patches/chrome/app/visual_elements_resources/browseros.VisualElementsManifest.xml deleted file mode 100644 index ae89c096..00000000 --- a/packages/browseros/chromium_patches/chrome/app/visual_elements_resources/browseros.VisualElementsManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/chrome/app/visual_elements_resources/browseros.VisualElementsManifest.xml b/chrome/app/visual_elements_resources/browseros.VisualElementsManifest.xml -new file mode 100644 -index 0000000000000..a7bb10b4056e2 ---- /dev/null -+++ b/chrome/app/visual_elements_resources/browseros.VisualElementsManifest.xml -@@ -0,0 +1,10 @@ -+ -+ -+ -+ diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/.gitignore b/packages/browseros/chromium_patches/chrome/browser/browseros_server/.gitignore new file mode 100644 index 00000000..0fa45fd4 --- /dev/null +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/.gitignore @@ -0,0 +1,7 @@ +diff --git a/chrome/browser/browseros_server/.gitignore b/chrome/browser/browseros_server/.gitignore +new file mode 100644 +index 0000000000000..cb76b31565a77 +--- /dev/null ++++ b/chrome/browser/browseros_server/.gitignore +@@ -0,0 +1 @@ ++resources/ diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/BUILD.gn b/packages/browseros/chromium_patches/chrome/browser/browseros_server/BUILD.gn index 7efe2ae9..5fa302ae 100644 --- a/packages/browseros/chromium_patches/chrome/browser/browseros_server/BUILD.gn +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/BUILD.gn @@ -1,15 +1,28 @@ diff --git a/chrome/browser/browseros_server/BUILD.gn b/chrome/browser/browseros_server/BUILD.gn new file mode 100644 -index 0000000000000..7165c4459db08 +index 0000000000000..ddbdf0b78c0a3 --- /dev/null +++ b/chrome/browser/browseros_server/BUILD.gn -@@ -0,0 +1,74 @@ +@@ -0,0 +1,66 @@ +# Copyright 2024 The Chromium Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/chrome_build.gni") + ++# Validate that required resources exist at build time ++# GN will fail at generation time if resources/bin/browseros_server is missing ++_browseros_binary_name = "browseros_server" ++if (is_win) { ++ _browseros_binary_name += ".exe" ++} ++ ++action("validate_browseros_resources") { ++ script = "validate_resources.py" ++ inputs = [ "resources/bin/${_browseros_binary_name}" ] ++ outputs = [ "$target_gen_dir/browseros_resources_validated" ] ++} ++ +source_set("browseros_server") { + sources = [ + "browseros_server_manager.cc", @@ -28,53 +41,32 @@ index 0000000000000..7165c4459db08 + ] +} + -+# Determine OS name for binary selection -+if (is_mac) { -+ _browseros_os = "darwin" -+} else if (is_win) { -+ _browseros_os = "windows" -+} else if (is_linux) { -+ _browseros_os = "linux" -+} -+ -+# Construct source binary filename based on target OS and architecture -+_browseros_binary_name = "browseros-server-${_browseros_os}-${target_cpu}" -+if (is_win) { -+ _browseros_binary_name += ".exe" -+} -+ -+# Source binary path (architecture-specific) -+_browseros_source_binary = "binaries/${_browseros_binary_name}" -+ -+# Output filename (standardized) -+_browseros_output_name = "browseros_server" -+if (is_win) { -+ _browseros_output_name += ".exe" -+} -+ +if (is_mac) { + import("//build/config/apple/symbols.gni") + import("//build/config/mac/mac_sdk.gni") + -+ # Bundle data for macOS - packages to Resources/BrowserOSServer/default/ -+ bundle_data("browseros_server_bundle_data") { -+ sources = [ _browseros_source_binary ] -+ outputs = -+ [ "{{bundle_resources_dir}}/BrowserOSServer/default/${_browseros_output_name}" ] ++ # Bundle data for macOS - recursively packages resources/ to Resources/BrowserOSServer/default/ ++ bundle_data("browseros_resources_bundle") { ++ sources = [ "resources" ] ++ outputs = [ "{{bundle_resources_dir}}/BrowserOSServer/default/{{source_file_part}}" ] ++ # TODO: Re-enable validation when resources/bin/browseros_server is available ++ # deps = [ ":validate_browseros_resources" ] + } +} else { -+ # Copy for Windows/Linux - packages to /BrowserOSServer/default/ -+ copy("browseros_server_binary") { -+ sources = [ _browseros_source_binary ] -+ outputs = [ "$root_out_dir/BrowserOSServer/default/${_browseros_output_name}" ] ++ # Copy for Windows/Linux - recursively packages resources/ to /BrowserOSServer/default/ ++ copy("browseros_resources_copy") { ++ sources = [ "resources" ] ++ outputs = [ "$root_out_dir/BrowserOSServer/default/{{source_file_part}}" ] ++ # TODO: Re-enable validation when resources/bin/browseros_server is available ++ # deps = [ ":validate_browseros_resources" ] + } +} + +# Group for all BrowserOS server resources +group("browseros_server_resources") { + if (is_mac) { -+ deps = [ ":browseros_server_bundle_data" ] ++ deps = [ ":browseros_resources_bundle" ] + } else { -+ deps = [ ":browseros_server_binary" ] ++ deps = [ ":browseros_resources_copy" ] + } +} diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.cc b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.cc index f80f07be..041c8fc0 100644 --- a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.cc +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.cc @@ -1,9 +1,9 @@ diff --git a/chrome/browser/browseros_server/browseros_server_manager.cc b/chrome/browser/browseros_server/browseros_server_manager.cc new file mode 100644 -index 0000000000000..e6bd851f42bfa +index 0000000000000..4d34f3efb2645 --- /dev/null +++ b/chrome/browser/browseros_server/browseros_server_manager.cc -@@ -0,0 +1,863 @@ +@@ -0,0 +1,1070 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -25,6 +25,11 @@ index 0000000000000..e6bd851f42bfa +#include "base/threading/thread_restrictions.h" +#include "build/build_config.h" +#include "chrome/browser/browser_process.h" ++ ++#if BUILDFLAG(IS_POSIX) ++#include ++#endif ++ +#include "chrome/browser/browseros_server/browseros_server_prefs.h" +#include "chrome/browser/net/system_network_context_manager.h" +#include "chrome/browser/profiles/profile.h" @@ -93,6 +98,8 @@ index 0000000000000..e6bd851f42bfa +// This function performs blocking I/O operations (PathExists, LaunchProcess). +base::Process LaunchProcessOnBackgroundThread( + const base::FilePath& exe_path, ++ const base::FilePath& resources_dir, ++ const base::FilePath& execution_dir, + uint16_t cdp_port, + uint16_t mcp_port, + uint16_t agent_port, @@ -104,12 +111,26 @@ index 0000000000000..e6bd851f42bfa + return base::Process(); + } + ++ if (execution_dir.empty()) { ++ LOG(ERROR) << "browseros: Execution directory path is empty"; ++ return base::Process(); ++ } ++ ++ // Ensure execution directory exists (blocking I/O) ++ if (!base::CreateDirectory(execution_dir)) { ++ LOG(ERROR) << "browseros: Failed to create execution directory at: " ++ << execution_dir; ++ return base::Process(); ++ } ++ + // Build command line + base::CommandLine cmd(exe_path); + cmd.AppendSwitchASCII("cdp-port", base::NumberToString(cdp_port)); + cmd.AppendSwitchASCII("http-mcp-port", base::NumberToString(mcp_port)); + cmd.AppendSwitchASCII("agent-port", base::NumberToString(agent_port)); + cmd.AppendSwitchASCII("extension-port", base::NumberToString(extension_port)); ++ cmd.AppendSwitchPath("resources-dir", resources_dir); ++ cmd.AppendSwitchPath("execution-dir", execution_dir); + + // Set up launch options + base::LaunchOptions options; @@ -158,6 +179,8 @@ index 0000000000000..e6bd851f42bfa + +} // namespace + ++namespace browseros { ++ +// static +BrowserOSServerManager* BrowserOSServerManager::GetInstance() { + static base::NoDestructor instance; @@ -170,88 +193,76 @@ index 0000000000000..e6bd851f42bfa + Shutdown(); +} + -+void BrowserOSServerManager::Start() { -+ if (is_running_) { -+ LOG(INFO) << "browseros: BrowserOS server already running"; -+ return; -+ } ++bool BrowserOSServerManager::AcquireLock() { ++ // Allow blocking for lock file operations (short-duration I/O) ++ base::ScopedAllowBlocking allow_blocking; + -+ // Check if server is disabled via command line -+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); -+ if (command_line->HasSwitch("disable-browseros-server")) { -+ LOG(INFO) << "browseros: BrowserOS server disabled via command line"; -+ return; ++ base::FilePath exec_dir = GetBrowserOSExecutionDir(); ++ if (exec_dir.empty()) { ++ LOG(ERROR) << "browseros: Failed to resolve execution directory for lock"; ++ return false; + } + -+ LOG(INFO) << "browseros: Starting BrowserOS server"; -+ -+ // Step 1: Start CDP server -+ StartCDPServer(); -+ -+ // Step 2: Launch BrowserOS process -+ LaunchBrowserOSProcess(); ++ base::FilePath lock_path = exec_dir.Append(FILE_PATH_LITERAL("server.lock")); + -+ // Step 3: Start health checking every 60 seconds -+ health_check_timer_.Start(FROM_HERE, base::Seconds(60), this, -+ &BrowserOSServerManager::CheckServerHealth); -+} ++ lock_file_ = base::File(lock_path, ++ base::File::FLAG_OPEN_ALWAYS | ++ base::File::FLAG_READ | ++ base::File::FLAG_WRITE); + -+void BrowserOSServerManager::Stop() { -+ if (!is_running_) { -+ return; ++ if (!lock_file_.IsValid()) { ++ LOG(ERROR) << "browseros: Failed to open lock file: " << lock_path; ++ return false; + } + -+ LOG(INFO) << "browseros: Stopping BrowserOS server"; -+ health_check_timer_.Stop(); -+ process_check_timer_.Stop(); -+ -+ TerminateBrowserOSProcess(); -+ StopCDPServer(); -+} -+ -+bool BrowserOSServerManager::IsRunning() const { -+ return is_running_ && process_.IsValid(); -+} ++ base::File::Error lock_error = ++ lock_file_.Lock(base::File::LockMode::kExclusive); ++ if (lock_error != base::File::FILE_OK) { ++ LOG(INFO) << "browseros: Server already running in another Chrome process " ++ << "(lock file: " << lock_path << ")"; ++ lock_file_.Close(); ++ return false; ++ } + -+void BrowserOSServerManager::Shutdown() { -+ Stop(); ++ LOG(INFO) << "browseros: Acquired exclusive lock on " << lock_path; ++ return true; +} + -+void BrowserOSServerManager::StartCDPServer() { ++void BrowserOSServerManager::InitializePortsAndPrefs() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); -+ + PrefService* prefs = g_browser_process->local_state(); ++ ++ // STEP 1: Read from prefs or use defaults + if (!prefs) { -+ LOG(ERROR) << "browseros: Failed to get local state prefs"; -+ // Find available ports from standard defaults -+ cdp_port_ = FindAvailablePort(browseros_server::kDefaultCDPPort); -+ mcp_port_ = FindAvailablePort(browseros_server::kDefaultMCPPort); -+ agent_port_ = FindAvailablePort(browseros_server::kDefaultAgentPort); -+ extension_port_ = FindAvailablePort(browseros_server::kDefaultExtensionPort); ++ cdp_port_ = browseros_server::kDefaultCDPPort; ++ mcp_port_ = browseros_server::kDefaultMCPPort; ++ agent_port_ = browseros_server::kDefaultAgentPort; ++ extension_port_ = browseros_server::kDefaultExtensionPort; ++ mcp_enabled_ = true; + } else { -+ // Read CDP port from prefs and find available port -+ int saved_cdp_port = prefs->GetInteger(browseros_server::kCDPServerPort); -+ int cdp_starting_port = (saved_cdp_port > 0) ? saved_cdp_port : browseros_server::kDefaultCDPPort; -+ cdp_port_ = FindAvailablePort(cdp_starting_port); -+ -+ // Read MCP settings -+ int saved_mcp_port = prefs->GetInteger(browseros_server::kMCPServerPort); -+ int mcp_starting_port = (saved_mcp_port > 0) ? saved_mcp_port : browseros_server::kDefaultMCPPort; -+ mcp_port_ = FindAvailablePort(mcp_starting_port); -+ -+ // Read Agent port from prefs and find available port -+ int saved_agent_port = prefs->GetInteger(browseros_server::kAgentServerPort); -+ int agent_starting_port = (saved_agent_port > 0) ? saved_agent_port : browseros_server::kDefaultAgentPort; -+ agent_port_ = FindAvailablePort(agent_starting_port); -+ -+ // Read Extension port from prefs and find available port -+ int saved_extension_port = prefs->GetInteger(browseros_server::kExtensionServerPort); -+ int extension_starting_port = (saved_extension_port > 0) ? saved_extension_port : browseros_server::kDefaultExtensionPort; -+ extension_port_ = FindAvailablePort(extension_starting_port); ++ cdp_port_ = prefs->GetInteger(browseros_server::kCDPServerPort); ++ if (cdp_port_ <= 0) { ++ cdp_port_ = browseros_server::kDefaultCDPPort; ++ } ++ ++ mcp_port_ = prefs->GetInteger(browseros_server::kMCPServerPort); ++ if (mcp_port_ <= 0) { ++ mcp_port_ = browseros_server::kDefaultMCPPort; ++ } ++ ++ agent_port_ = prefs->GetInteger(browseros_server::kAgentServerPort); ++ if (agent_port_ <= 0) { ++ agent_port_ = browseros_server::kDefaultAgentPort; ++ } ++ ++ extension_port_ = prefs->GetInteger(browseros_server::kExtensionServerPort); ++ if (extension_port_ <= 0) { ++ extension_port_ = browseros_server::kDefaultExtensionPort; ++ } + + mcp_enabled_ = prefs->GetBoolean(browseros_server::kMCPServerEnabled); + -+ // Set up preference change observer for MCP enabled flag ++ // Set up pref change observers + if (!pref_change_registrar_) { + pref_change_registrar_ = std::make_unique(); + pref_change_registrar_->Init(prefs); @@ -259,21 +270,29 @@ index 0000000000000..e6bd851f42bfa + browseros_server::kMCPServerEnabled, + base::BindRepeating(&BrowserOSServerManager::OnMCPEnabledChanged, + base::Unretained(this))); ++ pref_change_registrar_->Add( ++ browseros_server::kRestartServerRequested, ++ base::BindRepeating(&BrowserOSServerManager::OnRestartServerRequestedChanged, ++ base::Unretained(this))); + } + } ++ cdp_port_ = FindAvailablePort(cdp_port_); ++ mcp_port_ = FindAvailablePort(mcp_port_); ++ agent_port_ = FindAvailablePort(agent_port_); ++ extension_port_ = FindAvailablePort(extension_port_); ++ ++ // STEP 3: Apply command-line overrides (these take highest priority) ++ int cdp_override = GetPortOverrideFromCommandLine( ++ command_line, "browseros-cdp-port", "CDP port"); ++ if (cdp_override > 0) { ++ cdp_port_ = cdp_override; ++ } + -+ LOG(INFO) << "browseros: Ports allocated - CDP: " << cdp_port_ -+ << ", MCP: " << mcp_port_ << ", Agent: " << agent_port_ -+ << ", Extension: " << extension_port_; -+ -+ // Check for command-line port overrides + int mcp_override = GetPortOverrideFromCommandLine( + command_line, "browseros-mcp-port", "MCP port"); + if (mcp_override > 0) { + mcp_port_ = mcp_override; -+ // Implicitly enable MCP when port is specified -+ mcp_enabled_ = true; -+ LOG(INFO) << "browseros: MCP server implicitly enabled via command line"; ++ mcp_enabled_ = true; // Implicit enable when port specified + } + + int agent_override = GetPortOverrideFromCommandLine( @@ -288,18 +307,99 @@ index 0000000000000..e6bd851f42bfa + extension_port_ = extension_override; + } + ++ LOG(INFO) << "browseros: Final ports - CDP: " << cdp_port_ ++ << ", MCP: " << mcp_port_ << ", Agent: " << agent_port_ ++ << ", Extension: " << extension_port_; ++} ++ ++void BrowserOSServerManager::SavePortsToPrefs() { ++ PrefService* prefs = g_browser_process->local_state(); ++ if (!prefs) { ++ return; ++ } ++ ++ prefs->SetInteger(browseros_server::kCDPServerPort, cdp_port_); ++ prefs->SetInteger(browseros_server::kMCPServerPort, mcp_port_); ++ prefs->SetInteger(browseros_server::kAgentServerPort, agent_port_); ++ prefs->SetInteger(browseros_server::kExtensionServerPort, extension_port_); ++ prefs->SetBoolean(browseros_server::kMCPServerEnabled, mcp_enabled_); ++ ++ LOG(INFO) << "browseros: Saved finalized ports to prefs"; ++} ++ ++void BrowserOSServerManager::Start() { ++ if (is_running_) { ++ LOG(INFO) << "browseros: BrowserOS server already running"; ++ return; ++ } ++ ++ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); ++ if (command_line->HasSwitch("disable-browseros-server")) { ++ LOG(INFO) << "browseros: BrowserOS server disabled via command line"; ++ return; ++ } ++ ++ // Try to acquire system-wide lock ++ if (!AcquireLock()) { ++ return; // Another Chrome process already owns the server ++ } ++ ++ LOG(INFO) << "browseros: Starting BrowserOS server"; ++ ++ // Initialize and finalize ports ++ InitializePortsAndPrefs(); ++ SavePortsToPrefs(); ++ ++ // Start servers and process ++ StartCDPServer(); ++ LaunchBrowserOSProcess(); ++ ++ health_check_timer_.Start(FROM_HERE, base::Seconds(60), this, ++ &BrowserOSServerManager::CheckServerHealth); ++} ++ ++void BrowserOSServerManager::Stop() { ++ if (!is_running_) { ++ return; ++ } ++ ++ LOG(INFO) << "browseros: Stopping BrowserOS server"; ++ health_check_timer_.Stop(); ++ process_check_timer_.Stop(); ++ ++ TerminateBrowserOSProcess(); ++ StopCDPServer(); ++ ++ // Release lock ++ if (lock_file_.IsValid()) { ++ lock_file_.Unlock(); ++ lock_file_.Close(); ++ LOG(INFO) << "browseros: Released lock file"; ++ } ++} ++ ++bool BrowserOSServerManager::IsRunning() const { ++ return is_running_ && process_.IsValid(); ++} ++ ++void BrowserOSServerManager::Shutdown() { ++ Stop(); ++} ++ ++void BrowserOSServerManager::StartCDPServer() { + LOG(INFO) << "browseros: Starting CDP server on port " << cdp_port_; + -+ // Start Chromium's built-in DevTools remote debugging server + content::DevToolsAgentHost::StartRemoteDebuggingServer( + std::make_unique(cdp_port_), -+ base::FilePath(), // No output dir needed -+ base::FilePath()); // No debug frontend dir ++ base::FilePath(), ++ base::FilePath()); + + LOG(INFO) << "browseros: CDP WebSocket server started at ws://127.0.0.1:" + << cdp_port_; + LOG(INFO) << "browseros: MCP server port: " << mcp_port_ + << " (enabled: " << (mcp_enabled_ ? "true" : "false") << ")"; ++ LOG(INFO) << "browseros: Agent server port: " << agent_port_; ++ LOG(INFO) << "browseros: Extension server port: " << extension_port_; +} + +void BrowserOSServerManager::StopCDPServer() { @@ -313,8 +413,18 @@ index 0000000000000..e6bd851f42bfa +} + +void BrowserOSServerManager::LaunchBrowserOSProcess() { -+ // Get executable path on UI thread (PathService::Get is thread-safe) + base::FilePath exe_path = GetBrowserOSServerExecutablePath(); ++ base::FilePath resources_dir = GetBrowserOSServerResourcesPath(); ++ base::FilePath execution_dir = GetBrowserOSExecutionDir(); ++ if (execution_dir.empty()) { ++ LOG(ERROR) << "browseros: Failed to resolve execution directory"; ++ StopCDPServer(); ++ return; ++ } ++ ++ LOG(INFO) << "browseros: Launching server - binary: " << exe_path; ++ LOG(INFO) << "browseros: Launching server - resources: " << resources_dir; ++ LOG(INFO) << "browseros: Launching server - execution dir: " << execution_dir; + + // Capture values to pass to background thread + uint16_t cdp_port = cdp_port_; @@ -325,8 +435,9 @@ index 0000000000000..e6bd851f42bfa + // Post blocking work to background thread, get result back on UI thread + base::ThreadPool::PostTaskAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, -+ base::BindOnce(&LaunchProcessOnBackgroundThread, exe_path, cdp_port, -+ mcp_port, agent_port, extension_port), ++ base::BindOnce(&LaunchProcessOnBackgroundThread, exe_path, resources_dir, ++ execution_dir, cdp_port, mcp_port, agent_port, ++ extension_port), + base::BindOnce(&BrowserOSServerManager::OnProcessLaunched, + weak_factory_.GetWeakPtr())); +} @@ -335,6 +446,7 @@ index 0000000000000..e6bd851f42bfa + if (!process.IsValid()) { + LOG(ERROR) << "browseros: Failed to launch BrowserOS server"; + StopCDPServer(); ++ is_restarting_ = false; + return; + } + @@ -347,21 +459,19 @@ index 0000000000000..e6bd851f42bfa + LOG(INFO) << "browseros: Agent port: " << agent_port_; + LOG(INFO) << "browseros: Extension port: " << extension_port_; + -+ // Save prefs to Local State now that we know the server launched -+ PrefService* prefs = g_browser_process->local_state(); -+ if (prefs) { -+ prefs->SetInteger(browseros_server::kCDPServerPort, cdp_port_); -+ prefs->SetInteger(browseros_server::kMCPServerPort, mcp_port_); -+ prefs->SetInteger(browseros_server::kAgentServerPort, agent_port_); -+ prefs->SetInteger(browseros_server::kExtensionServerPort, extension_port_); -+ prefs->SetBoolean(browseros_server::kMCPServerEnabled, mcp_enabled_); -+ LOG(INFO) << "browseros: Saved prefs to Local State"; -+ } -+ -+ // Start monitoring the process + process_check_timer_.Start(FROM_HERE, base::Seconds(5), this, + &BrowserOSServerManager::CheckProcessStatus); + ++ // Reset restart flag and pref after successful launch ++ if (is_restarting_) { ++ is_restarting_ = false; ++ PrefService* prefs = g_browser_process->local_state(); ++ if (prefs && prefs->GetBoolean(browseros_server::kRestartServerRequested)) { ++ prefs->SetBoolean(browseros_server::kRestartServerRequested, false); ++ LOG(INFO) << "browseros: Restart completed, reset restart_requested pref"; ++ } ++ } ++ + // /init will be sent after first successful periodic health check + + // If MCP is disabled, send control request to disable it @@ -375,26 +485,40 @@ index 0000000000000..e6bd851f42bfa + return; + } + -+ LOG(INFO) << "browseros: Terminating BrowserOS server process"; ++ LOG(INFO) << "browseros: Force killing BrowserOS server process (PID: " ++ << process_.Pid() << ")"; + + // Reset init flag so it gets sent again after restart + init_request_sent_ = false; + -+ // Try graceful shutdown first -+ process_.Terminate(0, false); -+ -+ // Give it some time to shut down, then force kill if still running -+ base::ThreadPool::PostDelayedTask( -+ FROM_HERE, {base::MayBlock()}, -+ base::BindOnce( -+ [](base::Process process) { -+ if (process.IsValid()) { -+ // Force kill if still running -+ process.Terminate(0, false); -+ } -+ }, -+ process_.Duplicate()), -+ base::Seconds(2)); ++ // sync primitives is needed for process termination. ++ // NOTE: only run on background threads ++ base::ScopedAllowBaseSyncPrimitives allow_sync; ++ base::ScopedAllowBlocking allow_blocking; ++ ++#if BUILDFLAG(IS_POSIX) ++ // POSIX: Send SIGKILL for immediate termination (no graceful shutdown) ++ // This matches Windows TerminateProcess behavior ++ base::ProcessId pid = process_.Pid(); ++ if (kill(pid, SIGKILL) == 0) { ++ int exit_code = 0; ++ if (process_.WaitForExit(&exit_code)) { ++ LOG(INFO) << "browseros: Process killed successfully with SIGKILL"; ++ } else { ++ LOG(WARNING) << "browseros: SIGKILL sent but WaitForExit failed"; ++ } ++ } else { ++ PLOG(ERROR) << "browseros: Failed to send SIGKILL to PID " << pid; ++ } ++#else ++ // Windows: TerminateProcess is already immediate force kill ++ bool terminated = process_.Terminate(0, true); ++ if (terminated) { ++ LOG(INFO) << "browseros: Process terminated successfully"; ++ } else { ++ LOG(ERROR) << "browseros: Failed to terminate process"; ++ } ++#endif + + is_running_ = false; +} @@ -553,6 +677,60 @@ index 0000000000000..e6bd851f42bfa + } +} + ++void BrowserOSServerManager::OnRestartServerRequestedChanged() { ++ PrefService* prefs = g_browser_process->local_state(); ++ if (!prefs) { ++ return; ++ } ++ ++ bool restart_requested = prefs->GetBoolean(browseros_server::kRestartServerRequested); ++ ++ // Only process if pref is set to true ++ if (!restart_requested) { ++ return; ++ } ++ ++ // Ignore if already restarting (prevents thrashing from UI spam) ++ if (is_restarting_) { ++ LOG(INFO) << "browseros: Restart already in progress, ignoring duplicate request"; ++ return; ++ } ++ ++ // Ignore if not running ++ if (!is_running_) { ++ LOG(WARNING) << "browseros: Cannot restart - server is not running"; ++ // Reset pref anyway ++ prefs->SetBoolean(browseros_server::kRestartServerRequested, false); ++ return; ++ } ++ ++ LOG(INFO) << "browseros: Server restart requested via preference"; ++ is_restarting_ = true; ++ ++ // Stop timer now (must be on UI thread) ++ process_check_timer_.Stop(); ++ ++ // Capture UI task runner to post back after background work ++ auto ui_task_runner = base::SequencedTaskRunner::GetCurrentDefault(); ++ ++ // Kill process on background thread, then relaunch on UI thread ++ base::ThreadPool::PostTask( ++ FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, ++ base::BindOnce( ++ [](BrowserOSServerManager* manager, ++ scoped_refptr ui_runner) { ++ // Kill old process and wait for exit (blocking, safe on background) ++ manager->TerminateBrowserOSProcess(); ++ ++ // Post back to UI thread to launch new process ++ ui_runner->PostTask( ++ FROM_HERE, ++ base::BindOnce(&BrowserOSServerManager::LaunchBrowserOSProcess, ++ base::Unretained(manager))); ++ }, ++ base::Unretained(this), ui_task_runner)); ++} ++ +void BrowserOSServerManager::SendMCPControlRequest(bool enabled) { + if (!is_running_) { + return; @@ -813,13 +991,13 @@ index 0000000000000..e6bd851f42bfa + return true; +} + -+base::FilePath BrowserOSServerManager::GetBrowserOSServerExecutablePath() const { ++base::FilePath BrowserOSServerManager::GetBrowserOSServerResourcesPath() const { + // Check for command-line override first + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); -+ if (command_line->HasSwitch("browseros-server-binary")) { ++ if (command_line->HasSwitch("browseros-server-resources-dir")) { + base::FilePath custom_path = -+ command_line->GetSwitchValuePath("browseros-server-binary"); -+ LOG(INFO) << "browseros: Using custom server binary from command line: " ++ command_line->GetSwitchValuePath("browseros-server-resources-dir"); ++ LOG(INFO) << "browseros: Using custom resources dir from command line: " + << custom_path; + return custom_path; + } @@ -844,7 +1022,6 @@ index 0000000000000..e6bd851f42bfa + return base::FilePath(); + } + // Append version directory (chrome.release places BrowserOSServer under versioned dir) -+ // chrome/installer/mini_installer/chrome.release + exe_dir = exe_dir.AppendASCII(version_info::GetVersionNumber()); + +#elif BUILDFLAG(IS_LINUX) @@ -855,11 +1032,39 @@ index 0000000000000..e6bd851f42bfa + } +#endif + -+ // Navigate to BrowserOSServer/default/ subdirectory -+ // This structure allows future updates to install to versioned directories -+ base::FilePath browseros_exe = exe_dir.Append(FILE_PATH_LITERAL("BrowserOSServer")) -+ .Append(FILE_PATH_LITERAL("default")) -+ .Append(FILE_PATH_LITERAL("browseros_server")); ++ // Return path to resources directory ++ return exe_dir.Append(FILE_PATH_LITERAL("BrowserOSServer")) ++ .Append(FILE_PATH_LITERAL("default")) ++ .Append(FILE_PATH_LITERAL("resources")); ++} ++ ++base::FilePath BrowserOSServerManager::GetBrowserOSExecutionDir() const { ++ base::FilePath user_data_dir; ++ if (!base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) { ++ LOG(ERROR) << "browseros: Failed to resolve DIR_USER_DATA path"; ++ return base::FilePath(); ++ } ++ ++ base::FilePath exec_dir = user_data_dir.Append(FILE_PATH_LITERAL(".browseros")); ++ ++ // Ensure directory exists before returning ++ base::ScopedAllowBlocking allow_blocking; ++ if (!base::PathExists(exec_dir)) { ++ if (!base::CreateDirectory(exec_dir)) { ++ LOG(ERROR) << "browseros: Failed to create execution directory: " << exec_dir; ++ return base::FilePath(); ++ } ++ } ++ ++ LOG(INFO) << "browseros: Using execution directory: " << exec_dir; ++ return exec_dir; ++} ++ ++base::FilePath BrowserOSServerManager::GetBrowserOSServerExecutablePath() const { ++ base::FilePath browseros_exe = ++ GetBrowserOSServerResourcesPath() ++ .Append(FILE_PATH_LITERAL("bin")) ++ .Append(FILE_PATH_LITERAL("browseros_server")); + +#if BUILDFLAG(IS_WIN) + browseros_exe = browseros_exe.AddExtension(FILE_PATH_LITERAL(".exe")); @@ -867,3 +1072,5 @@ index 0000000000000..e6bd851f42bfa + + return browseros_exe; +} ++ ++} // namespace browseros diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.h b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.h index 687eb850..a6690cd7 100644 --- a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.h +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_manager.h @@ -1,9 +1,9 @@ diff --git a/chrome/browser/browseros_server/browseros_server_manager.h b/chrome/browser/browseros_server/browseros_server_manager.h new file mode 100644 -index 0000000000000..edddc9a8a428c +index 0000000000000..d991d965c4913 --- /dev/null +++ b/chrome/browser/browseros_server/browseros_server_manager.h -@@ -0,0 +1,125 @@ +@@ -0,0 +1,138 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -13,6 +13,7 @@ index 0000000000000..edddc9a8a428c + +#include + ++#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" @@ -30,6 +31,8 @@ index 0000000000000..edddc9a8a428c +class SimpleURLLoader; +} + ++namespace browseros { ++ +// BrowserOS: Manages the lifecycle of the BrowserOS server process (singleton) +// This manager: +// 1. Starts Chromium's CDP WebSocket server (port 9222+, auto-discovered) @@ -80,6 +83,9 @@ index 0000000000000..edddc9a8a428c + BrowserOSServerManager(); + ~BrowserOSServerManager(); + ++ bool AcquireLock(); ++ void InitializePortsAndPrefs(); ++ void SavePortsToPrefs(); + void StartCDPServer(); + void StopCDPServer(); + void LaunchBrowserOSProcess(); @@ -92,6 +98,7 @@ index 0000000000000..edddc9a8a428c + std::unique_ptr url_loader, + scoped_refptr headers); + void OnMCPEnabledChanged(); ++ void OnRestartServerRequestedChanged(); + void SendMCPControlRequest(bool enabled); + void OnMCPControlRequestComplete( + bool requested_state, @@ -103,10 +110,13 @@ index 0000000000000..edddc9a8a428c + scoped_refptr headers); + void CheckProcessStatus(); + ++ base::FilePath GetBrowserOSServerResourcesPath() const; ++ base::FilePath GetBrowserOSExecutionDir() const; + base::FilePath GetBrowserOSServerExecutablePath() const; + int FindAvailablePort(int starting_port); + bool IsPortAvailable(int port); + ++ base::File lock_file_; // System-wide lock to ensure single instance + base::Process process_; + int cdp_port_ = 0; // CDP port (auto-discovered) + int mcp_port_ = 0; // MCP port (auto-discovered) @@ -114,6 +124,7 @@ index 0000000000000..edddc9a8a428c + int extension_port_ = 0; // Extension port (auto-discovered) + bool mcp_enabled_ = true; // Whether MCP server is enabled + bool is_running_ = false; ++ bool is_restarting_ = false; // Whether server is currently restarting + bool init_request_sent_ = false; // Whether /init request has been sent + + // Timer for health checks @@ -128,4 +139,6 @@ index 0000000000000..edddc9a8a428c + base::WeakPtrFactory weak_factory_{this}; +}; + ++} // namespace browseros ++ +#endif // CHROME_BROWSER_BROWSEROS_SERVER_BROWSEROS_SERVER_MANAGER_H_ diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.cc b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.cc index e3c9398a..ff4547be 100644 --- a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.cc +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.cc @@ -1,9 +1,9 @@ diff --git a/chrome/browser/browseros_server/browseros_server_prefs.cc b/chrome/browser/browseros_server/browseros_server_prefs.cc new file mode 100644 -index 0000000000000..fdd265d2f134c +index 0000000000000..f9c7a9990cb01 --- /dev/null +++ b/chrome/browser/browseros_server/browseros_server_prefs.cc -@@ -0,0 +1,43 @@ +@@ -0,0 +1,49 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -29,6 +29,9 @@ index 0000000000000..fdd265d2f134c +// Whether MCP server is enabled +const char kMCPServerEnabled[] = "browseros.server.mcp_enabled"; + ++// Whether server restart has been requested (auto-reset after restart) ++const char kRestartServerRequested[] = "browseros.server.restart_requested"; ++ +void RegisterLocalStatePrefs(PrefRegistrySimple* registry) { + // CDP port + registry->RegisterIntegerPref(kCDPServerPort, kDefaultCDPPort); @@ -44,6 +47,9 @@ index 0000000000000..fdd265d2f134c + + // MCP enabled + registry->RegisterBooleanPref(kMCPServerEnabled, true); ++ ++ // Restart requested (default false, auto-reset after restart) ++ registry->RegisterBooleanPref(kRestartServerRequested, false); +} + +} // namespace browseros_server diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.h b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.h index a38fb585..acc9d032 100644 --- a/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.h +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/browseros_server_prefs.h @@ -1,9 +1,9 @@ diff --git a/chrome/browser/browseros_server/browseros_server_prefs.h b/chrome/browser/browseros_server/browseros_server_prefs.h new file mode 100644 -index 0000000000000..e86296bdc15c7 +index 0000000000000..03719e252a15a --- /dev/null +++ b/chrome/browser/browseros_server/browseros_server_prefs.h -@@ -0,0 +1,30 @@ +@@ -0,0 +1,31 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -27,6 +27,7 @@ index 0000000000000..e86296bdc15c7 +extern const char kAgentServerPort[]; +extern const char kExtensionServerPort[]; +extern const char kMCPServerEnabled[]; ++extern const char kRestartServerRequested[]; + +// Registers BrowserOS server preferences in Local State (browser-wide prefs) +void RegisterLocalStatePrefs(PrefRegistrySimple* registry); diff --git a/packages/browseros/chromium_patches/chrome/browser/browseros_server/validate_resources.py b/packages/browseros/chromium_patches/chrome/browser/browseros_server/validate_resources.py new file mode 100644 index 00000000..2c892019 --- /dev/null +++ b/packages/browseros/chromium_patches/chrome/browser/browseros_server/validate_resources.py @@ -0,0 +1,49 @@ +diff --git a/chrome/browser/browseros_server/validate_resources.py b/chrome/browser/browseros_server/validate_resources.py +new file mode 100644 +index 0000000000000..d7dc82b132dad +--- /dev/null ++++ b/chrome/browser/browseros_server/validate_resources.py +@@ -0,0 +1,43 @@ ++#!/usr/bin/env python3 ++# Copyright 2024 The Chromium Authors ++# Use of this source code is governed by a BSD-style license that can be ++# found in the LICENSE file. ++ ++"""Validates that required BrowserOS resources exist. ++ ++Required resources must be listed in REQUIRED_RESOURCES below. ++""" ++ ++import os ++import sys ++ ++# Required resources that must exist in the resources/ directory ++# Add more resources as needed - paths are relative to resources/ ++REQUIRED_RESOURCES = [ ++ "bin/browseros_server", ++] ++ ++script_dir = os.path.dirname(os.path.abspath(__file__)) ++resources_dir = os.path.join(script_dir, "resources") ++ ++all_valid = True ++for resource in REQUIRED_RESOURCES: ++ resource_path = os.path.join(resources_dir, resource) ++ ++ if not os.path.exists(resource_path): ++ print(f"ERROR: Required BrowserOS resource not found: {resource_path}") ++ all_valid = False ++ continue ++ ++ if not os.path.isfile(resource_path): ++ print(f"ERROR: Resource exists but is not a file: {resource_path}") ++ all_valid = False ++ ++if not all_valid: ++ print(f"\nEnsure all required resources exist in resources/ directory:") ++ for resource in REQUIRED_RESOURCES: ++ print(f" - resources/{resource}") ++ sys.exit(1) ++ ++print(f"✓ BrowserOS resources validated ({len(REQUIRED_RESOURCES)} resources)") ++sys.exit(0) diff --git a/packages/browseros/chromium_patches/chrome/browser/chrome_browser_main.cc b/packages/browseros/chromium_patches/chrome/browser/chrome_browser_main.cc index 64f1b8e9..395bd901 100644 --- a/packages/browseros/chromium_patches/chrome/browser/chrome_browser_main.cc +++ b/packages/browseros/chromium_patches/chrome/browser/chrome_browser_main.cc @@ -1,5 +1,5 @@ diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc -index 681fd3282078c..8b1d525c80bb9 100644 +index 681fd3282078c..df6e7d2cbb9e4 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -10,6 +10,7 @@ @@ -25,7 +25,7 @@ index 681fd3282078c..8b1d525c80bb9 100644 + // BrowserOS: Start the BrowserOS server after browser initialization + LOG(INFO) << "browseros: Starting BrowserOS server process"; -+ BrowserOSServerManager::GetInstance()->Start(); ++ browseros::BrowserOSServerManager::GetInstance()->Start(); + #if BUILDFLAG(IS_WIN) // If the command line specifies 'uninstall' then we need to work here @@ -37,7 +37,7 @@ index 681fd3282078c..8b1d525c80bb9 100644 + + // BrowserOS: Stop the BrowserOS server during shutdown + LOG(INFO) << "browseros: Stopping BrowserOS server process"; -+ BrowserOSServerManager::GetInstance()->Shutdown(); ++ browseros::BrowserOSServerManager::GetInstance()->Shutdown(); + TranslateService::Shutdown(); diff --git a/packages/browseros/chromium_patches/chrome/browser/extensions/browseros_extension_constants.h b/packages/browseros/chromium_patches/chrome/browser/extensions/browseros_extension_constants.h index f2be3d1a..d9118e79 100644 --- a/packages/browseros/chromium_patches/chrome/browser/extensions/browseros_extension_constants.h +++ b/packages/browseros/chromium_patches/chrome/browser/extensions/browseros_extension_constants.h @@ -1,9 +1,9 @@ diff --git a/chrome/browser/extensions/browseros_extension_constants.h b/chrome/browser/extensions/browseros_extension_constants.h new file mode 100644 -index 0000000000000..6bb906bc7068f +index 0000000000000..17b78fbb99a9f --- /dev/null +++ b/chrome/browser/extensions/browseros_extension_constants.h -@@ -0,0 +1,74 @@ +@@ -0,0 +1,80 @@ +// Copyright 2024 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. @@ -30,6 +30,12 @@ index 0000000000000..6bb906bc7068f +inline constexpr char kControllerExtensionId[] = + "nlnihljpboknmfagkikhkdblbedophja"; + ++// BrowserOS CDN update manifest URL ++// Used for extensions installed from local .crx files that don't have ++// an update_url in their manifest ++inline constexpr char kBrowserOSUpdateUrl[] = ++ "https://cdn.browseros.com/extensions/update-manifest.xml"; ++ +// Allowlist of BrowserOS extension IDs that are permitted to be installed +// Only extensions with these IDs will be loaded from the config +constexpr const char* kAllowedExtensions[] = { diff --git a/packages/browseros/chromium_patches/chrome/browser/extensions/extension_management.cc b/packages/browseros/chromium_patches/chrome/browser/extensions/extension_management.cc index 5eecf0db..490fdf12 100644 --- a/packages/browseros/chromium_patches/chrome/browser/extensions/extension_management.cc +++ b/packages/browseros/chromium_patches/chrome/browser/extensions/extension_management.cc @@ -1,5 +1,5 @@ diff --git a/chrome/browser/extensions/extension_management.cc b/chrome/browser/extensions/extension_management.cc -index ae782891ad341..393c6c78e372e 100644 +index ae782891ad341..fa1a80d0265b1 100644 --- a/chrome/browser/extensions/extension_management.cc +++ b/chrome/browser/extensions/extension_management.cc @@ -14,6 +14,7 @@ @@ -10,7 +10,31 @@ index ae782891ad341..393c6c78e372e 100644 #include "base/containers/contains.h" #include "base/feature_list.h" #include "base/functional/bind.h" -@@ -593,6 +594,12 @@ ExtensionIdSet ExtensionManagement::GetForcePinnedList() const { +@@ -244,7 +245,22 @@ GURL ExtensionManagement::GetEffectiveUpdateURL(const Extension& extension) { + << "Update URL cannot be overridden to be the webstore URL!"; + return update_url; + } +- return ManifestURL::GetUpdateURL(&extension); ++ ++ // Get the update URL from the extension's manifest ++ GURL manifest_update_url = ManifestURL::GetUpdateURL(&extension); ++ ++ // BrowserOS extension fallback: If a BrowserOS extension doesn't have an ++ // force-set the BrowserOS CDN update URL so the extension can receive updates ++ if (manifest_update_url.is_empty() && ++ browseros::IsBrowserOSExtension(extension.id())) { ++ const GURL browseros_update_url(browseros::kBrowserOSUpdateUrl); ++ LOG(INFO) << "browseros: Extension " << extension.id() ++ << " missing update_url in manifest, using BrowserOS CDN: " ++ << browseros_update_url.spec(); ++ return browseros_update_url; ++ } ++ ++ return manifest_update_url; + } + + bool ExtensionManagement::UpdatesFromWebstore(const Extension& extension) { +@@ -593,6 +609,12 @@ ExtensionIdSet ExtensionManagement::GetForcePinnedList() const { force_pinned_list.insert(entry.first); } } diff --git a/packages/browseros/chromium_patches/chrome/installer/mini_installer/chrome.release b/packages/browseros/chromium_patches/chrome/installer/mini_installer/chrome.release index 39605553..ba54aa60 100644 --- a/packages/browseros/chromium_patches/chrome/installer/mini_installer/chrome.release +++ b/packages/browseros/chromium_patches/chrome/installer/mini_installer/chrome.release @@ -1,15 +1,16 @@ diff --git a/chrome/installer/mini_installer/chrome.release b/chrome/installer/mini_installer/chrome.release -index 0f331c3e0f2a3..1844d47e4bd39 100644 +index 0f331c3e0f2a3..e16dcd326b97f 100644 --- a/chrome/installer/mini_installer/chrome.release +++ b/chrome/installer/mini_installer/chrome.release -@@ -63,6 +63,11 @@ PrivacySandboxAttestationsPreloaded\privacy-sandbox-attestations.dat: %(VersionD +@@ -63,6 +63,12 @@ PrivacySandboxAttestationsPreloaded\privacy-sandbox-attestations.dat: %(VersionD MEIPreload\manifest.json: %(VersionDir)s\MEIPreload\ MEIPreload\preloaded_data.pb: %(VersionDir)s\MEIPreload\ +# +# BrowserOS Server +# -+BrowserOSServer\default\*.*: %(VersionDir)s\BrowserOSServer\default\ ++BrowserOSServer\default\resources\*.*: %(VersionDir)s\BrowserOSServer\default\resources\ ++BrowserOSServer\default\resources\bin\*.*: %(VersionDir)s\BrowserOSServer\default\resources\bin\ + # # IWA Key Distribution diff --git a/packages/browseros/chromium_patches/chrome/installer/setup/setup_constants.cc b/packages/browseros/chromium_patches/chrome/installer/setup/setup_constants.cc deleted file mode 100644 index 0625a014..00000000 --- a/packages/browseros/chromium_patches/chrome/installer/setup/setup_constants.cc +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/chrome/installer/setup/setup_constants.cc b/chrome/installer/setup/setup_constants.cc -index c3fa94531d449..8f4449ba75e64 100644 ---- a/chrome/installer/setup/setup_constants.cc -+++ b/chrome/installer/setup/setup_constants.cc -@@ -10,7 +10,7 @@ namespace installer { - const wchar_t kChromeArchive[] = L"chrome.7z"; - const wchar_t kChromeCompressedArchive[] = L"chrome.packed.7z"; - const char kVisualElements[] = "VisualElements"; --const wchar_t kVisualElementsManifest[] = L"chrome.VisualElementsManifest.xml"; -+const wchar_t kVisualElementsManifest[] = L"browseros.VisualElementsManifest.xml"; - - // Sub directory of install source package under install temporary directory. - const wchar_t kInstallSourceDir[] = L"source"; diff --git a/packages/browseros/chromium_patches/extensions/browser/process_manager.cc b/packages/browseros/chromium_patches/extensions/browser/process_manager.cc new file mode 100644 index 00000000..0b280b0a --- /dev/null +++ b/packages/browseros/chromium_patches/extensions/browser/process_manager.cc @@ -0,0 +1,50 @@ +diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc +index 426d1f04cddcc..4463d95410b8e 100644 +--- a/extensions/browser/process_manager.cc ++++ b/extensions/browser/process_manager.cc +@@ -36,6 +36,7 @@ + #include "content/public/browser/site_instance.h" + #include "content/public/browser/web_contents.h" + #include "content/public/common/url_constants.h" ++#include "chrome/browser/extensions/browseros_extension_constants.h" + #include "extensions/browser/extension_host.h" + #include "extensions/browser/extension_registry.h" + #include "extensions/browser/extension_system.h" +@@ -990,6 +991,19 @@ void ProcessManager::StartTrackingServiceWorkerRunningInstance( + all_running_extension_workers_.Add(worker_id, browser_context_); + worker_context_ids_[worker_id] = base::Uuid::GenerateRandomV4(); + ++ // BrowserOS: Add permanent keepalive for BrowserOS extensions to prevent ++ // their service workers from being terminated due to inactivity. ++ if (browseros::IsBrowserOSExtension(worker_id.extension_id)) { ++ base::Uuid keepalive_uuid = IncrementServiceWorkerKeepaliveCount( ++ worker_id, ++ content::ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout, ++ Activity::PROCESS_MANAGER, ++ "browseros_permanent_keepalive"); ++ browseros_permanent_keepalives_[worker_id] = keepalive_uuid; ++ VLOG(1) << "browseros: Added permanent keepalive for extension " ++ << worker_id.extension_id; ++ } ++ + // Observe the RenderProcessHost for cleaning up on process shutdown. + int render_process_id = worker_id.render_process_id; + bool inserted = worker_process_to_extension_ids_[render_process_id] +@@ -1076,6 +1090,17 @@ void ProcessManager::StopTrackingServiceWorkerRunningInstance( + return; + } + ++ // BrowserOS: Clean up permanent keepalive for BrowserOS extensions. ++ auto keepalive_iter = browseros_permanent_keepalives_.find(worker_id); ++ if (keepalive_iter != browseros_permanent_keepalives_.end()) { ++ DecrementServiceWorkerKeepaliveCount( ++ worker_id, keepalive_iter->second, Activity::PROCESS_MANAGER, ++ "browseros_permanent_keepalive"); ++ browseros_permanent_keepalives_.erase(keepalive_iter); ++ VLOG(1) << "browseros: Removed permanent keepalive for extension " ++ << worker_id.extension_id; ++ } ++ + all_running_extension_workers_.Remove(worker_id); + worker_context_ids_.erase(worker_id); + for (auto& observer : observer_list_) diff --git a/packages/browseros/chromium_patches/extensions/browser/process_manager.h b/packages/browseros/chromium_patches/extensions/browser/process_manager.h new file mode 100644 index 00000000..215139e5 --- /dev/null +++ b/packages/browseros/chromium_patches/extensions/browser/process_manager.h @@ -0,0 +1,16 @@ +diff --git a/extensions/browser/process_manager.h b/extensions/browser/process_manager.h +index e467d7d6245c3..ba83e0a868ac1 100644 +--- a/extensions/browser/process_manager.h ++++ b/extensions/browser/process_manager.h +@@ -439,6 +439,11 @@ class ProcessManager : public KeyedService, + // A map of the active service worker keepalives. + ServiceWorkerKeepaliveDataMap service_worker_keepalives_; + ++ // BrowserOS: Maps WorkerId to keepalive UUID for BrowserOS extensions that ++ // should never be terminated. These permanent keepalives prevent the service ++ // worker from being killed due to inactivity. ++ std::map browseros_permanent_keepalives_; ++ + // Must be last member, see doc on WeakPtrFactory. + base::WeakPtrFactory weak_ptr_factory_{this}; + }; diff --git a/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-arm64 b/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-arm64 index 49660e87..ed047a72 100755 --- a/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-arm64 +++ b/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-arm64 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:607e53f94b43a9169fec9be0c273f6c1338be7752ac67903dee2033b81afcd03 -size 67267728 +oid sha256:80e4a4fff1924f5c9d37473c92976f936973d3109c17403391d0ddd48363b982 +size 67383312 diff --git a/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-x64 b/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-x64 index 5c922655..ab07a878 100755 --- a/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-x64 +++ b/packages/browseros/resources/binaries/browseros_server/browseros-server-darwin-x64 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf33ae5b9cbc3bf65988bc7f61d5d72501c288ae3f79986d78390ae82f8aec98 -size 73010400 +oid sha256:7cf9692adde856a75c3c284b11a433c6ba43de98708669a32b2195f7ed71d139 +size 73125088 diff --git a/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-arm64 b/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-arm64 index 4b6bb2f5..6a4967a6 100755 --- a/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-arm64 +++ b/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-arm64 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20c4e1bc5e56f30e88d5b1d6b88bd40d21a4f45d3038d7c1dc95ca186179f927 -size 103792748 +oid sha256:cc40be2602d2d374b1beb0533f6ff884439fc1ca66ea611a257830aee75d5af3 +size 103909762 diff --git a/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-x64 b/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-x64 index a395019d..14652b2b 100755 --- a/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-x64 +++ b/packages/browseros/resources/binaries/browseros_server/browseros-server-linux-x64 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b3e3c1db666c297e7ff67dfa54850feb13654d419bd248e3e415937df356edd -size 110481298 +oid sha256:d2968caa3a7bd3fc2390ab26f7e55469de6e4d095bc6e595a1b0b3ff9500af4f +size 110598312 diff --git a/packages/browseros/resources/binaries/browseros_server/browseros-server-windows-x64.exe b/packages/browseros/resources/binaries/browseros_server/browseros-server-windows-x64.exe index 959b245a..b65af9be 100755 --- a/packages/browseros/resources/binaries/browseros_server/browseros-server-windows-x64.exe +++ b/packages/browseros/resources/binaries/browseros_server/browseros-server-windows-x64.exe @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:735a5de94e9885631e69d15d56ad80c260ee270af91c4bcb2c52b0f5faf346cf -size 125859840 +oid sha256:20a1e1ee2dc217674cd111043064463d4d1065e0316938eaebae3d90c0eede7c +size 125977088 diff --git a/packages/browseros/resources/binaries/codex/codex-aarch64-apple-darwin b/packages/browseros/resources/binaries/codex/codex-aarch64-apple-darwin index 497804b6..c652410c 100755 Binary files a/packages/browseros/resources/binaries/codex/codex-aarch64-apple-darwin and b/packages/browseros/resources/binaries/codex/codex-aarch64-apple-darwin differ diff --git a/packages/browseros/resources/binaries/codex/codex-aarch64-pc-windows-msvc.exe b/packages/browseros/resources/binaries/codex/codex-aarch64-pc-windows-msvc.exe index 180653ed..c21f6070 100644 Binary files a/packages/browseros/resources/binaries/codex/codex-aarch64-pc-windows-msvc.exe and b/packages/browseros/resources/binaries/codex/codex-aarch64-pc-windows-msvc.exe differ diff --git a/packages/browseros/resources/binaries/codex/codex-aarch64-unknown-linux-musl b/packages/browseros/resources/binaries/codex/codex-aarch64-unknown-linux-musl index 2eb342ac..5a2bce8e 100755 Binary files a/packages/browseros/resources/binaries/codex/codex-aarch64-unknown-linux-musl and b/packages/browseros/resources/binaries/codex/codex-aarch64-unknown-linux-musl differ diff --git a/packages/browseros/resources/binaries/codex/codex-x86_64-apple-darwin b/packages/browseros/resources/binaries/codex/codex-x86_64-apple-darwin index 2fbe6975..a166e373 100755 Binary files a/packages/browseros/resources/binaries/codex/codex-x86_64-apple-darwin and b/packages/browseros/resources/binaries/codex/codex-x86_64-apple-darwin differ diff --git a/packages/browseros/resources/binaries/codex/codex-x86_64-pc-windows-msvc.exe b/packages/browseros/resources/binaries/codex/codex-x86_64-pc-windows-msvc.exe index 46376737..940ac732 100644 Binary files a/packages/browseros/resources/binaries/codex/codex-x86_64-pc-windows-msvc.exe and b/packages/browseros/resources/binaries/codex/codex-x86_64-pc-windows-msvc.exe differ diff --git a/packages/browseros/resources/binaries/codex/codex-x86_64-unknown-linux-musl b/packages/browseros/resources/binaries/codex/codex-x86_64-unknown-linux-musl index 6e3e4c66..85470ddb 100755 Binary files a/packages/browseros/resources/binaries/codex/codex-x86_64-unknown-linux-musl and b/packages/browseros/resources/binaries/codex/codex-x86_64-unknown-linux-musl differ