跨源 iframe 中的 JavaScript 对话框 alert()、confirm() 和 prompt() 不再起



应用程序脚本Web应用程序在<iframe>中工作。看来Chrome已经不再支持alert()confirm(),在Web应用上推广这些功能。

有什么解决方法吗?

  • Chrome 版本 92.0.4515.107(官方版本)(64 位) - 不起作用
  • 边缘版本 91.0.864.71(官方版本)(64 位) -- 工作

尝试用window.alert()替换alert(),但仍然不起作用。

exec:1 一个不同的原点子帧试图创建一个 JavaScript 对话框。这不再被允许,并且已被阻止。有关更多详细信息,请参阅 https://www.chromestatus.com/feature/5148698084376576。

这是谷歌删除跨源iframe的alert(),confirm()和prompt()的荒谬和主观决定。他们称之为">功能"。而且理由很差——见下面的"动机"。删除如此重要功能的一个非常弱的理由!社区和开发者应该抗议!

问题

https://www.chromestatus.com/feature/5148698084376576

功能:删除警报(),确认(),并提示跨源iframe。

Chrome允许iframe触发Javascript对话框,它显示" 说..."当 iframe 与顶部框架的原点相同,并且"此页面上的嵌入页面显示..."当 iframe 是跨源的时。当前的用户体验令人困惑,并且以前曾导致恶搞,网站假装消息来自Chrome或其他网站。删除对跨源 iframe 触发 UI 的功能的支持将防止此类欺骗,并取消阻止进一步的 UI 简化。

赋予动机

JS对话框的当前UI(通常,不仅仅是跨源子帧大小写)令人困惑,因为消息看起来像浏览器自己的UI。这导致了欺骗(特别是使用window.prompt),其中网站假装特定消息来自Chrome(例如1,2,3)。Chrome 通过在消息前面加上"说..."来缓解这些欺骗行为。但是,当这些警报来自跨源 iframe 时,界面更加混乱,因为 Chrome 试图解释对话框不是来自浏览器本身或顶级页面。鉴于跨源 iframe JS 对话框的使用率较低,当使用 JS 对话框时,站点的主要功能通常不需要它们,并且难以可靠地解释对话框的来源,我们建议删除跨源 iframe 的 JS 对话框。这也将取消我们进一步简化对话框的能力,方法是删除主机名指示,并通过将其移动到内容区域的中心,使对话框更明显地成为页面(而不是浏览器)的一部分。这些更改在删除对 JS 对话框的跨源支持时被阻止,因为否则这些子框架可能会假装其对话框来自父页面。

溶液

通过Window.postMessage()从 iframe 向父页面发送消息,并通过父页面显示对话框。这是谷歌上非常优雅的黑客和耻辱,因为在Chrome版本92之前,客户端看到了警报对话框,例如An embedded page iframe.com" says: ...(这是正确的 - 客户端看到调用警报的真实域),但现在使用 postMessage 解决方案客户端将看到谎言,例如The page example.com" says: ...但 example.com 没有援引警报。愚蠢的谷歌决定导致他们达到相反的效果 - 客户现在会更加困惑。谷歌的决定是仓促的,他们没有考虑后果。在使用 prompt() 和 confirm() 的情况下,通过 Window.postMessage() 有点棘手,因为我们需要将结果从顶部发送回 iframe。

谷歌下一步会做什么?禁用 Window.postMessage()?似曾相识。我们又回到了IE时代...开发人员通过做愚蠢的黑客来浪费时间。

TL;DR:演示

https://domain-a.netlify.app/parent.html

法典

使用下面的代码,您可以在跨源 iframe 中使用覆盖的本机 alert()、confirm() 和 prompt(),只需最少的代码更改。alert() 用法没有变化。我在 confirm() 和 prompt() 的情况下只是在它之前添加"await"关键字,或者随意使用回调方式,以防您无法轻松地将同步函数切换到异步函数。查看 iframe 中的所有使用示例.html下面。

所有坏事都伴随着好东西 - 现在我通过这个解决方案获得了一个优势,即 iframe 域不会被显示(地址栏中的域现在用于对话框中)。

https://example-a.com/parent.html

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Parent (domain A)</title>
<script type="text/javascript" src="dialogs.js"></script>
</head>
<body>
<h1>Parent (domain A)</h1>
<iframe src="https://example-b.com/iframe.html">
</body>
</html>

https://example-b.com/iframe.html

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Iframe (domain B)</title>
<script type="text/javascript" src="dialogs.js"></script>
</head>
<body>
<h1>Iframe (domain B)</h1>
<script type="text/javascript">
alert('alert() forwarded from iframe.html');

confirm('confirm() forwarded from iframe.html via callback', (result) => {
console.log('confirm() result via callback: ', result);
});
prompt('prompt() forwarded from iframe.html via callback', null, (result) => {
console.log('prompt() result via callback: ', result);
});

(async () => {
var result1 = await confirm('confirm() forwarded from iframe.html via promise');
console.log('confirm() result via promise: ', result1);
var result2 = await prompt('prompt() forwarded from iframe.html via promise');
console.log('prompt() result via promise: ', result2);
})();
</script>
</body>
</html>

对话框.js

(function() {
var id = 1,
store = {},
isIframe = (window === window.parent || window.opener) ? false : true;
// Send message
var sendMessage = function(windowToSend, data) {
windowToSend.postMessage(JSON.stringify(data), '*');
};
// Helper for overridden confirm() and prompt()
var processInteractiveDialog = function(data, callback) {
sendMessage(parent, data);
if (callback)
store[data.id] = callback;
else
return new Promise(resolve => { store[data.id] = resolve; })
};
// Override native dialog functions
if (isIframe) {
// alert()
window.alert = function(message) {
var data = { event : 'dialog', type : 'alert', message : message };
sendMessage(parent, data);
};
// confirm()
window.confirm = function(message, callback) {
var data = { event : 'dialog', type : 'confirm', id : id++, message : message };
return processInteractiveDialog(data, callback);
};
// prompt()
window.prompt = function(message, value, callback) {
var data = { event : 'dialog', type : 'prompt', id : id++, message : message, value : value || '' };
return processInteractiveDialog(data, callback);
};
}
// Listen to messages
window.addEventListener('message', function(event) {
try {
var data = JSON.parse(event.data);
}
catch (error) {
return;
}
if (!data || typeof data != 'object')
return;
if (data.event != 'dialog' || !data.type)
return;
// Initial message from iframe to parent
if (!isIframe) {
// alert()
if (data.type == 'alert')
alert(data.message)
// confirm()
else if (data.type == 'confirm') {
var data = { event : 'dialog', type : 'confirm', id : data.id, result : confirm(data.message) };
sendMessage(event.source, data);
}
// prompt()
else if (data.type == 'prompt') {
var data = { event : 'dialog', type : 'prompt', id : data.id, result : prompt(data.message, data.value) };
sendMessage(event.source, data);
}
}
// Response message from parent to iframe
else {
// confirm()
if (data.type == 'confirm') {
store[data.id](data.result);
delete store[data.id];
}
// prompt()
else if (data.type == 'prompt') {
store[data.id](data.result);
delete store[data.id];
}
}
}, false);
})();

File a feature request:

请考虑使用此问题跟踪器模板提交功能请求。

我要么要求对应用程序脚本网络应用程序进行例外,要么添加用于警报和确认的内置方法,类似于现有的警报和提示对话框,这些对话框目前适用于Google编辑器。

Bug filed:

顺便说一下,此问题跟踪器中已报告此行为(作为错误):

  • html文件中的Javascript不起作用

我会考虑主演它以跟踪它。

解决方法:

同时,正如其他人所说,考虑降级或更改浏览器,或使用以下命令行标志执行它:

--

disable-features="SuppressDifferentOriginSubframeJSDialogs">

  • Chrome SuppressDifferentOriginSubframeJSDialogs settings override using JS?

到目前为止,唯一的"解决方案"是将以下内容添加到Chrome/Edge浏览器快捷方式中:

--disable-features="SuppressDifferentOriginSubframeJSDialogs"

或者降级您的浏览器。显然,这些都不是理想的。谷歌非常努力地将我们从自己手中拯救出来。

最新更新