don't create an extra tab when starting a browser

Chrome already starts with a blank page, so use that for the first
target context instead of creating a new tab.

Add the first version of the Targets API, which is useful to test this
feature.

Fixes #291.
This commit is contained in:
Daniel Martí 2019-04-06 22:32:02 +02:00
parent 97e80a00d5
commit b647c708b4
3 changed files with 109 additions and 7 deletions

View File

@ -124,6 +124,10 @@ func (p *ExecAllocator) Allocate(ctx context.Context) (*Browser, error) {
p.wg.Done() p.wg.Done()
}() }()
// force the first page to be blank, instead of the welcome page
// TODO: why isn't --no-first-run enough?
args = append(args, "about:blank")
cmd = exec.CommandContext(ctx, p.execPath, args...) cmd = exec.CommandContext(ctx, p.execPath, args...)
stderr, err := cmd.StderrPipe() stderr, err := cmd.StderrPipe()
if err != nil { if err != nil {

View File

@ -65,7 +65,8 @@ func Run(ctx context.Context, actions ...Action) error {
if c == nil || c.Allocator == nil { if c == nil || c.Allocator == nil {
return ErrInvalidContext return ErrInvalidContext
} }
if c.Browser == nil { first := c.Browser == nil
if first {
browser, err := c.Allocator.Allocate(ctx) browser, err := c.Allocator.Allocate(ctx)
if err != nil { if err != nil {
return err return err
@ -73,24 +74,47 @@ func Run(ctx context.Context, actions ...Action) error {
c.Browser = browser c.Browser = browser
} }
if c.Target == nil { if c.Target == nil {
if err := c.newSession(ctx); err != nil { if err := c.newSession(ctx, first); err != nil {
return err return err
} }
} }
return Tasks(actions).Do(ctx, c.Target) return Tasks(actions).Do(ctx, c.Target)
} }
func (c *Context) newSession(ctx context.Context) error { func (c *Context) newSession(ctx context.Context, first bool) error {
targetID, err := target.CreateTarget("about:blank").Do(ctx, c.Browser) var targetID target.ID
if err != nil { if first {
return err // If we just allocated this browser, and it has a single page
// that's blank and not attached, use it.
infos, err := target.GetTargets().Do(ctx, c.Browser)
if err != nil {
return err
}
pages := 0
for _, info := range infos {
if info.Type == "page" && info.URL == "about:blank" && !info.Attached {
targetID = info.TargetID
pages++
}
}
if pages > 1 {
// Multiple blank pages; just in case, don't use any.
targetID = ""
}
}
if targetID == "" {
var err error
targetID, err = target.CreateTarget("about:blank").Do(ctx, c.Browser)
if err != nil {
return err
}
} }
sessionID, err := target.AttachToTarget(targetID).Do(ctx, c.Browser) sessionID, err := target.AttachToTarget(targetID).Do(ctx, c.Browser)
if err != nil { if err != nil {
return err return err
} }
c.Target = c.Browser.newExecutorForTarget(ctx, sessionID) c.Target = c.Browser.newExecutorForTarget(ctx, sessionID)
// enable domains // enable domains
@ -111,3 +135,22 @@ func (c *Context) newSession(ctx context.Context) error {
} }
type ContextOption func(*Context) type ContextOption func(*Context)
// Targets lists all the targets in the browser attached to the given context.
func Targets(ctx context.Context) ([]*target.Info, error) {
// Don't rely on Run, as that needs to be able to call Targets, and we
// don't want cyclic func calls.
c := FromContext(ctx)
if c == nil || c.Allocator == nil {
return nil, ErrInvalidContext
}
if c.Browser == nil {
browser, err := c.Allocator.Allocate(ctx)
if err != nil {
return nil, err
}
c.Browser = browser
}
return target.GetTargets().Do(ctx, c.Browser)
}

55
context_test.go Normal file
View File

@ -0,0 +1,55 @@
package chromedp
import (
"context"
"testing"
)
func TestTargets(t *testing.T) {
t.Parallel()
// Start one browser with one tab.
ctx1, cancel := NewContext(context.Background())
defer cancel()
if err := Run(ctx1); err != nil {
t.Fatal(err)
}
{
infos, err := Targets(ctx1)
if err != nil {
t.Fatal(err)
}
if want, got := 1, len(infos); want != got {
t.Fatalf("want %d targets, got %d", want, got)
}
}
// Start a second tab on the same browser
ctx2, cancel := NewContext(ctx1)
defer cancel()
if err := Run(ctx2); err != nil {
t.Fatal(err)
}
{
infos, err := Targets(ctx2)
if err != nil {
t.Fatal(err)
}
if want, got := 2, len(infos); want != got {
t.Fatalf("want %d targets, got %d", want, got)
}
}
// The first context should also see both targets.
{
infos, err := Targets(ctx1)
if err != nil {
t.Fatal(err)
}
if want, got := 2, len(infos); want != got {
t.Fatalf("want %d targets, got %d", want, got)
}
}
}