feat: add PromoteToInteractive and DemoteToDocument for mid-session page transfer
Some checks failed
CI / build (pull_request) Successful in 29s
CI / test (pull_request) Successful in 36s
CI / vet (pull_request) Failing after 6m18s

Allow transferring ownership of a Playwright page between Document and
InteractiveBrowser modes without tearing down the browser. This enables
handing a live page to a human (e.g. for captcha solving) and resuming
scraping on the same page afterward.

Closes #76

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-24 02:27:42 +00:00
parent 39371dc261
commit e0da88b9b0
4 changed files with 155 additions and 22 deletions

View File

@@ -48,10 +48,12 @@ type InteractiveBrowser interface {
}
type interactiveBrowser struct {
pw *playwright.Playwright
browser playwright.Browser
ctx playwright.BrowserContext
page playwright.Page
pw *playwright.Playwright
browser playwright.Browser
ctx playwright.BrowserContext
page playwright.Page
ownsInfrastructure bool
detached bool
}
// NewInteractiveBrowser creates a headless browser with a page ready for interactive control.
@@ -94,10 +96,11 @@ func NewInteractiveBrowser(ctx context.Context, opts ...BrowserOptions) (Interac
ch <- result{
ib: &interactiveBrowser{
pw: res.pw,
browser: res.browser,
ctx: res.bctx,
page: page,
pw: res.pw,
browser: res.browser,
ctx: res.bctx,
page: page,
ownsInfrastructure: true,
},
}
}()
@@ -194,25 +197,31 @@ func (ib *interactiveBrowser) Cookies() ([]Cookie, error) {
}
func (ib *interactiveBrowser) Close() error {
if ib.detached {
return nil
}
var errs []error
if ib.page != nil {
if err := ib.page.Close(); err != nil {
errs = append(errs, err)
}
}
if ib.ctx != nil {
if err := ib.ctx.Close(); err != nil {
errs = append(errs, err)
if ib.ownsInfrastructure {
if ib.ctx != nil {
if err := ib.ctx.Close(); err != nil {
errs = append(errs, err)
}
}
}
if ib.browser != nil {
if err := ib.browser.Close(); err != nil {
errs = append(errs, err)
if ib.browser != nil {
if err := ib.browser.Close(); err != nil {
errs = append(errs, err)
}
}
}
if ib.pw != nil {
if err := ib.pw.Stop(); err != nil {
errs = append(errs, err)
if ib.pw != nil {
if err := ib.pw.Stop(); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {