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

add exposefunc feature #1222

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open

add exposefunc feature #1222

wants to merge 19 commits into from

Conversation

hillguo
Copy link

@hillguo hillguo commented Dec 12, 2022

In some scenarios, need to bind go func to the browser environment. when js called, the function executes in go process and returns a Promise
refer https://pptr.dev/api/puppeteer.page.exposefunction/

@ZekeLu
Copy link
Member

ZekeLu commented Dec 13, 2022

Thank you! Can you add some tests for the new feature?

Copy link
Member

@ZekeLu ZekeLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will take a careful look later.

expose.go Outdated Show resolved Hide resolved
expose.go Outdated Show resolved Hide resolved
expose.go Outdated Show resolved Hide resolved
Copy link
Member

@ZekeLu ZekeLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the second round. Please take a look. Thank you!

expose.go Outdated Show resolved Hide resolved
expose.go Outdated Show resolved Hide resolved
expose.go Outdated Show resolved Hide resolved
expose.go Outdated Show resolved Hide resolved
js/expose.js Outdated Show resolved Hide resolved
js/expose.js Outdated Show resolved Hide resolved
js/expose.js Outdated Show resolved Hide resolved
expose_test.go Outdated Show resolved Hide resolved
@hillguo
Copy link
Author

hillguo commented Feb 1, 2023

According to your suggestion, the implementation method has been modified.

  1. as an action to call
  2. remove the lock
  3. modify test code

Please review it again

@ZekeLu
Copy link
Member

ZekeLu commented Feb 6, 2023

I'm sorry that it takes a little long to review. I just want to let you know that I'm still working on it.

I'm confusing by this feature. IIUC, runtime.AddBinding adds a new js function to the page. It does not replace an existing function in the page. That means this feature is only useful when the web page is written with the mind that the page will be accessed by a browser that is controlled by a Go application, which will expose a js function to be called by the web page. I can not think of such kind of use cases.

Do you have a real world use case to share? Thank you!

@hillguo
Copy link
Author

hillguo commented Feb 7, 2023

@ZekeLu Yes, I think so, too.
There are two scenarios:

  1. The browser knows in advance that there is a callable function
  2. After chromedp exposes the function, chromedp initiatively calls

Scenario 1, in order to ensure that the function must exist, it usually needs to be exposed before the page js is loaded. (AddScriptToEvaluateOnNewDocument) does this, but it needs to take effect after navigation.

My business scenario is cloud Web game rendering.
A brief introduction to the exposed functions:

  1. GameLoaded. After the game is loaded, js calls this function, and when chromedp receives it, it calls the logic to capture the game screen.
  2. HeartbeatReport. Monitor the normal operation of the game. The reason why we expose go heartbeat function to browser is that heartbeat service in the private network does not have https, but we do not want to use the public network https.
  3. VideoSave. The data recorded by the browser is saved locally.

The two most important points for this feature:

  1. Js environment call
  2. go environment execution

@ZekeLu
Copy link
Member

ZekeLu commented Feb 7, 2023

Thank you for the explanation!

I'm not sure I understand it. But it seems that in the use cases, the js environment does not need the returned value from the Go environment. If this is the case, runtime.AddBinding is enough.

@hillguo
Copy link
Author

hillguo commented Feb 7, 2023

In my scenario, I don't need a return value and runtime.AddBinding is enough.
Other scenes For example:

  1. you need a complex encryption algorithm function without js implementation or js running is inefficient;
  2. read local text content

I'm sorry that my example is not very accurate , more examples can search for puppeteer-related usage
exposefunction

Finally, I think this feature is similar to JSBridge, a means of communication, but not necessary.

@ZekeLu
Copy link
Member

ZekeLu commented Feb 15, 2023

I have put the suggested changes into this commit: ZekeLu@feef44e. The tests could be improved too, but I haven't touched it yet.

Please take a look. If you agree with the suggested changes, please apply them to your commit.

Please note that I'm still not sure whether this function is useful enough to be shipped by chromedp.

@hillguo
Copy link
Author

hillguo commented Feb 16, 2023

Thank you for the code optimization. It's more elegant.😊

Copy link
Member

@ZekeLu ZekeLu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your hard work too!

I have added some more comments.

I think some of them are hard to address.

I'm inclined to convert this feature to an example and add it to https://github.com/chromedp/examples instead. If we provide it as an example, we don't have to address the reminding issues. And we can add it back to the package if someone find it useful and there are attracting use cases. What do you think?

expose.go Outdated
// Note:
// 1. This is the lite version of puppeteer's [page.exposeFunction].
// 2. It adds "chromedpExposeFunc" to the page's window object too.
// 3. The exposed function survives page navigation until the tab is closed?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// 3. The exposed function survives page navigation until the tab is closed?

I think we have to answer this question.

There is not way to revert this action. I think we should mention this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, no way to revert this action.

`Runtime.removeBinding` does not remove binding function from global object 
but unsubscribes current runtime agent from Runtime.bindingCalled notifications.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not only the binding, but also the scripts that will be evaluated on new document (page.AddScriptToEvaluateOnNewDocument). I know that we can store the ScriptIdentifier and remove it later, but it will make the code complicated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It only takes effect on the page after navigation. The target(Page) hasn't changed. this kind of behavior should be understood

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most state does not survive page navigation. But the scripts that the expose action uses survive page navigation and there is no way to stop that (unless the tab is closed), so this behavior should be documented.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get it.

expose.go Outdated Show resolved Hide resolved
js/expose.js Show resolved Hide resolved
})
}

func evaluateOnAllFrames(script string) Action {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ZekeLu expose the function to all frames. please check this function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a test case to cover it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test cases have been added and a bug has been fixed

)

// ExposedFunc is the function type that can be exposed to the browser env.
type ExposedFunc func(args string) (string, error)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be more flexible if the args parameter was defined as an interface{} type.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this design is that it is consistent with the cdp bindingCalled event parameter, which can only pass string.
Refer to Runtime.bindingCalled
https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#event-bindingCalled

PARAMETERS
    name string
    payload string

var payload struct {
    Type string `json:"type"`
    Name string `json:"name"`
    Seq  int64  `json:"seq"`
    Args string `json:"args"`
}```

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

Successfully merging this pull request may close these issues.

None yet

3 participants