store a Target pointer directly in Context
That way, we avoid the racy map access via Browser.executorForTarget. If a context is attached to a target, the Target field must be non-nil. The Browser.pages map is still racy, since multiple tabs can be created concurrently; we'll fix this other data race in another commit.
This commit is contained in:
parent
117274bc5d
commit
1decbccd74
|
@ -9,6 +9,7 @@ package chromedp
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
|
@ -102,16 +103,16 @@ func (b *Browser) send(method cdproto.MethodType, params easyjson.RawMessage) er
|
||||||
return b.conn.Write(msg)
|
return b.conn.Write(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Browser) executorForTarget(ctx context.Context, sessionID target.SessionID) *Target {
|
func (b *Browser) newExecutorForTarget(ctx context.Context, sessionID target.SessionID) *Target {
|
||||||
if sessionID == "" {
|
if sessionID == "" {
|
||||||
panic("empty session ID")
|
panic("empty session ID")
|
||||||
}
|
}
|
||||||
if t, ok := b.pages[sessionID]; ok {
|
if _, ok := b.pages[sessionID]; ok {
|
||||||
return t
|
panic(fmt.Sprintf("executor for %q already exists", sessionID))
|
||||||
}
|
}
|
||||||
t := &Target{
|
t := &Target{
|
||||||
browser: b,
|
browser: b,
|
||||||
sessionID: sessionID,
|
SessionID: sessionID,
|
||||||
|
|
||||||
eventQueue: make(chan *cdproto.Message, 1024),
|
eventQueue: make(chan *cdproto.Message, 1024),
|
||||||
waitQueue: make(chan func(cur *cdp.Frame) bool, 1024),
|
waitQueue: make(chan func(cur *cdp.Frame) bool, 1024),
|
||||||
|
|
12
context.go
12
context.go
|
@ -19,7 +19,7 @@ type Context struct {
|
||||||
|
|
||||||
Browser *Browser
|
Browser *Browser
|
||||||
|
|
||||||
SessionID target.SessionID
|
Target *Target
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContext creates a browser context using the parent context.
|
// NewContext creates a browser context using the parent context.
|
||||||
|
@ -72,12 +72,12 @@ func Run(ctx context.Context, action Action) error {
|
||||||
}
|
}
|
||||||
c.Browser = browser
|
c.Browser = browser
|
||||||
}
|
}
|
||||||
if c.SessionID == "" {
|
if c.Target == nil {
|
||||||
if err := c.newSession(ctx); err != nil {
|
if err := c.newSession(ctx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return action.Do(ctx, c.Browser.executorForTarget(ctx, c.SessionID))
|
return action.Do(ctx, c.Target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) newSession(ctx context.Context) error {
|
func (c *Context) newSession(ctx context.Context) error {
|
||||||
|
@ -93,7 +93,7 @@ func (c *Context) newSession(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
target := c.Browser.executorForTarget(ctx, sessionID)
|
c.Target = c.Browser.newExecutorForTarget(ctx, sessionID)
|
||||||
|
|
||||||
// enable domains
|
// enable domains
|
||||||
for _, enable := range []Action{
|
for _, enable := range []Action{
|
||||||
|
@ -105,12 +105,10 @@ func (c *Context) newSession(ctx context.Context) error {
|
||||||
dom.Enable(),
|
dom.Enable(),
|
||||||
css.Enable(),
|
css.Enable(),
|
||||||
} {
|
} {
|
||||||
if err := enable.Do(ctx, target); err != nil {
|
if err := enable.Do(ctx, c.Target); err != nil {
|
||||||
return fmt.Errorf("unable to execute %T: %v", enable, err)
|
return fmt.Errorf("unable to execute %T: %v", enable, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.SessionID = sessionID
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ func ExampleManyTabs() {
|
||||||
c2 := chromedp.FromContext(ctx2)
|
c2 := chromedp.FromContext(ctx2)
|
||||||
|
|
||||||
fmt.Printf("Same browser: %t\n", c1.Browser == c2.Browser)
|
fmt.Printf("Same browser: %t\n", c1.Browser == c2.Browser)
|
||||||
fmt.Printf("Same tab: %t\n", c1.SessionID == c2.SessionID)
|
fmt.Printf("Same tab: %t\n", c1.Target == c2.Target)
|
||||||
|
|
||||||
// wait for the resources to be cleaned up
|
// wait for the resources to be cleaned up
|
||||||
cancel()
|
cancel()
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
// Target manages a Chrome DevTools Protocol target.
|
// Target manages a Chrome DevTools Protocol target.
|
||||||
type Target struct {
|
type Target struct {
|
||||||
browser *Browser
|
browser *Browser
|
||||||
sessionID target.SessionID
|
SessionID target.SessionID
|
||||||
|
|
||||||
waitQueue chan func(cur *cdp.Frame) bool
|
waitQueue chan func(cur *cdp.Frame) bool
|
||||||
eventQueue chan *cdproto.Message
|
eventQueue chan *cdproto.Message
|
||||||
|
@ -87,7 +87,7 @@ func (t *Target) Execute(ctx context.Context, method string, params json.Marshal
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
sendParams := target.SendMessageToTarget(string(msgJSON)).
|
sendParams := target.SendMessageToTarget(string(msgJSON)).
|
||||||
WithSessionID(t.sessionID)
|
WithSessionID(t.SessionID)
|
||||||
sendParamsJSON, _ := json.Marshal(sendParams)
|
sendParamsJSON, _ := json.Marshal(sendParams)
|
||||||
|
|
||||||
// We want to grab the response from the inner message.
|
// We want to grab the response from the inner message.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user