文件选取器边栏通信错误
File Picker Sidebar Communication Errors
我正在开发一个独立的脚本,该脚本最终将作为附加组件发布。它将有一个侧边栏用户与之交互,他们可以从那里启动 Google 文件选择器来上传报告以供处理。我以前一直在寻找一种方法让侧边栏知道文件选择器已完成。我成功地复制了 this 答案,并获得了文件选择器对话框,将一条消息发送回侧边栏,表明它已完成。但是,控制台充满了一些错误和警告,我想知道我是否应该关注这些错误和警告。错误是:
Unsafe attempt to initiate navigation for frame with origin
'https://docs.google.com' from frame with URL
'https://***.googleusercontent.com/userCodeAppPanel'.
The frame attempting navigation of the top-level window is sandboxed,
but the flag of 'allow-top-navigation' or
'allow-top-navigation-by-user-activation' is not set.
DOMException: Blocked a frame with origin
"https://###.googleusercontent.com"
from accessing a cross-origin frame.
at findSideBar (https://###.googleusercontent.com/userCodeAppPanel:80:18)
at w0.pickerCallback [as Fb] (https://###.googleusercontent.com/userCodeAppPanel:98:11)
at w0._.h.iV (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:740:393)
at d (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:215:143)
at b (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:210:1)
Refused to get unsafe header "X-HTTP-Status-Code-Override"
我以前见过第一个错误,但是,第二个是新错误,与将消息从文件选择器对话框发送回侧边栏有关。
值得一提的是,一切仍然有效。我的问题主要是这些错误是我应该担心的,在发布前审核附加组件时它们是否会引起问题,我该如何更正它们?
我在下面包含了用于侧边栏创建、选择器创建的代码,以及用于将消息从选择器发送到侧边栏的相关代码。
边栏创建:
function buildSidebar(type) {
hideColumns();
hideRows();
hideSheets();
if(type == 'setup' || checkSetup()) {
var html = HtmlService.createTemplateFromFile('SidebarSetupHTML')
.evaluate()
.setTitle('Test');
} else {
var html = HtmlService.createTemplateFromFile('SidebarMainHTML')
.evaluate()
.setTitle('Test');
}
SpreadsheetApp.getUi().showSidebar(html);
}
选择器创建:
function showPicker() {
var html = HtmlService.createHtmlOutputFromFile('PickerHTML.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(html, 'Select Report(s)');
}
选择器消息代码:
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
(function findSideBar(limit) {
let f = window.top.frames;
for (let i = 0; i < limit; ++i) {
try {
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
}
} catch (e) {
console.error(e);
continue;
}
}
})(10);
}
}
边栏启动选择器并接收消息:
function testPicker() {
google.script.run.withSuccessHandler(pickerResponse).showPicker();
}
function pickerResponse(e) {
(async () => {
let receiver = new Promise((res, rej) => {
window.modalDone = res;
});
var message = await receiver;
console.log('message received');
if(message == 'PICKED' || message == "NOT_PICKED") {
console.log(message);
}
//Do what you want here
})();
}
当我看到你的脚本时,我认为你的问题的原因可能是 if 语句 true
之后的循环。因此,例如,当 if 语句是 true
时,将 break
放在最后一行怎么样?那么,下面的修改呢?
发件人:
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
}
收件人:
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
break; // <--- Added
}
注:
当我在 Google 文档(例如 Google 电子表格)上测试自定义对话框的以下示例脚本时,
<script>
google.script.host.close();
alert("ok");
</script>
我确认警报 window 已打开,并且对话框已关闭。所以我认为 google.script.host.close()
可能适用于异步进程。
例如,当google.script.run
的处理成本较高时,如下使用,
<script>
google.script.host.close();
google.script.run.withSuccessHandler(_ => alert("ok2")).myFunction();
alert("ok1");
</script>
似乎 myFunction()
和 alert("ok2")
不是 运行 因为对话框在 运行 myFunction()
之前关闭,而 alert("ok1")
是运行。另一方面,当进程成本低时,google.script.host.close()
之后的脚本似乎是运行。
鉴于这种情况,作为尝试,我提议在OP的问题google.script.host.close()
之后添加break
。
参考:
我正在开发一个独立的脚本,该脚本最终将作为附加组件发布。它将有一个侧边栏用户与之交互,他们可以从那里启动 Google 文件选择器来上传报告以供处理。我以前一直在寻找一种方法让侧边栏知道文件选择器已完成。我成功地复制了 this 答案,并获得了文件选择器对话框,将一条消息发送回侧边栏,表明它已完成。但是,控制台充满了一些错误和警告,我想知道我是否应该关注这些错误和警告。错误是:
Unsafe attempt to initiate navigation for frame with origin 'https://docs.google.com' from frame with URL 'https://***.googleusercontent.com/userCodeAppPanel'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.
DOMException: Blocked a frame with origin "https://###.googleusercontent.com" from accessing a cross-origin frame. at findSideBar (https://###.googleusercontent.com/userCodeAppPanel:80:18) at w0.pickerCallback [as Fb] (https://###.googleusercontent.com/userCodeAppPanel:98:11) at w0._.h.iV (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:740:393) at d (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:215:143) at b (https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en.uAzDleg2hnU.O/m=picker/rt=j/sv=1/d=1/ed=1/am=AQ/rs=AGLTcCMT6b3QcRI88QolvkcdUjC8YnoTvA/cb=gapi.loaded_0:210:1)
Refused to get unsafe header "X-HTTP-Status-Code-Override"
我以前见过第一个错误,但是,第二个是新错误,与将消息从文件选择器对话框发送回侧边栏有关。
值得一提的是,一切仍然有效。我的问题主要是这些错误是我应该担心的,在发布前审核附加组件时它们是否会引起问题,我该如何更正它们?
我在下面包含了用于侧边栏创建、选择器创建的代码,以及用于将消息从选择器发送到侧边栏的相关代码。
边栏创建:
function buildSidebar(type) {
hideColumns();
hideRows();
hideSheets();
if(type == 'setup' || checkSetup()) {
var html = HtmlService.createTemplateFromFile('SidebarSetupHTML')
.evaluate()
.setTitle('Test');
} else {
var html = HtmlService.createTemplateFromFile('SidebarMainHTML')
.evaluate()
.setTitle('Test');
}
SpreadsheetApp.getUi().showSidebar(html);
}
选择器创建:
function showPicker() {
var html = HtmlService.createHtmlOutputFromFile('PickerHTML.html')
.setWidth(600)
.setHeight(425)
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi().showModalDialog(html, 'Select Report(s)');
}
选择器消息代码:
function pickerCallback(data) {
var action = data[google.picker.Response.ACTION];
if (action == google.picker.Action.PICKED) {
(function findSideBar(limit) {
let f = window.top.frames;
for (let i = 0; i < limit; ++i) {
try {
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
}
} catch (e) {
console.error(e);
continue;
}
}
})(10);
}
}
边栏启动选择器并接收消息:
function testPicker() {
google.script.run.withSuccessHandler(pickerResponse).showPicker();
}
function pickerResponse(e) {
(async () => {
let receiver = new Promise((res, rej) => {
window.modalDone = res;
});
var message = await receiver;
console.log('message received');
if(message == 'PICKED' || message == "NOT_PICKED") {
console.log(message);
}
//Do what you want here
})();
}
当我看到你的脚本时,我认为你的问题的原因可能是 if 语句 true
之后的循环。因此,例如,当 if 语句是 true
时,将 break
放在最后一行怎么样?那么,下面的修改呢?
发件人:
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
}
收件人:
if (
f[i] /*/iframedAppPanel*/ &&
f[i].length &&
f[i][0] && //#sandboxFrame
f[i][0][0] && //#userHtmlFrame
window !== f[i][0][0] //!== self
) {
console.info('Sidebar found');
var sidebar = f[i][0][0];
sidebar.modalDone('PICKED'); //Modal has finished
console.log('Message sent');
google.script.host.close();
break; // <--- Added
}
注:
当我在 Google 文档(例如 Google 电子表格)上测试自定义对话框的以下示例脚本时,
<script>
google.script.host.close();
alert("ok");
</script>
我确认警报 window 已打开,并且对话框已关闭。所以我认为 google.script.host.close()
可能适用于异步进程。
例如,当google.script.run
的处理成本较高时,如下使用,
<script>
google.script.host.close();
google.script.run.withSuccessHandler(_ => alert("ok2")).myFunction();
alert("ok1");
</script>
似乎 myFunction()
和 alert("ok2")
不是 运行 因为对话框在 运行 myFunction()
之前关闭,而 alert("ok1")
是运行。另一方面,当进程成本低时,google.script.host.close()
之后的脚本似乎是运行。
鉴于这种情况,作为尝试,我提议在OP的问题google.script.host.close()
之后添加break
。