3d3bf22ccc
First, we want all of the functionality in a single package; this means collapsing whatever is useful into the root chromedp package. The runner package is being replaced by the Allocator interface, with a default implementation which starts browser processes. The client package doesn't really have a place in the new design. The context, allocator, and browser types will handle the connection with each browser. Finally, the new API is context-based, hence the addition of context.go. The tests have been modified to build and run against the new API.
116 lines
2.4 KiB
Go
116 lines
2.4 KiB
Go
package chromedp
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
)
|
|
|
|
// Executor
|
|
type Executor interface {
|
|
Execute(context.Context, string, json.Marshaler, json.Unmarshaler) error
|
|
}
|
|
|
|
// Context
|
|
type Context struct {
|
|
Allocator Allocator
|
|
|
|
browser *Browser
|
|
handler *TargetHandler
|
|
|
|
logf func(string, ...interface{})
|
|
errf func(string, ...interface{})
|
|
}
|
|
|
|
// Wait can be called after cancelling the context containing Context, to block
|
|
// until all the underlying resources have been cleaned up.
|
|
func (c *Context) Wait() {
|
|
if c.Allocator != nil {
|
|
c.Allocator.Wait()
|
|
}
|
|
}
|
|
|
|
// NewContext creates a browser context using the parent context.
|
|
func NewContext(parent context.Context, opts ...ContextOption) (context.Context, context.CancelFunc) {
|
|
ctx, cancel := context.WithCancel(parent)
|
|
|
|
c := &Context{}
|
|
if pc := FromContext(parent); pc != nil {
|
|
c.Allocator = pc.Allocator
|
|
}
|
|
|
|
for _, o := range opts {
|
|
o(c)
|
|
}
|
|
if c.Allocator == nil {
|
|
WithExecAllocator(
|
|
NoFirstRun,
|
|
NoDefaultBrowserCheck,
|
|
Headless,
|
|
)(&c.Allocator)
|
|
}
|
|
|
|
ctx = context.WithValue(ctx, contextKey{}, c)
|
|
return ctx, cancel
|
|
}
|
|
|
|
type contextKey struct{}
|
|
|
|
// FromContext creates a new browser context from the provided context.
|
|
func FromContext(ctx context.Context) *Context {
|
|
c, _ := ctx.Value(contextKey{}).(*Context)
|
|
return c
|
|
}
|
|
|
|
// Run runs the action against the provided browser context.
|
|
func Run(ctx context.Context, action Action) error {
|
|
c := FromContext(ctx)
|
|
if c == nil || c.Allocator == nil {
|
|
return ErrInvalidContext
|
|
}
|
|
if c.browser == nil {
|
|
browser, err := c.Allocator.Allocate(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.browser = browser
|
|
}
|
|
if c.handler == nil {
|
|
if err := c.newHandler(ctx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return action.Do(ctx, c.handler)
|
|
}
|
|
|
|
func (c *Context) newHandler(ctx context.Context) error {
|
|
// TODO: add RemoteAddr() to the Transport interface?
|
|
conn := c.browser.conn.(*Conn).Conn
|
|
addr := conn.RemoteAddr()
|
|
url := "http://" + addr.String() + "/json/new"
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
var wurl withWebsocketURL
|
|
if err := json.NewDecoder(resp.Body).Decode(&wurl); err != nil {
|
|
return err
|
|
}
|
|
c.handler, err = NewTargetHandler(wurl.WebsocketURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := c.handler.Run(ctx); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type withWebsocketURL struct {
|
|
WebsocketURL string `json:"webSocketDebuggerUrl"`
|
|
}
|
|
|
|
// ContextOption
|
|
type ContextOption func(*Context)
|