Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript execution on unfocused Chrome tabs? #830

Closed
caner-cetin opened this issue Mar 9, 2023 · 6 comments
Closed

Javascript execution on unfocused Chrome tabs? #830

caner-cetin opened this issue Mar 9, 2023 · 6 comments
Labels
enhance New feature or request

Comments

@caner-cetin
Copy link
Contributor

caner-cetin commented Mar 9, 2023

Rod Version: v0.112.6

First watch this to understand what the heck I am trying to say:

2023-03-09.23-10-53.mp4
package main

import (
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/launcher"
	"sync"
)

func main() {
	launch := launcher.New().Leakless(false).MustLaunch()
	browser := rod.New().ControlURL(launch).MustConnect()
	var wg = new(sync.WaitGroup)
	wg.Add(5)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	wg.Wait()
}

func tester(browser *rod.Browser, wg *sync.WaitGroup) {
	defer wg.Done()
	page := browser.MustPage("https://www.wikipedia.org/")
	elem := page.MustElement("#js-link-box-en strong")
	elem.MustClick()
}

This code is trying to achieve click the English button at https://www.wikipedia.org/, in 5 separate tabs. BUUUUUT, unfortunately, I cannot do that.
As you see in the video, MustClick executes when I focus on the tabs. In the background, all of them are idle, except the currently focused one.

I dwindled around a bit, and found this:

https://vanilla.aslushnikov.com/?Page.setWebLifecycleState

from:

puppeteer/puppeteer#3339 (comment)

So uhhhhh, these two might be totally unrelevant, but can I achieve something like this with Go Rod? Like, set the lifecycle state to active before executing MustClick, and boom, now I can click the English button in 5 separate tabs.

sorry if this is a too dumb request, it is 1 AM and my sanity is slowly leaking.

@caner-cetin caner-cetin added the enhance New feature or request label Mar 9, 2023
@rod-robot
Copy link

Please fix the format of your markdown:

35 MD031/blanks-around-fences Fenced code blocks should be surrounded by blank lines [Context: "```"]

generated by check-issue

@caner-cetin
Copy link
Contributor Author

There is a way!

https://sqa.stackexchange.com/a/32355

This forces the page to be hidden or visible. Look, it is visible but I set it to hidden!

image

@caner-cetin
Copy link
Contributor Author

Okay it seems like I cant execute MustClick(), even if I force the pages to be visible
https://user-images.githubusercontent.com/92731060/224166491-b097d407-9c85-4e83-8abc-e2c26ec9fb88.mp4

package main

import (
	"fmt"
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/launcher"
	"github.com/go-rod/rod/lib/proto"
	"sync"
)

func main() {
	launch := launcher.New().Leakless(false).MustLaunch()
	browser := rod.New().ControlURL(launch).MustConnect()
	var wg = new(sync.WaitGroup)
	wg.Add(5)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	wg.Wait()
}

func tester(browser *rod.Browser, wg *sync.WaitGroup) {
	defer wg.Done()
	page := browser.MustPage("https://www.wikipedia.org/")
	page.MustWaitLoad()
	_, err := proto.RuntimeEvaluate{
		IncludeCommandLineAPI: true,
		Expression: `Object.defineProperty(document, 'visibilityState', {value: 'visible', writable: true});
Object.defineProperty(document, 'hidden', {value: false, writable: true});
document.dispatchEvent(new Event("visibilitychange"));`,
	}.Call(page)
	if err != nil {
		panic(err)
	}
	result, err := proto.RuntimeEvaluate{
		IncludeCommandLineAPI: true,
		Expression:            `document.visibilityState`,
	}.Call(page)
	if err != nil {
		panic(err)
	}
	fmt.Println(result.Result.Value)
	elem := page.MustElement("#js-link-box-en strong")
	elem.MustClick()
}

soooooooo, dunno I am out of ideas.

@caner-cetin
Copy link
Contributor Author

Yeah, javascript execution works, I clicked the English with JS, but MustClick() or other input-based functions doesn't work. How can we implement something for this?

2023-03-09.23-49-45.mp4
package main

import (
	"github.com/go-rod/rod"
	"github.com/go-rod/rod/lib/launcher"
	"github.com/go-rod/rod/lib/proto"
	"sync"
)

func main() {
	launch := launcher.New().Leakless(false).MustLaunch()
	browser := rod.New().ControlURL(launch).MustConnect()
	var wg = new(sync.WaitGroup)
	wg.Add(5)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	go tester(browser, wg)
	wg.Wait()
}

func tester(browser *rod.Browser, wg *sync.WaitGroup) {
	defer wg.Done()
	page := browser.MustPage("https://www.wikipedia.org/")
	page.MustWaitLoad()
	_, err := proto.RuntimeEvaluate{
		IncludeCommandLineAPI: true,
		Expression: `Object.defineProperty(document, 'visibilityState', {value: 'visible', writable: true});
Object.defineProperty(document, 'hidden', {value: false, writable: true});
document.dispatchEvent(new Event("visibilitychange"));`,
	}.Call(page)
	if err != nil {
		panic(err)
	}
	_, err = proto.RuntimeEvaluate{
		IncludeCommandLineAPI: true,
		Expression:            `document.visibilityState`,
	}.Call(page)
	if err != nil {
		panic(err)
	}
	_, err = proto.RuntimeEvaluate{
		IncludeCommandLineAPI: true,
		Expression:            `document.querySelector("#js-link-box-en strong").click()`,
	}.Call(page)
	if err != nil {
		panic(err)
	}
}

@ysmood
Copy link
Member

ysmood commented Mar 10, 2023

I think it's a limitation of Chromium.

I usually run multiple browsers to do similar thing, not multiple pages on a single browser.

Rod has browser pool helper to do it:

rod/utils.go

Lines 119 to 126 in 3a88bf4

// BrowserPool to thread-safely limit the number of browsers at the same time.
// It's a common practice to use a channel to limit concurrency, it's not special for rod.
// This helper is more like an example to use Go Channel.
// Reference: https://golang.org/doc/effective_go#channels
type BrowserPool chan *Browser
// NewBrowserPool instance
func NewBrowserPool(limit int) BrowserPool {

rod/browser_test.go

Lines 442 to 450 in 3a88bf4

func TestBrowserPool(t *testing.T) {
pool := rod.NewBrowserPool(3)
create := func() *rod.Browser { return rod.New().MustConnect() }
b := pool.Get(create)
pool.Put(b)
pool.Cleanup(func(p *rod.Browser) {
p.MustClose()
})
}

@caner-cetin
Copy link
Contributor Author

I mean, yeah separate browsers are less painful and more maintenable to use. I wondered this "single browser more tabs" for scalability, "more browser less tabs" eats far more CPU and memory as far as I seen.

I will reach out to you if I can think a non-spaghetti way for "single browser more tabs" approach, for now I will stick with BrowserPool.

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhance New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants