日期:2023年7月8日
作者:Commas
签名:(ง •_•)ง 积跬步以致千里,积小流以成江海……
注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^
1.01365 = 37.7834;0.99365 = 0.0255
1.02365 = 1377.4083;0.98365 = 0.0006

一、前言
上期教程《【Go|第5期】Lorca无法正常运行的解决方案》解决了 Chrome 安全机制导致无法正常运行 Lorca 程序的问题。小伙伴们可能又发现了另外一个问题,Lorca 窗口的副标题上 多了一行非常醒目的字:Chrome正受到自动测试软件的控制。虽然功能方面不受影响,但是这一行字却足以让你的用户感到困惑。
-
示例一:

-
示例二:

-
示例三:

二、分析问题
关于“Chrome正受到自动测试软件的控制”这个副标题的问题,一般是由于浏览器的自动化测试工具或插件所引起的。这些工具可能会在浏览器窗口的标题栏中显示此消息,以指示当前的浏览器会话处于自动化测试模式下。
- 第一点,Lorca 整个运行过程中,没有使用
Selenium或其他类似的自动化测试框架进行Web
应用程序测试; - 第二点,Lorca 在启动的时候,添加了 禁用浏览器扩展(
Extensions)功能 的命令行启动参数; - 第三点,旧版
Chrome不会显示此类副标题,可能没有实现这一功能,而新版则增加了这个浏览器行为,旨在提醒用户当前浏览器正处于自动化测试模式下。
不确定未来最新版本会不会解决此问题,与其寄托未来,不如把握现在。既然是 自动化测试(automation) 这个因素引起的,那么我们是否可以查找一下 Lorca 是否运行的时候有添加此类参数呢?
(1)打开 Lorca 库
main.go的代码片段:
package main
import (
"log"
"net/url"
"github.com/zserge/lorca"
)
func main() {
// Create UI with basic HTML passed via data URI
ui, err := lorca.New("data:text/html,"+url.PathEscape(`
<html>
<head><title>Hello</title></head>
<body><h1>Hello, world!</h1></body>
</html>
`), "", 480, 320, "--remote-allow-origins=*")
if err != nil {
log.Fatal(err)
}
defer ui.Close()
// Wait until UI window is closed
<-ui.Done()
}
- 截图说明:
Ctrl + 鼠标左键,点选lorca.New中的New, 打开Lorca库。

Lorca库的ui.go代码片段
func New(url, dir string, width, height int, customArgs ...string) (UI, error) {
if url == "" {
url = "data:text/html,<html></html>"
}
tmpDir := ""
if dir == "" {
name, err := ioutil.TempDir("", "lorca")
if err != nil {
return nil, err
}
dir, tmpDir = name, name
}
args := append(defaultChromeArgs, fmt.Sprintf("--app=%s", url))
args = append(args, fmt.Sprintf("--user-data-dir=%s", dir))
args = append(args, fmt.Sprintf("--window-size=%d,%d", width, height))
args = append(args, customArgs...)
args = append(args, "--remote-debugging-port=0")
chrome, err := newChromeWithArgs(ChromeExecutable(), args...)
done := make(chan struct{})
if err != nil {
return nil, err
}
go func() {
chrome.cmd.Wait()
close(done)
}()
return &ui{chrome: chrome, done: done, tmpDir: tmpDir}, nil
}
(2)分析 Lorca 库中的 New 函数
1、chrome, err := newChromeWithArgs(ChromeExecutable(), args...) 这一行代码是创建一个 chrome 实例,其中 args 就是启动程序的命令行参数的切片(slice)。

| 命令行参数 | 参数说明 |
|---|---|
--app=<url> | 用于指定要打开的应用程序或网页,其中 <url> 提供要打开的应用程序或网页的 URL。通过使用 -app= 参数,您可以在 Chromium 浏览器中直接打开指定的应用程序或网页,而无需显示浏览器的地址栏、标签栏和其他界面元素。 |
--user-data-dir=<path> | 用于指定用户数据目录的路径,其中 <path> 代表需要指定的路径。每个用户在 Chromium 浏览器中都有一个对应的用户数据目录,用于存储其个人配置、书签、扩展、历史记录等数据。通过使用 --user-data-dir= 参数,您可以自定义用户数据目录的路径。 |
--window-size=<w>,<h> | 用于设置浏览器窗口的尺寸,其中 <w> 代表宽度像素值,<h> 代表高度像素值。通过使用 --window-size= 参数,您可以指定浏览器窗口的宽度和高度。尺寸可以以像素(px)为单位进行指定。 |
-remote-debugging-port=0 | 用于禁用远程调试端口。Chromium 浏览器默认情况下会打开一个用于远程调试的端口,允许开发者通过远程连接来调试和控制浏览器的行为。该参数允许您指定要使用的特定端口号,但使用0会禁用远程调试端口。 |
这些参数也并非我们所要查找的参数,这里可以过了。
2、接着,我们顺藤摸瓜看一下 customArgs,如下:
var defaultChromeArgs = []string{
"--disable-background-networking",
"--disable-background-timer-throttling",
"--disable-backgrounding-occluded-windows",
"--disable-breakpad",
"--disable-client-side-phishing-detection",
"--disable-default-apps",
"--disable-dev-shm-usage",
"--disable-infobars",
"--disable-extensions",
"--disable-features=site-per-process",
"--disable-hang-monitor",
"--disable-ipc-flooding-protection",
"--disable-popup-blocking",
"--disable-prompt-on-repost",
"--disable-renderer-backgrounding",
"--disable-sync",
"--disable-translate",
"--disable-windows10-custom-titlebar",
"--metrics-recording-only",
"--no-first-run",
"--no-default-browser-check",
"--safebrowsing-disable-auto-update",
"--enable-automation",
"--password-store=basic",
"--use-mock-keychain",
}
其中,--enable-automation 貌似与自动化测试(automation)有关,这不就是我们想要找到参数吗?
| 命令行参数 | 参数说明 |
|---|---|
–enable-automation | 用于启用自动化功能。通过启用自动化功能,您可以使用自动化工具(如 Selenium)或脚本控制 Chromium 浏览器的行为。这对于自动化测试、网络爬虫或其他自动化任务非常有用。 |
另外,在上述命令行中,我们也发现了--disable-infobars命令,用于启动 Chromium 网页浏览器并禁用副标题(也叫做信息栏)功能。该信息栏就是浏览器窗口顶部显示通知、警告或其他消息的小型信息栏,然而新版 chrome 目前已不再支持该命令了,这才是导致问题出现的真正原因。
三、解决问题
不希望看到"Chrome正受到自动测试软件的控制" 这个副标题,可以在启动 Chrome 时省略 --enable-automation 标志,或者将其设置为 --disable-automation。这里我们以省略 --enable-automation 做演示:

完整的 ui.go 代码,可以直接拿走,覆盖原来的代码即可使用,如下:
package lorca
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"reflect"
)
// UI interface allows talking to the HTML5 UI from Go.
type UI interface {
Load(url string) error
Bounds() (Bounds, error)
SetBounds(Bounds) error
Bind(name string, f interface{}) error
Eval(js string) Value
Done() <-chan struct{}
Close() error
}
type ui struct {
chrome *chrome
done chan struct{}
tmpDir string
}
var defaultChromeArgs = []string{
"--disable-background-networking",
"--disable-background-timer-throttling",
"--disable-backgrounding-occluded-windows",
"--disable-breakpad",
"--disable-client-side-phishing-detection",
"--disable-default-apps",
"--disable-dev-shm-usage",
"--disable-infobars",
"--disable-extensions",
"--disable-features=site-per-process",
"--disable-hang-monitor",
"--disable-ipc-flooding-protection",
"--disable-popup-blocking",
"--disable-prompt-on-repost",
"--disable-renderer-backgrounding",
"--disable-sync",
"--disable-translate",
"--disable-windows10-custom-titlebar",
"--metrics-recording-only",
"--no-first-run",
"--no-default-browser-check",
"--safebrowsing-disable-auto-update",
"--password-store=basic",
"--use-mock-keychain",
}
// New returns a new HTML5 UI for the given URL, user profile directory, window
// size and other options passed to the browser engine. If URL is an empty
// string - a blank page is displayed. If user profile directory is an empty
// string - a temporary directory is created and it will be removed on
// ui.Close(). You might want to use "--headless" custom CLI argument to test
// your UI code.
func New(url, dir string, width, height int, customArgs ...string) (UI, error) {
if url == "" {
url = "data:text/html,<html></html>"
}
tmpDir := ""
if dir == "" {
name, err := ioutil.TempDir("", "lorca")
if err != nil {
return nil, err
}
dir, tmpDir = name, name
}
args := append(defaultChromeArgs, fmt.Sprintf("--app=%s", url))
args = append(args, fmt.Sprintf("--user-data-dir=%s", dir))
args = append(args, fmt.Sprintf("--window-size=%d,%d", width, height))
args = append(args, customArgs...)
args = append(args, "--remote-debugging-port=0")
chrome, err := newChromeWithArgs(ChromeExecutable(), args...)
done := make(chan struct{})
if err != nil {
return nil, err
}
go func() {
chrome.cmd.Wait()
close(done)
}()
return &ui{chrome: chrome, done: done, tmpDir: tmpDir}, nil
}
func (u *ui) Done() <-chan struct{} {
return u.done
}
func (u *ui) Close() error {
// ignore err, as the chrome process might be already dead, when user close the window.
u.chrome.kill()
<-u.done
if u.tmpDir != "" {
if err := os.RemoveAll(u.tmpDir); err != nil {
return err
}
}
return nil
}
func (u *ui) Load(url string) error { return u.chrome.load(url) }
func (u *ui) Bind(name string, f interface{}) error {
v := reflect.ValueOf(f)
// f must be a function
if v.Kind() != reflect.Func {
return errors.New("only functions can be bound")
}
// f must return either value and error or just error
if n := v.Type().NumOut(); n > 2 {
return errors.New("function may only return a value or a value+error")
}
return u.chrome.bind(name, func(raw []json.RawMessage) (interface{}, error) {
if len(raw) != v.Type().NumIn() {
return nil, errors.New("function arguments mismatch")
}
args := []reflect.Value{}
for i := range raw {
arg := reflect.New(v.Type().In(i))
if err := json.Unmarshal(raw[i], arg.Interface()); err != nil {
return nil, err
}
args = append(args, arg.Elem())
}
errorType := reflect.TypeOf((*error)(nil)).Elem()
res := v.Call(args)
switch len(res) {
case 0:
// No results from the function, just return nil
return nil, nil
case 1:
// One result may be a value, or an error
if res[0].Type().Implements(errorType) {
if res[0].Interface() != nil {
return nil, res[0].Interface().(error)
}
return nil, nil
}
return res[0].Interface(), nil
case 2:
// Two results: first one is value, second is error
if !res[1].Type().Implements(errorType) {
return nil, errors.New("second return value must be an error")
}
if res[1].Interface() == nil {
return res[0].Interface(), nil
}
return res[0].Interface(), res[1].Interface().(error)
default:
return nil, errors.New("unexpected number of return values")
}
})
}
func (u *ui) Eval(js string) Value {
v, err := u.chrome.eval(js)
return value{err: err, raw: v}
}
func (u *ui) SetBounds(b Bounds) error {
return u.chrome.setBounds(b)
}
func (u *ui) Bounds() (Bounds, error) {
return u.chrome.bounds()
}
四、最终效果展示
-
示例一:

-
示例二:

-
示例三:

参考文章:
我的微信公众号 会飞的小猴子,等你来关注哦 ^ - ^
版权声明:本文为博主原创文章,如需转载,请给出:
原文链接:https://blog.csdn.net/qq_35844043/article/details/131612569

1万+

被折叠的 条评论
为什么被折叠?



