Compare commits
4 Commits
61b68adfd0
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b6d864330 | |||
| adefaaef36 | |||
| d89031b20d | |||
| 84e811572b |
@@ -48,6 +48,16 @@ type InteractiveBrowser interface {
|
|||||||
// Cookies returns all cookies from the browser context.
|
// Cookies returns all cookies from the browser context.
|
||||||
Cookies() ([]Cookie, error)
|
Cookies() ([]Cookie, error)
|
||||||
|
|
||||||
|
// SetDefaultTimeout sets the default timeout for all Playwright operations
|
||||||
|
// (navigation, clicks, screenshots, cookie extraction, etc.). A value of 0
|
||||||
|
// disables timeouts. By default, Playwright uses a 30-second timeout.
|
||||||
|
//
|
||||||
|
// This is the primary mechanism for preventing hung sessions: callers can
|
||||||
|
// set a timeout so that any Playwright call returns an error instead of
|
||||||
|
// blocking forever if the browser process crashes or the remote server
|
||||||
|
// becomes unresponsive.
|
||||||
|
SetDefaultTimeout(timeout time.Duration)
|
||||||
|
|
||||||
// Close tears down the browser.
|
// Close tears down the browser.
|
||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
@@ -246,6 +256,14 @@ func (ib *interactiveBrowser) Cookies() ([]Cookie, error) {
|
|||||||
return cookies, nil
|
return cookies, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ib *interactiveBrowser) SetDefaultTimeout(timeout time.Duration) {
|
||||||
|
ms := float64(timeout.Milliseconds())
|
||||||
|
ib.page.SetDefaultTimeout(ms)
|
||||||
|
ib.page.SetDefaultNavigationTimeout(ms)
|
||||||
|
ib.ctx.SetDefaultTimeout(ms)
|
||||||
|
ib.ctx.SetDefaultNavigationTimeout(ms)
|
||||||
|
}
|
||||||
|
|
||||||
func (ib *interactiveBrowser) Close() error {
|
func (ib *interactiveBrowser) Close() error {
|
||||||
if ib.detached {
|
if ib.detached {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -122,12 +122,17 @@ func playwrightSameSiteToSameSite(s *playwright.SameSiteAttribute) SameSite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cookieToPlaywrightOptionalCookie(cookie Cookie) playwright.OptionalCookie {
|
func cookieToPlaywrightOptionalCookie(cookie Cookie) playwright.OptionalCookie {
|
||||||
|
expires := float64(cookie.Expires.Unix())
|
||||||
|
if cookie.Expires.IsZero() || expires <= 0 {
|
||||||
|
expires = -1
|
||||||
|
}
|
||||||
|
|
||||||
oc := playwright.OptionalCookie{
|
oc := playwright.OptionalCookie{
|
||||||
Name: cookie.Name,
|
Name: cookie.Name,
|
||||||
Value: cookie.Value,
|
Value: cookie.Value,
|
||||||
Domain: playwright.String(cookie.Host),
|
Domain: playwright.String(cookie.Host),
|
||||||
Path: playwright.String(cookie.Path),
|
Path: playwright.String(cookie.Path),
|
||||||
Expires: playwright.Float(float64(cookie.Expires.Unix())),
|
Expires: playwright.Float(expires),
|
||||||
Secure: playwright.Bool(cookie.Secure),
|
Secure: playwright.Bool(cookie.Secure),
|
||||||
HttpOnly: playwright.Bool(cookie.HttpOnly),
|
HttpOnly: playwright.Bool(cookie.HttpOnly),
|
||||||
}
|
}
|
||||||
@@ -214,11 +219,35 @@ func (b playWrightBrowser) updateCookies(_ context.Context, page playwright.Page
|
|||||||
return fmt.Errorf("error getting cookies from browser: %w", err)
|
return fmt.Errorf("error getting cookies from browser: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build a lookup of existing cookies so we can preserve their security
|
||||||
|
// attributes. Chromium's Cookies() API can lose or normalize Secure,
|
||||||
|
// SameSite, and HttpOnly during the AddCookies → navigate → Cookies()
|
||||||
|
// round-trip, so we only update Value and Expires for cookies that
|
||||||
|
// already exist in the jar.
|
||||||
|
existing, err := b.cookieJar.Get(page.URL())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error getting existing cookies from jar: %w", err)
|
||||||
|
}
|
||||||
|
type cookieKey struct{ Name, Path string }
|
||||||
|
existingMap := make(map[cookieKey]Cookie, len(existing))
|
||||||
|
for _, c := range existing {
|
||||||
|
existingMap[cookieKey{c.Name, c.Path}] = c
|
||||||
|
}
|
||||||
|
|
||||||
for _, cookie := range cookies {
|
for _, cookie := range cookies {
|
||||||
// TODO: add support for deleting cookies from the jar which are deleted in the browser
|
// TODO: add support for deleting cookies from the jar which are deleted in the browser
|
||||||
err = b.cookieJar.Set(playwrightCookieToCookie(cookie))
|
c := playwrightCookieToCookie(cookie)
|
||||||
|
|
||||||
if err != nil {
|
if prev, ok := existingMap[cookieKey{c.Name, c.Path}]; ok {
|
||||||
|
// Preserve the original security attributes; only update
|
||||||
|
// Value and Expires which are the fields that legitimately
|
||||||
|
// change during navigation.
|
||||||
|
c.Secure = prev.Secure
|
||||||
|
c.HttpOnly = prev.HttpOnly
|
||||||
|
c.SameSite = prev.SameSite
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = b.cookieJar.Set(c); err != nil {
|
||||||
return fmt.Errorf("error setting cookie in cookie jar: %w", err)
|
return fmt.Errorf("error setting cookie in cookie jar: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package extractor
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mockInteractiveBrowser implements InteractiveBrowser for testing without Playwright.
|
// mockInteractiveBrowser implements InteractiveBrowser for testing without Playwright.
|
||||||
@@ -22,6 +23,7 @@ func (m mockInteractiveBrowser) KeyboardPress(string) error { return
|
|||||||
func (m mockInteractiveBrowser) KeyboardInsertText(string) error { return nil }
|
func (m mockInteractiveBrowser) KeyboardInsertText(string) error { return nil }
|
||||||
func (m mockInteractiveBrowser) Screenshot(int) ([]byte, error) { return nil, nil }
|
func (m mockInteractiveBrowser) Screenshot(int) ([]byte, error) { return nil, nil }
|
||||||
func (m mockInteractiveBrowser) Cookies() ([]Cookie, error) { return nil, nil }
|
func (m mockInteractiveBrowser) Cookies() ([]Cookie, error) { return nil, nil }
|
||||||
|
func (m mockInteractiveBrowser) SetDefaultTimeout(time.Duration) {}
|
||||||
func (m mockInteractiveBrowser) Close() error { return nil }
|
func (m mockInteractiveBrowser) Close() error { return nil }
|
||||||
|
|
||||||
func TestPromoteToInteractive_NonPromotable(t *testing.T) {
|
func TestPromoteToInteractive_NonPromotable(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user