cefsharp执行dom的click()时,提示File chooser dialog can only be shown with a user activation解决方案
·
最近在所一款办公自动化的软件,需要可以静默上传文件,改造了cefsharp的dialoghanlder,代码如下
public bool OnFileDialog(IWebBrowser chromiumWebBrowser, IBrowser browser, CefFileDialogMode mode, CefFileDialogFlags flags, string title, string defaultFilePath, List<string> acceptFilters, int selectedAcceptFilter, IFileDialogCallback callback)
{
/*拦截后,可以指定要返回的值
if (!callback.IsDisposed)
{
using (callback)
{
List<string> filepath = new List<string> { };
filepath.Add(@"C:\测试.txt");
callback.Continue(1, filepath);
}
}
return true;*/
return false;
}
}
网页前端采用的element,上传用的el-upload,直接用Browser.ExecuteScriptAsync执行Javascript脚本对指定的上传按钮进行click(),
执行的JavaScript脚本如下
elementsShareFamily = function (primaryEl, siblingEl) {
if (primaryEl.tagName === siblingEl.tagName &&
(!primaryEl.className || primaryEl.className === siblingEl.className) &&
(!primaryEl.id || primaryEl.id === siblingEl.id)) {
return true;
}
return false;
};
getElementIndex = function (el) {
var className = el.className;
var id = el.id;
var index = 1; // XPath is one-indexed
var sib;
for (sib = el.previousSibling; sib; sib = sib.previousSibling) {
if (sib.nodeType === Node.ELEMENT_NODE && elementsShareFamily(el, sib)) {
index++;
}
}
if (index > 1) {
return index;
}
for (sib = el.nextSibling; sib; sib = sib.nextSibling) {
if (sib.nodeType === Node.ELEMENT_NODE && elementsShareFamily(el, sib)) {
return 1;
}
}
return 0;
};
//通过element获取xpath 方法2
getElementXpath2 = function (el, callback) {
var query = '';
for (; el && el.nodeType === Node.ELEMENT_NODE; el = el.parentNode) {
var component = el.tagName.toLowerCase();
var index = getElementIndex(el);
if (el.id) {
component += '[@id=\"' + el.id + '\"]';
} else if (el.className) {
component += '[@class=\"' + el.className + '\"]';
}
if (index >= 1) {
component += '[' + index + ']';
}
// If the last tag is an img, the user probably wants img/@src.
if (query === '' && el.tagName.toLowerCase() === 'img') {
component += '/@src';
}
query = '/' + component + query;
}
return query;
};
//获取xpath 方2
function getElementXpath(element,callback) {
if (element.id !== "" ) {
//判断id属性,如果这个元素有id,则显 示//*[@id="xPath"] 形式内容
return '//*[@id=\"' + element.id + '\"]';
}
//这里需要需要主要字符串转译问题,可参考js 动态生成html时字符串和变量转译(注意引号的作用)
if (element.tagName.toLowerCase() == "body") {//递归到body处,结束递归
return '/html/' + element.tagName.toLowerCase();
}
var ix = 1,//在nodelist中的位置,且每次点击初始化
siblings = element.parentNode.childNodes;//同级的子元素
for (var i = 0, l = siblings.length; i < l; i++) {
var sibling = siblings[i];
//如果这个元素是siblings数组中的元素,则执行递归操作
if (sibling == element) {
return arguments.callee(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + (ix) + ']';
//如果不符合,判断是否是element元素,并且是否是相同元素,如果是相同的就开始累加
} else if (sibling.nodeType == 1 && sibling.tagName == element.tagName) {
ix++;
}
}
}
//通过xpath获取element
function queryElementByXPath(e, path) {
var evaluator = new XPathEvaluator();
var result = evaluator.evaluate(path, e.documentElement, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
return result.singleNodeValue;
};
//获取页面当前活动的frame
function getFrame(frame,url,basejs)
{
var status=0;
var frames=frame.getElementsByTagName('iframe');
//先从本节点去找,本节点找不到,递归找子节点
for(var i=0;i<frames.length;i++)
{
if(frames[i].src==url||frames[i].contentDocument.location.href.split("?")[0]==url.split("?")[0])
{
status=1;
basejs+=".getElementsByTagName('iframe')["+i+"]";
break;
}
else
{
var tmp=basejs;
basejs+=".getElementsByTagName('iframe')["+i+"].contentDocument";
var re= getFrame(frames[i].contentDocument,url,basejs);
if(re.status==1)
{
status=re.status;
basejs=re.js;
break;
}
else
{
basejs=tmp;
}
}
}
return {status:status,js:basejs};
}
//执行js
function excuteJs(frameurl,xpath)
{
//获取当前frame的js脚本
var frameJs =getFrame(document,frameurl,"document");
var e=frameJs.js=='document'?eval(frameJs.js):eval(frameJs.js+'.contentDocument');
var ret= queryElementByXPath(e,xpath);
ret.click(); //执行dom元素的点击事件
return ret;
}
excuteJs("https://element.eleme.cn/#/zh-CN/component/upload",'//*[@id="app"]/div[2]/div[1]/div[1]/div[1]/div[1]/div[2]/section[1]/div[1]/div[1]/div[1]/div[1]/div[1]/button[1]/span[1]');
理论上所有逻辑都没有问题,但是在执行js脚本,无法打开选择文件对话框
在控制台可以看到“File chooser dialog can only be shown with a user activation”的警告文本
当鼠标点击住浏览器的任意网页空白处,则可以弹出对话框,
搜索全网,只有selenium的解决方案,但是不想再引入一套cef的driver,索性直接模拟浏览器点击事件解决。
方法如下:
在执行Browser.ExecuteScriptAsync 之前,增加模拟鼠标的点击事件
//浏览器先点击一下,为了保持当前浏览器为活动状态,否则部分由弹窗(附件上传)打不开对话框
Browser.GetBrowserHost().SendMouseClickEvent(1, 1, MouseButtonType.Left, false, 1, CefEventFlags.None);
至此,问题解决。
更多推荐


所有评论(0)