From b647c708b4dc24ba7010249be757f226102d6dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=AD?= Date: Sat, 6 Apr 2019 22:32:02 +0200 Subject: [PATCH] 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. --- allocate.go | 4 ++++ context.go | 57 +++++++++++++++++++++++++++++++++++++++++++------ context_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 7 deletions(-) create mode 100644 context_test.go diff --git a/allocate.go b/allocate.go index 34d7060..2d2887d 100644 --- a/allocate.go +++ b/allocate.go @@ -124,6 +124,10 @@ func (p *ExecAllocator) Allocate(ctx context.Context) (*Browser, error) { 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...) stderr, err := cmd.StderrPipe() if err != nil { diff --git a/context.go b/context.go index e3a8514..ab10005 100644 --- a/context.go +++ b/context.go @@ -65,7 +65,8 @@ func Run(ctx context.Context, actions ...Action) error { if c == nil || c.Allocator == nil { return ErrInvalidContext } - if c.Browser == nil { + first := c.Browser == nil + if first { browser, err := c.Allocator.Allocate(ctx) if err != nil { return err @@ -73,24 +74,47 @@ func Run(ctx context.Context, actions ...Action) error { c.Browser = browser } if c.Target == nil { - if err := c.newSession(ctx); err != nil { + if err := c.newSession(ctx, first); err != nil { return err } } return Tasks(actions).Do(ctx, c.Target) } -func (c *Context) newSession(ctx context.Context) error { - targetID, err := target.CreateTarget("about:blank").Do(ctx, c.Browser) - if err != nil { - return err +func (c *Context) newSession(ctx context.Context, first bool) error { + var targetID target.ID + if first { + // 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) if err != nil { return err } - c.Target = c.Browser.newExecutorForTarget(ctx, sessionID) // enable domains @@ -111,3 +135,22 @@ func (c *Context) newSession(ctx context.Context) error { } 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) +} diff --git a/context_test.go b/context_test.go new file mode 100644 index 0000000..e970dee --- /dev/null +++ b/context_test.go @@ -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) + } + } +}