fix: set default viewport for NewBrowser and align User-Agent with engine
NewBrowser previously had no viewport (strong headless signal) and used a Firefox User-Agent unconditionally, even for Chromium instances (detectable mismatch). Add per-engine UA constants (DefaultFirefoxUserAgent, DefaultChromiumUserAgent) and auto-select the matching UA in initBrowser when the caller hasn't set one explicitly. Keep DefaultUserAgent as a backward-compatible alias. Add 1920x1080 default viewport to NewBrowser (most common desktop resolution). NewInteractiveBrowser keeps its existing 1280x720 default but also gains engine-aware UA selection. Closes #70 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -52,6 +52,16 @@ func initBrowser(opt BrowserOptions) (*browserInitResult, error) {
|
|||||||
return nil, ErrInvalidBrowserSelection
|
return nil, ErrInvalidBrowserSelection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-select a User-Agent matching the browser engine when the caller hasn't set one.
|
||||||
|
if opt.UserAgent == "" {
|
||||||
|
switch opt.Browser {
|
||||||
|
case BrowserChromium:
|
||||||
|
opt.UserAgent = DefaultChromiumUserAgent
|
||||||
|
default:
|
||||||
|
opt.UserAgent = DefaultFirefoxUserAgent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Collect launch args and init scripts, starting with any stealth-mode presets.
|
// Collect launch args and init scripts, starting with any stealth-mode presets.
|
||||||
stealth := opt.Stealth == nil || *opt.Stealth
|
stealth := opt.Stealth == nil || *opt.Stealth
|
||||||
var launchArgs []string
|
var launchArgs []string
|
||||||
|
|||||||
@@ -59,10 +59,9 @@ type interactiveBrowser struct {
|
|||||||
func NewInteractiveBrowser(ctx context.Context, opts ...BrowserOptions) (InteractiveBrowser, error) {
|
func NewInteractiveBrowser(ctx context.Context, opts ...BrowserOptions) (InteractiveBrowser, error) {
|
||||||
var thirtySeconds = 30 * time.Second
|
var thirtySeconds = 30 * time.Second
|
||||||
opt := mergeOptions(BrowserOptions{
|
opt := mergeOptions(BrowserOptions{
|
||||||
UserAgent: DefaultUserAgent,
|
Browser: BrowserFirefox,
|
||||||
Browser: BrowserFirefox,
|
Timeout: &thirtySeconds,
|
||||||
Timeout: &thirtySeconds,
|
Stealth: Bool(true),
|
||||||
Stealth: Bool(true),
|
|
||||||
Dimensions: Size{
|
Dimensions: Size{
|
||||||
Width: 1280,
|
Width: 1280,
|
||||||
Height: 720,
|
Height: 720,
|
||||||
|
|||||||
@@ -36,8 +36,14 @@ const (
|
|||||||
BrowserWebKit BrowserSelection = "webkit"
|
BrowserWebKit BrowserSelection = "webkit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultUserAgent is the user-agent string used by all browser instances.
|
// DefaultFirefoxUserAgent is the user-agent string used for Firefox browser instances.
|
||||||
const DefaultUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
|
const DefaultFirefoxUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0"
|
||||||
|
|
||||||
|
// DefaultChromiumUserAgent is the user-agent string used for Chromium browser instances.
|
||||||
|
const DefaultChromiumUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
||||||
|
|
||||||
|
// DefaultUserAgent is an alias for DefaultFirefoxUserAgent, retained for backward compatibility.
|
||||||
|
const DefaultUserAgent = DefaultFirefoxUserAgent
|
||||||
|
|
||||||
// Bool returns a pointer to the given bool value.
|
// Bool returns a pointer to the given bool value.
|
||||||
func Bool(v bool) *bool { return &v }
|
func Bool(v bool) *bool { return &v }
|
||||||
@@ -47,7 +53,7 @@ type Size struct {
|
|||||||
Height int
|
Height int
|
||||||
}
|
}
|
||||||
type BrowserOptions struct {
|
type BrowserOptions struct {
|
||||||
UserAgent string // If empty, defaults to DefaultUserAgent
|
UserAgent string // If empty, auto-selected based on Browser engine
|
||||||
Browser BrowserSelection // If unset defaults to Firefox.
|
Browser BrowserSelection // If unset defaults to Firefox.
|
||||||
Timeout *time.Duration // If unset defaults to 30 seconds timeout. If set to 0, no timeout
|
Timeout *time.Duration // If unset defaults to 30 seconds timeout. If set to 0, no timeout
|
||||||
|
|
||||||
@@ -145,10 +151,13 @@ func playwrightCookieToCookie(cookie playwright.Cookie) Cookie {
|
|||||||
func NewBrowser(ctx context.Context, opts ...BrowserOptions) (Browser, error) {
|
func NewBrowser(ctx context.Context, opts ...BrowserOptions) (Browser, error) {
|
||||||
var thirtySeconds = 30 * time.Second
|
var thirtySeconds = 30 * time.Second
|
||||||
opt := mergeOptions(BrowserOptions{
|
opt := mergeOptions(BrowserOptions{
|
||||||
UserAgent: DefaultUserAgent,
|
Browser: BrowserFirefox,
|
||||||
Browser: BrowserFirefox,
|
Timeout: &thirtySeconds,
|
||||||
Timeout: &thirtySeconds,
|
Stealth: Bool(true),
|
||||||
Stealth: Bool(true),
|
Dimensions: Size{
|
||||||
|
Width: 1920,
|
||||||
|
Height: 1080,
|
||||||
|
},
|
||||||
}, opts)
|
}, opts)
|
||||||
|
|
||||||
if err := ctx.Err(); err != nil {
|
if err := ctx.Err(); err != nil {
|
||||||
|
|||||||
@@ -374,3 +374,68 @@ func TestStealthFirefoxScripts_NoChromiumMarkers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- User-Agent constants ---
|
||||||
|
|
||||||
|
func TestDefaultUserAgent_BackwardCompat(t *testing.T) {
|
||||||
|
if DefaultUserAgent != DefaultFirefoxUserAgent {
|
||||||
|
t.Fatal("DefaultUserAgent must equal DefaultFirefoxUserAgent for backward compatibility")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultFirefoxUserAgent_Content(t *testing.T) {
|
||||||
|
if !strings.Contains(DefaultFirefoxUserAgent, "Firefox") {
|
||||||
|
t.Fatal("DefaultFirefoxUserAgent must contain 'Firefox'")
|
||||||
|
}
|
||||||
|
if strings.Contains(DefaultFirefoxUserAgent, "Chrome") {
|
||||||
|
t.Fatal("DefaultFirefoxUserAgent must not contain 'Chrome'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDefaultChromiumUserAgent_Content(t *testing.T) {
|
||||||
|
if !strings.Contains(DefaultChromiumUserAgent, "Chrome") {
|
||||||
|
t.Fatal("DefaultChromiumUserAgent must contain 'Chrome'")
|
||||||
|
}
|
||||||
|
if strings.Contains(DefaultChromiumUserAgent, "Firefox") {
|
||||||
|
t.Fatal("DefaultChromiumUserAgent must not contain 'Firefox'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Viewport and UA defaults via mergeOptions ---
|
||||||
|
|
||||||
|
func TestMergeOptions_DefaultViewport(t *testing.T) {
|
||||||
|
base := BrowserOptions{
|
||||||
|
Dimensions: Size{Width: 1920, Height: 1080},
|
||||||
|
}
|
||||||
|
got := mergeOptions(base, nil)
|
||||||
|
if got.Dimensions.Width != 1920 || got.Dimensions.Height != 1080 {
|
||||||
|
t.Fatalf("expected default viewport 1920x1080, got %dx%d", got.Dimensions.Width, got.Dimensions.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeOptions_ViewportOverride(t *testing.T) {
|
||||||
|
base := BrowserOptions{
|
||||||
|
Dimensions: Size{Width: 1920, Height: 1080},
|
||||||
|
}
|
||||||
|
got := mergeOptions(base, []BrowserOptions{{Dimensions: Size{Width: 1280, Height: 720}}})
|
||||||
|
if got.Dimensions.Width != 1280 || got.Dimensions.Height != 720 {
|
||||||
|
t.Fatalf("expected overridden viewport 1280x720, got %dx%d", got.Dimensions.Width, got.Dimensions.Height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeOptions_EmptyUANotOverridden(t *testing.T) {
|
||||||
|
base := BrowserOptions{}
|
||||||
|
got := mergeOptions(base, []BrowserOptions{{Browser: BrowserChromium}})
|
||||||
|
if got.UserAgent != "" {
|
||||||
|
t.Fatalf("expected empty UserAgent after merge with no explicit UA, got %q", got.UserAgent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMergeOptions_ExplicitUAPreserved(t *testing.T) {
|
||||||
|
base := BrowserOptions{}
|
||||||
|
customUA := "MyCustomAgent/1.0"
|
||||||
|
got := mergeOptions(base, []BrowserOptions{{UserAgent: customUA}})
|
||||||
|
if got.UserAgent != customUA {
|
||||||
|
t.Fatalf("expected explicit UA %q preserved, got %q", customUA, got.UserAgent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user