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 (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync/atomic"
|
||||
|
||||
|
@ -102,16 +103,16 @@ func (b *Browser) send(method cdproto.MethodType, params easyjson.RawMessage) er
|
|||
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 == "" {
|
||||
panic("empty session ID")
|
||||
}
|
||||
if t, ok := b.pages[sessionID]; ok {
|
||||
return t
|
||||
if _, ok := b.pages[sessionID]; ok {
|
||||
panic(fmt.Sprintf("executor for %q already exists", sessionID))
|
||||
}
|
||||
t := &Target{
|
||||
browser: b,
|
||||
sessionID: sessionID,
|
||||
SessionID: sessionID,
|
||||
|
||||
eventQueue: make(chan *cdproto.Message, 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
|
||||
|
||||
SessionID target.SessionID
|
||||
Target *Target
|
||||
}
|
||||
|
||||
// NewContext creates a browser context using the parent context.
|
||||
|
@ -72,12 +72,12 @@ func Run(ctx context.Context, action Action) error {
|
|||
}
|
||||
c.Browser = browser
|
||||
}
|
||||
if c.SessionID == "" {
|
||||
if c.Target == nil {
|
||||
if err := c.newSession(ctx); err != nil {
|
||||
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 {
|
||||
|
@ -93,7 +93,7 @@ func (c *Context) newSession(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
target := c.Browser.executorForTarget(ctx, sessionID)
|
||||
c.Target = c.Browser.newExecutorForTarget(ctx, sessionID)
|
||||
|
||||
// enable domains
|
||||
for _, enable := range []Action{
|
||||
|
@ -105,12 +105,10 @@ func (c *Context) newSession(ctx context.Context) error {
|
|||
dom.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)
|
||||
}
|
||||
}
|
||||
|
||||
c.SessionID = sessionID
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ func ExampleManyTabs() {
|
|||
c2 := chromedp.FromContext(ctx2)
|
||||
|
||||
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
|
||||
cancel()
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
// Target manages a Chrome DevTools Protocol target.
|
||||
type Target struct {
|
||||
browser *Browser
|
||||
sessionID target.SessionID
|
||||
SessionID target.SessionID
|
||||
|
||||
waitQueue chan func(cur *cdp.Frame) bool
|
||||
eventQueue chan *cdproto.Message
|
||||
|
@ -87,7 +87,7 @@ func (t *Target) Execute(ctx context.Context, method string, params json.Marshal
|
|||
return err
|
||||
}
|
||||
sendParams := target.SendMessageToTarget(string(msgJSON)).
|
||||
WithSessionID(t.sessionID)
|
||||
WithSessionID(t.SessionID)
|
||||
sendParamsJSON, _ := json.Marshal(sendParams)
|
||||
|
||||
// We want to grab the response from the inner message.
|
||||
|
|
Loading…
Reference in New Issue
Block a user