Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/browser_harness/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,8 @@ def _open_chrome_inspect():
"""Open chrome://inspect/#remote-debugging so the user can tick the checkbox."""
import platform, subprocess, webbrowser
url = "chrome://inspect/#remote-debugging"
if platform.system() == "Darwin":
system = platform.system()
if system == "Darwin":
try:
subprocess.run([
"osascript",
Expand All @@ -527,6 +528,26 @@ def _open_chrome_inspect():
return
except Exception:
pass
elif system == "Windows":
# webbrowser.open() routes chrome:// through ShellExecute, which has no
# registered handler for the scheme -> Windows shows a "Get an app to
# open this" dialog instead of opening Chrome. Hand the URL to chrome.exe
# directly. (#290)
for env_var, suffix in (
("PROGRAMFILES", "Google/Chrome/Application/chrome.exe"),
("PROGRAMFILES(X86)", "Google/Chrome/Application/chrome.exe"),
("LOCALAPPDATA", "Google/Chrome/Application/chrome.exe"),
):
base = os.environ.get(env_var)
if not base:
continue
candidate = Path(base) / suffix
if candidate.exists():
try:
subprocess.Popen([str(candidate), url])
return
except Exception:
pass
try:
webbrowser.open(url, new=2)
except Exception:
Expand Down
44 changes: 44 additions & 0 deletions tests/unit/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import platform
import subprocess
import webbrowser

import pytest

from browser_harness import admin
Expand Down Expand Up @@ -48,6 +52,46 @@ def test_stale_websocket_does_not_open_chrome_inspect():
assert not admin._needs_chrome_remote_debugging_prompt(msg)


def test_open_chrome_inspect_invokes_chrome_exe_directly_on_windows(tmp_path, monkeypatch):
"""On Windows, webbrowser.open() can't handle chrome:// URLs (#290).

Without a registered protocol handler, ShellExecute pops a "Get an app to
open this" dialog instead of opening Chrome -- so the function must spawn
chrome.exe with the URL as a command-line argument instead.
"""
chrome_exe = tmp_path / "Google" / "Chrome" / "Application" / "chrome.exe"
chrome_exe.parent.mkdir(parents=True)
chrome_exe.touch()

monkeypatch.setattr(platform, "system", lambda: "Windows")
monkeypatch.setenv("PROGRAMFILES", str(tmp_path))
monkeypatch.delenv("PROGRAMFILES(X86)", raising=False)
monkeypatch.delenv("LOCALAPPDATA", raising=False)

spawned, opened = [], []
monkeypatch.setattr(subprocess, "Popen", lambda argv, **kw: spawned.append(argv) or object())
monkeypatch.setattr(webbrowser, "open", lambda url, **kw: opened.append(url) or True)

admin._open_chrome_inspect()

assert spawned == [[str(chrome_exe), "chrome://inspect/#remote-debugging"]]
assert opened == [], "webbrowser.open fallback must not run when chrome.exe was spawned"


def test_open_chrome_inspect_falls_back_to_webbrowser_when_no_chrome_exe_on_windows(monkeypatch):
monkeypatch.setattr(platform, "system", lambda: "Windows")
monkeypatch.delenv("PROGRAMFILES", raising=False)
monkeypatch.delenv("PROGRAMFILES(X86)", raising=False)
monkeypatch.delenv("LOCALAPPDATA", raising=False)

opened = []
monkeypatch.setattr(webbrowser, "open", lambda url, **kw: opened.append(url) or True)

admin._open_chrome_inspect()

assert opened == ["chrome://inspect/#remote-debugging"]


def test_daemon_endpoint_names_discovers_valid_socket_names(tmp_path, monkeypatch):
monkeypatch.setattr(admin.ipc, "IS_WINDOWS", False)
monkeypatch.setattr(admin.ipc, "BH_TMP_DIR", None) # shared-tmpdir mode
Expand Down