How to execute JavaScript in a specified context?
Lua12138 opened this issue · comments
forDream commented
What versions are you running?
$ go list -m github.com/chromedp/chromedp
github.com/chromedp/chromedp v0.9.5
$ google-chrome --version
123.0.6312.86
$ go version
1.22.0
What did you do? Include clear steps.
I want to execute JavaScript in a specific context, for example in a specific iframe in a web page.
This feature is shown in Chrome's DevTools like this
When I select a specific context, the JavaScript entered in the console will only run in that context.
forDream commented
I used a less elegant implementation here. Please let me know if there is a better way later.
example.go
func examples(){
// Omit the content of the creation context.
ctx, cancel := .......
chromedp.ListenTarget(ctx, func(ev interface{}) {
switch ev := ev.(type) {
case *runtime.EventExecutionContextCreated:
data := EventExecutionContextAuxData{}
json.Unmarshal(ev.Context.AuxData, &data)
if data.FrameId != "" {
GlobalExecutionContextPool.Push(data.FrameId, ev.Context.UniqueID)
}
case *runtime.EventExecutionContextDestroyed:
GlobalExecutionContextPool.RemoveByUniqueId(ev.ExecutionContextUniqueID)
case *page.EventFrameNavigated:
// This is my method for executing JavaScript after the frame is navigated, and matching UniqueID through frameId.
go onTargetNavigated(ev, ctx)
})
}
func onTargetNavigated(ev *page.EventFrameNavigated, ctx context.Context){
if strings.HasPrefix(ev.Frame.URL, "http") {
var err error
if ev.Frame.ParentID == "" {
// Parent Page/Non-iframe
} else {
uniqueId := ""
deadline := time.Now().Add(5 * time.Second)
for uniqueId == "" {
time.Sleep(10 * time.Millisecond)
uniqueId = GlobalExecutionContextPool.Find(ev.Frame.ID.String())
if time.Now().UnixMilli() > deadline.UnixMilli() {
break
}
}
if uniqueId == "" {
log.Println("cannot match frame execution context")
return
}
err = chromedp.Run(
ctx,
chromedp.EvaluateAsDevTools(
"alert('current page is ' + location.href)", // this is the script
nil,
func(ep *runtime.EvaluateParams) *runtime.EvaluateParams {
return ep.WithUniqueContextID(uniqueId)
},
),
)
}
}
}
type ExecutionContextPool struct {
pool map[string]string
locker sync.RWMutex
}
var GlobalExecutionContextPool ExecutionContextPool = ExecutionContextPool{
pool: map[string]string{},
}
func (s *ExecutionContextPool) Push(frameId string, uniqueId string) {
log.Println("ExecutionContextPool.Push:", frameId, uniqueId)
// s.pool.Store(frameId, uniqueId)
s.locker.Lock()
defer s.locker.Unlock()
s.pool[frameId] = uniqueId
}
func (s *ExecutionContextPool) Find(frameId string) string {
s.locker.RLock()
defer s.locker.RUnlock()
if val, ok := s.pool[frameId]; ok {
return val
}
return ""
}
func (s *ExecutionContextPool) RemoveByFrameId(frameId string) {
s.locker.Lock()
defer s.locker.Unlock()
delete(s.pool, frameId)
}
func (s *ExecutionContextPool) RemoveByUniqueId(uniqueId string) {
s.locker.Lock()
defer s.locker.Unlock()
for k, v := range s.pool {
if v == uniqueId {
log.Println("ExecutionContextPool.RemoveByUniqueId", k, v)
delete(s.pool, k)
return
}
}
}