16 Commits
v0.1.2 ... v0.1

Author SHA1 Message Date
xinglong
5aca12cc3e send messages with select to avoid deadlocks
Fixes #287.
2019-04-01 10:35:07 +01:00
Daniel Martí
e9aa66f87e fix build breakage with newer cdproto versions
This isn't strictly necessary, as one can always build with the earlier
cdproto version specified in go.mod. However, many people still install
chromedp in GOPATH via 'go get -u', so this workaround makes life easier
for a lot of developers.

Fixes #285.
2019-03-28 21:45:27 +00:00
Killian Brackey
39bd95c850 Clarified SIGTERM in shutdown comments 2019-03-02 14:20:46 +00:00
Killian Brackey
b61de69d62 Added SIGTERM to linux systems on shutdown
Closes https://github.com/chromedp/chromedp/issues/274
2019-03-02 14:20:46 +00:00
Daniel Martí
4cc9890745 add a simple issue template
For now, all it asks for is a few versions, and the typical three
questions to understand a bug.
2019-02-22 00:13:27 +01:00
Daniel Martí
37d13f2933 update all mod dependencies 2019-02-21 23:43:53 +01:00
Daniel Martí
26c9acb5b1 avoid ctx.Done() goroutine leak in Selector.run
As spotted in #162 by a contributor, if the context is done before the
Selector.run caller has received from the channel, the spawned goroutine
may leak if blocked on a send.
2019-02-21 17:58:08 +01:00
Daniel Martí
811d6d54d3 don't run TestFileUpload subtests in parallel
Turns out that these subtests are the only pair which cannot run in
parallel with each other. Undo that change and add a TODO. This should
fix the CI failures.

While at it, remove an unnecessary testAllocate line.
2019-02-21 17:22:40 +01:00
Daniel Martí
4c16288502 skip the error log in TestAllocatePortInUse
We know we're going to see an error, so don't log it too:

	$ go test -run TestAllocatePortInUse
	2019/02/21 17:11:34 ERROR: pool could not allocate runner ...
	PASS
	ok      github.com/chromedp/chromedp    0.004s
2019-02-21 17:13:38 +01:00
Daniel Martí
da4f783362 make all tests run in parallel
The subtests were almost all marked as parallel, but that's not enough.
That only makes the subtests run in parallel with other subtests within
the same tests, not with any other test.

Since none of the tests make use of globals nor require the entire
program to themselves, properly run all the tests in parallel.

Speeds up 'go test' on my 8-core laptop from an average of ~130s to an
average of ~50s. Many tests hit timeouts and have sleeps, so we want to
avoid running those sequentially whenever possible.
2019-02-21 13:56:54 +01:00
Daniel Martí
5ca52f3e1b use runner.LookChromeNames in TestMain
It supports alternative names for Chrome such as chromium, as well as
extra names to look for like headless-shell.

Also swap the os.Getenv logic, so that we only do the exec.LookPath work
if the env var is unset.
2019-02-21 13:53:05 +01:00
zhongjiajia
5dc1e0f3af use buffered chan for h.detached 2019-02-20 13:12:41 +01:00
Bob Potter
85ecf4f31f chromedp: fix SetHandlerByID
Don't fall through and return an error if we found a handler with a
matching ID.
2019-02-20 13:01:21 +01:00
Daniel Martí
7f54f3f93c CI: test on 1.11.x instead of tip
tip is rather unstable, so we shouldn't block PRs if it happens to break
our build or tests.

While at it, run 'go mod tidy' with the latest tip version.
2019-01-14 10:38:19 +00:00
Daniel Martí
98d4b0de6e pool: error quickly if we find a port in use
Before the fix, the added test would give a Pool.Allocate error like:

	pool could not connect to 9000: timeout waiting for initial target

The actual underlying error, which can only be seen if one inspects
chrome's stderr, is that it failed to bind to the debugging protocol
port if it was already in use.

This is of course an issue with the environment that chromedp is being
run under, since it was given a port range that wasn't available.
However, the confusing error can lead to developers wasting their time
instead of spotting the error quickly.

Unfortunately, there doesn't seem to be a way to have Chrome exit
immediately if it can't bind to the given port. So, instead of relying
on it, check if the current process can bind to the port first.

Add a test too, where we grab the first port in the pool range, and
check that we get an error that's not confusing.

Fixes #253.
2018-12-01 11:54:16 +00:00
Kenneth Shaw
bf52fed0d3 Fixing windows build issue 2018-07-18 06:19:22 +07:00
14 changed files with 173 additions and 73 deletions

15
.github/ISSUE_TEMPLATE vendored Normal file
View File

@@ -0,0 +1,15 @@
#### What versions are you running?
<pre>
$ go list -m github.com/chromedp/chromedp
$ chromium --version
$ go version
</pre>
#### What did you do?
#### What did you expect to see?
#### What did you see instead?

View File

@@ -1,7 +1,7 @@
language: go
go:
- 1.10.x
- tip
- 1.11.x
addons:
apt:
chrome: stable

View File

@@ -239,6 +239,7 @@ func (c *CDP) SetHandlerByID(id string) error {
if i, ok := c.handlerMap[id]; ok {
c.cur = c.handlers[i]
return nil
}
return fmt.Errorf("no handler associated with target id %s", id)

View File

@@ -4,7 +4,6 @@ import (
"context"
"log"
"os"
"os/exec"
"path"
"testing"
"time"
@@ -78,16 +77,9 @@ func TestMain(m *testing.M) {
// its worth noting that newer versions of chrome (64+) run much faster
// than older ones -- same for headless_shell ...
execPath := runner.DefaultChromePath
if testRunner := os.Getenv("CHROMEDP_TEST_RUNNER"); testRunner != "" {
execPath = testRunner
} else {
// use headless_shell, if on path
var hsPath string
hsPath, err = exec.LookPath("headless_shell")
if err == nil {
execPath = hsPath
}
execPath := os.Getenv("CHROMEDP_TEST_RUNNER")
if execPath == "" {
execPath = runner.LookChromeNames("headless_shell")
}
cliOpts = append(cliOpts, runner.ExecPath(execPath))

11
go.mod
View File

@@ -1,10 +1,9 @@
module github.com/chromedp/chromedp
require (
github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013
github.com/disintegration/imaging v1.4.2
github.com/gorilla/websocket v1.2.0
github.com/knq/sysutil v0.0.0-20180306023629-0218e141a794
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81
github.com/chromedp/cdproto v0.0.0-20190327003620-8d5e1d04ce19
github.com/disintegration/imaging v1.6.0
github.com/gorilla/websocket v1.4.0
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 // indirect
)

31
go.sum
View File

@@ -1,19 +1,16 @@
github.com/chromedp/cdproto v0.0.0-20180522032958-55db67b53f25/go.mod h1:C2GPAraqdt1KfZU7aSmx1XUgarNq/3JmxevQkmCjOVs=
github.com/chromedp/cdproto v0.0.0-20180703215205-c125a34ea3b3 h1:4b4LwyHW4sf6zXZqWsKMYoICFLWGhaqwpEHlnQMN5SE=
github.com/chromedp/cdproto v0.0.0-20180703215205-c125a34ea3b3/go.mod h1:C2GPAraqdt1KfZU7aSmx1XUgarNq/3JmxevQkmCjOVs=
github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013 h1:8nmuTwCseJcww39MvVHI59223+PxSzn6g3cl8ChF0/4=
github.com/chromedp/cdproto v0.0.0-20180713053126-e314dc107013/go.mod h1:C2GPAraqdt1KfZU7aSmx1XUgarNq/3JmxevQkmCjOVs=
github.com/disintegration/imaging v1.4.2 h1:BSVxoYQ2NfLdvIGCDD8GHgBV5K0FCEsc0d/6FxQII3I=
github.com/disintegration/imaging v1.4.2/go.mod h1:9B/deIUIrliYkyMTuXJd6OUFLcrZ2tf+3Qlwnaf/CjU=
github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ=
github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/knq/sysutil v0.0.0-20180306023629-0218e141a794 h1:hgWKTlyruPI7k8W+0FmTMLf+8d2KPxyzTxsfDDQhNp8=
github.com/knq/sysutil v0.0.0-20180306023629-0218e141a794/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
github.com/mailru/easyjson v0.0.0-20180323154445-8b799c424f57/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856 h1:hOnidOuIWNsFRPcxxStGeN3NNm4n4+w6KJ9cVJIh70o=
github.com/mailru/easyjson v0.0.0-20180606163543-3fdea8d05856/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
golang.org/x/image v0.0.0-20180403161127-f315e4403028/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20180628062038-cc896f830ced h1:2QsAEqOy4Mp+V4HL2Wr1iBNpZWaL72EvTO4oj5bmr5w=
golang.org/x/image v0.0.0-20180628062038-cc896f830ced/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
github.com/chromedp/cdproto v0.0.0-20190327003620-8d5e1d04ce19 h1:KOdZXVcB8L3zR4ZsMAnviYJFIgfRP/iYSEzXl7rYXhc=
github.com/chromedp/cdproto v0.0.0-20190327003620-8d5e1d04ce19/go.mod h1:xquOK9dIGFlLaIGI4c6IyfLI/Gz0LiYYuJtzhsUODgI=
github.com/disintegration/imaging v1.6.0 h1:nVPXRUUQ36Z7MNf0O77UzgnOb1mkMMor7lmJMJXc/mA=
github.com/disintegration/imaging v1.6.0/go.mod h1:xuIt+sRxDFrHS0drzXUlCJthkJ8k7lkkUojDSR247MQ=
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307 h1:vl4eIlySbjertFaNwiMjXsGrFVK25aOWLq7n+3gh2ls=
github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ=
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe h1:W/GaMY0y69G4cFlmsC6B9sbuo2fP8OFP1ABjt4kPz+w=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 h1:+vH8qNweCrORN49012OX3h0oWEXO3p+rRnpAGQinddk=
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -89,7 +89,7 @@ func (h *TargetHandler) Run(ctxt context.Context) error {
h.qres = make(chan *cdproto.Message)
h.qevents = make(chan *cdproto.Message)
h.res = make(map[int64]chan *cdproto.Message)
h.detached = make(chan *inspector.EventDetached)
h.detached = make(chan *inspector.EventDetached, 1)
h.pageWaitGroup = new(sync.WaitGroup)
h.domWaitGroup = new(sync.WaitGroup)
h.Unlock()
@@ -155,10 +155,18 @@ func (h *TargetHandler) run(ctxt context.Context) {
switch {
case msg.Method != "":
h.qevents <- msg
select {
case h.qevents <- msg:
case <-ctxt.Done():
return
}
case msg.ID != 0:
h.qres <- msg
select {
case h.qres <- msg:
case <-ctxt.Done():
return
}
default:
h.errf("ignoring malformed incoming message (missing id or method): %#v", msg)
@@ -226,6 +234,15 @@ func (h *TargetHandler) processEvent(ctxt context.Context, msg *cdproto.Message)
if msg == nil {
return ErrChannelClosed
}
switch msg.Method {
case "Page.frameClearedScheduledNavigation",
"Page.frameScheduledNavigation":
// These events are now deprecated, and UnmarshalMessage panics
// when they are received from Chrome. For now, to avoid panics
// and compile errors, and to fix chromedp v0 when installed via
// 'go get -u', skip the events here.
return nil
}
// unmarshal
ev, err := cdproto.UnmarshalMessage(msg)
@@ -346,10 +363,14 @@ func (h *TargetHandler) Execute(ctxt context.Context, methodType string, params
h.resrw.Unlock()
// queue message
h.qcmd <- &cdproto.Message{
select {
case h.qcmd <- &cdproto.Message{
ID: id,
Method: cdproto.MethodType(methodType),
Params: paramsBuf,
}:
case <- ctxt.Done():
return ctxt.Err()
}
errch := make(chan error, 1)
@@ -530,13 +551,9 @@ func (h *TargetHandler) pageEvent(ctxt context.Context, ev interface{}) {
case *page.EventFrameStoppedLoading:
id, op = e.FrameID, frameStoppedLoading
case *page.EventFrameScheduledNavigation:
id, op = e.FrameID, frameScheduledNavigation
case *page.EventFrameClearedScheduledNavigation:
id, op = e.FrameID, frameClearedScheduledNavigation
// ignored events
case *page.EventFrameRequestedNavigation:
return
case *page.EventDomContentEventFired:
return
case *page.EventLoadEventFired:

View File

@@ -21,10 +21,10 @@ const (
)
func TestMouseClickXY(t *testing.T) {
var err error
t.Parallel()
var err error
c := testAllocate(t, "input.html")
defer c.Release()
@@ -78,6 +78,8 @@ func TestMouseClickXY(t *testing.T) {
}
func TestMouseClickNode(t *testing.T) {
t.Parallel()
tests := []struct {
sel, exp string
opt MouseOption
@@ -128,6 +130,8 @@ func TestMouseClickNode(t *testing.T) {
}
func TestMouseClickOffscreenNode(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
exp int
@@ -186,6 +190,8 @@ func TestMouseClickOffscreenNode(t *testing.T) {
}
func TestKeyAction(t *testing.T) {
t.Parallel()
tests := []struct {
sel, exp string
by QueryOption
@@ -238,6 +244,8 @@ func TestKeyAction(t *testing.T) {
}
func TestKeyActionNode(t *testing.T) {
t.Parallel()
tests := []struct {
sel, exp string
by QueryOption

13
pool.go
View File

@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"log"
"net"
"sync"
"github.com/chromedp/chromedp/runner"
@@ -70,6 +71,18 @@ func (p *Pool) Allocate(ctxt context.Context, opts ...runner.CommandLineOption)
r := p.next(ctxt)
// Check if the port is available first. If it's not, Chrome will print
// an "address already in use" error, but it will otherwise keep
// running. This can lead to Allocate succeeding, while the chrome
// process isn't actually listening on the port we need.
l, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", r.port))
if err != nil {
// we can't use this port, e.g. address already in use
p.errf("pool could not allocate runner on port %d: %v", r.port, err)
return nil, err
}
l.Close()
p.debugf("pool allocating %d", r.port)
// create runner

47
pool_test.go Normal file
View File

@@ -0,0 +1,47 @@
package chromedp
import (
"context"
"net"
"strconv"
"strings"
"testing"
)
func TestAllocatePortInUse(t *testing.T) {
t.Parallel()
// take a random available port
l, err := net.Listen("tcp4", "localhost:0")
if err != nil {
t.Fatal(err)
}
defer l.Close()
ctxt, cancel := context.WithCancel(context.Background())
defer cancel()
// make the pool use the port already in use via a port range
_, portStr, _ := net.SplitHostPort(l.Addr().String())
port, _ := strconv.Atoi(portStr)
pool, err := NewPool(
PortRange(port, port+1),
// skip the error log from the used port
PoolLog(nil, nil, func(string, ...interface{}) {}),
)
if err != nil {
t.Fatal(err)
}
c, err := pool.Allocate(ctxt)
if err != nil {
want := "address already in use"
got := err.Error()
if !strings.Contains(got, want) {
t.Fatalf("wanted error to contain %q, but got %q", want, got)
}
} else {
t.Fatal("wanted Allocate to error if port is in use")
c.Release()
}
}

View File

@@ -190,6 +190,8 @@ func TestText(t *testing.T) {
}
func TestClear(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -244,6 +246,8 @@ func TestClear(t *testing.T) {
}
func TestReset(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -315,6 +319,8 @@ func TestValue(t *testing.T) {
}
func TestSetValue(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -442,6 +448,8 @@ func TestAttributesAll(t *testing.T) {
}
func TestSetAttributes(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -547,6 +555,8 @@ func TestAttributeValue(t *testing.T) {
}
func TestSetAttributeValue(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -589,6 +599,8 @@ func TestSetAttributeValue(t *testing.T) {
}
func TestRemoveAttribute(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -626,6 +638,8 @@ func TestRemoveAttribute(t *testing.T) {
}
func TestClick(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -666,6 +680,8 @@ func TestClick(t *testing.T) {
}
func TestDoubleClick(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -703,6 +719,8 @@ func TestDoubleClick(t *testing.T) {
}
func TestSendKeys(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -773,6 +791,8 @@ func TestScreenshot(t *testing.T) {
}
func TestSubmit(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -813,6 +833,8 @@ func TestSubmit(t *testing.T) {
}
func TestComputedStyle(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -870,6 +892,8 @@ func TestComputedStyle(t *testing.T) {
}
func TestMatchedStyle(t *testing.T) {
t.Parallel()
tests := []struct {
sel string
by QueryOption
@@ -940,9 +964,6 @@ func TestFileUpload(t *testing.T) {
t.Fatal(err)
}
c := testAllocate(t, "")
defer c.Release()
tests := []struct {
a Action
}{
@@ -952,6 +973,10 @@ func TestFileUpload(t *testing.T) {
for i, test := range tests {
t.Run(fmt.Sprintf("test %d", i), func(t *testing.T) {
// TODO: refactor the test so the subtests can run in
// parallel
//t.Parallel()
c := testAllocate(t, "")
defer c.Release()

View File

@@ -213,7 +213,8 @@ func (r *Runner) Start(ctxt context.Context, opts ...string) error {
return nil
}
// Shutdown shuts down the Chrome process.
// Shutdown shuts down the Chrome process. Currently only has support for
// SIGTERM in darwin and linux systems
func (r *Runner) Shutdown(ctxt context.Context, opts ...client.Option) error {
var err error
@@ -241,12 +242,15 @@ func (r *Runner) Shutdown(ctxt context.Context, opts ...client.Option) error {
}
}
// osx applications do not automatically exit when all windows (ie, tabs)
// osx and linux applications do not automatically exit when all windows (ie, tabs)
// closed, so send SIGTERM.
//
// TODO: add other behavior here for more process options on shutdown?
if runtime.GOOS == "darwin" && r.cmd != nil && r.cmd.Process != nil {
return r.cmd.Process.Signal(syscall.SIGTERM)
if r.cmd != nil && r.cmd.Process != nil {
switch runtime.GOOS {
case "darwin", "linux":
return r.cmd.Process.Signal(syscall.SIGTERM)
}
}
return nil

View File

@@ -24,21 +24,3 @@ func KillProcessGroup(m map[string]interface{}) error {
func ForceKill(m map[string]interface{}) error {
return nil
}
// EdgeDiagnosticsAdapterWithPath is a command line option to specify using the
// Microsoft Edge Diagnostics adapter at the specified path.
func EdgeDiagnosticsAdapterWithPathAndPort(path string, port int) CommandLineOption {
return func(m map[string]interface{}) error {
m["exec-path"] = path
m["port"] = port
return nil
}
}
// EdgeDiagnosticsAdapter is a command line option to specify using the
// Microsoft Edge Diagnostics adapter found on the path.
//
// If the
func EdgeDiagnosticsAdapter() CommandLineOption {
return EdgeDiagnosticsAdapterWithPathAndPort(findEdgePath(), 9222)
}

2
sel.go
View File

@@ -80,7 +80,7 @@ func (s *Selector) Do(ctxt context.Context, h cdp.Executor) error {
// are invalidated prior to finishing the selector's by, wait, check, and after
// funcs.
func (s *Selector) run(ctxt context.Context, h *TargetHandler) chan error {
ch := make(chan error)
ch := make(chan error, 1)
go func() {
defer close(ch)