跨源 iframe 中的 JavaScript 对话框 alert()、confirm() 和 prompt() 不再起作用 [英] JavaScript dialogs alert(), confirm() and prompt() in cross origin iframe does not work any longer
问题描述
Apps 脚本网络应用程序在 中工作.Chrome似乎不再支持
alert()
、confirm()
,在web应用上推广这些功能.
有什么解决方法吗?
- Chrome 版本 92.0.4515.107(官方版本)(64 位) -- 不起作用
- Edge 版本 91.0.864.71(官方版本)(64 位) -- 有效
尝试将 alert()
替换为 window.alert()
,但仍然无效.
exec:1 一个不同的源子框架试图创建一个 JavaScript 对话框.这不再被允许并被阻止.有关详细信息,请参阅 https://www.chromestatus.com/feature/5148698084376576.>
Google 为跨源 iframe 删除 alert()、confirm() 和 prompt() 的决定是荒谬而主观的.他们称之为功能".理由很差——见动机";吼叫.删除如此重要功能的一个非常薄弱的理由!社区和开发者应该抗议!
问题
https://www.chromestatus.com/feature/5148698084376576
<块引用>功能:删除 alert()、confirm() 和跨源 iframe 提示
<块引用>
Chrome 允许 iframe 触发 Javascript 对话框,当 iframe 与顶部框架位于同一原点时,它会显示say ...",当 iframe 交叉时显示该页面上的嵌入页面说..."起源.当前的用户体验令人困惑,并且之前曾导致网站假装消息来自 Chrome 或其他网站的欺骗行为.取消对跨源 iframe 触发 UI 功能的支持将防止这种欺骗行为,并阻止进一步的 UI 简化.
<块引用>
动机
JS 对话框的当前 UI(通常,不仅仅是跨源子框架的情况)令人困惑,因为消息看起来像浏览器自己的 UI.这导致了欺骗(尤其是 window.prompt),其中站点假装特定消息来自 Chrome(例如 1,2,3).Chrome 通过在消息前加上say..."来缓解这些欺骗行为.然而,当这些警报来自跨域 iframe 时,UI 会更加混乱,因为 Chrome 试图解释对话框不是来自浏览器本身或顶级页面.鉴于跨源 iframe JS 对话框的使用率较低,事实上,当使用 JS 对话框时,站点的主要功能通常不需要它们,并且难以可靠地解释对话框的来源,我们建议删除 JS 对话框跨域 iframe.这也将取消阻止我们通过删除主机名指示并通过将对话框移动到内容区域的中心使对话框更明显地成为页面(而不是浏览器)的一部分来进一步简化对话框的能力.这些更改在移除对 JS 对话框的跨源支持时被阻止,否则这些子框架可能会假装它们的对话框来自父页面.
解决方案
通过 Window.postMessage() 从 iframe 向父级发送消息并通过父级页面显示对话框.这是谷歌上非常优雅的黑客和耻辱,因为在 Chrome 92 版客户端看到警报对话框之前,例如嵌入页面 iframe.com"说:...
(这是正确的 - 客户端看到调用警报的真实域)但现在使用 postMessage 解决方案客户端将看到一个谎言,例如页面example.com"说:...
但警报没有被 example.com 调用.愚蠢的谷歌决定导致他们达到相反的效果 - 客户现在会更加困惑.谷歌的决定是仓促的,他们没有考虑后果.在 prompt() 和 confirm() 的情况下,通过 Window.postMessage() 有点棘手,因为我们需要将结果从顶部发送回 iframe.
谷歌接下来会做什么?禁用 Window.postMessage()?似曾相识.我们又回到了 Internet Explorer 时代……开发人员通过进行愚蠢的黑客攻击来浪费时间.
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强>
<头><元字符集=utf-8"><title>父(域 A)</title><script type="text/javascript";src=dialogs.js"></script>头部><身体><h1>父(域A)</h1><iframe src="https://example-b.com/iframe.html"></html>
https://example-b.com/iframe.html强>
<头><元字符集=utf-8"><title>Iframe(域B)</title><script type="text/javascript";src=dialogs.js"></script>头部><身体><h1>Iframe(域B)</h1><script type="text/javascript">alert('alert() 从 iframe.html 转发');confirm('confirm() 通过回调从 iframe.html 转发', (result) => {console.log('confirm() result via callback: ', result);});prompt('prompt() 通过回调从 iframe.html 转发', null, (result) => {console.log('prompt() result via callback: ', result);});(异步() => {var result1 = await confirm('confirm() 通过承诺从 iframe.html 转发');console.log('confirm() result via promise:', result1);var result2 = await prompt('prompt() 通过 promise 从 iframe.html 转发');console.log('prompt() result via promise:', result2);})();</html>
dialogs.js
(function() {变量 ID = 1,商店 = {},isIframe = (window === window.parent || window.opener) ?假:真;//发信息var sendMessage = 函数(windowToSend,数据){windowToSend.postMessage(JSON.stringify(data), '*');};//覆盖confirm() 和prompt() 的助手var processInteractiveDialog = 函数(数据,回调){发送消息(父母,数据);如果(回调)store[data.id] = 回调;别的return new Promise(resolve => { store[data.id] = resolve; })};//覆盖原生对话框函数如果(isIframe){//警报()window.alert = 函数(消息){var data = { event : 'dialog', type : 'alert', message : message };发送消息(父母,数据);};//确认()window.confirm = 函数(消息,回调){var data = { event : 'dialog', type : 'confirm', id : id++, message : message };返回 processInteractiveDialog(data, callback);};//迅速的()window.prompt = 函数(消息,值,回调){var data = { event : 'dialog', type : 'prompt', id : id++, message : message, value : value ||'' };返回 processInteractiveDialog(data, callback);};}//监听消息window.addEventListener('message', function(event) {尝试 {var data = JSON.parse(event.data);}捕捉(错误){返回;}if (!data || typeof data !='object')返回;if (data.event !='dialog' || !data.type)返回;//从 iframe 到父级的初始消息如果(!isIframe){//警报()如果(数据类型=='警报')警报(数据.消息)//确认()否则如果(数据类型=='确认'){var data = { event : 'dialog', type : 'confirm', id : data.id, result : confirm(data.message) };发送消息(事件源,数据);}//迅速的()否则如果(数据类型=='提示'){var data = { event : 'dialog', type : 'prompt', id : data.id, result : prompt(data.message, data.value) };发送消息(事件源,数据);}}//从父级到 iframe 的响应消息别的 {//确认()如果(数据类型=='确认'){存储[data.id](data.result);删除商店[data.id];}//迅速的()否则如果(数据类型=='提示'){存储[data.id](data.result);删除商店[data.id];}}}, 错误的);})();
Apps script web app works in <iframe>
. It seems Chrome is no longer supporting alert()
, confirm()
, Promote these functions on the web app.
Any workaround to this?
- Chrome Version 92.0.4515.107 (Official Build) (64-bit) -- does not work
- Edge Version 91.0.864.71 (Official build) (64-bit) -- works
Tried replacing alert()
with window.alert()
, but still does not work.
exec:1 A different origin subframe tried to create a JavaScript dialog. This is no longer allowed and was blocked. See https://www.chromestatus.com/feature/5148698084376576 for more details.
This is absurd and subjective decision of Google to remove alert(), confirm(), and prompt() for cross origin iframes. And they called it "feature". And justification is very poor - see "motivation" bellow. A very weak reason for removing such an important feature! Community and developers should protest!
Problem
https://www.chromestatus.com/feature/5148698084376576
Feature: Remove alert(), confirm(), and prompt for cross origin iframes
Chrome allows iframes to trigger Javascript dialogs, it shows " says ..." when the iframe is the same origin as the top frame, and "An embedded page on this page says..." when the iframe is cross-origin. The current UX is confusing, and has previously led to spoofs where sites pretend the message comes from Chrome or a different website. Removing support for cross origin iframes’ ability to trigger the UI will prevent this kind of spoofing, and unblock further UI simplifications.
Motivation
The current UI for JS dialogs (in general, not just for the cross-origin subframe case) is confusing, because the message looks like the browser’s own UI. This has led to spoofs (particularly with window.prompt) where sites pretend that a particular message is coming from Chrome (e.g. 1,2,3). Chrome mitigates these spoofs by prefacing the message with " says...". However, when these alerts are coming from a cross-origin iframe, the UI is even more confusing because Chrome tries to explain the dialog is not coming from the browser itself or the top level page. Given the low usage of cross-origin iframe JS dialogs, the fact that when JS dialogs are used they are generally not required for the site’s primary functionality, and the difficulty in explaining reliably where the dialog is coming from, we propose removing JS dialogs for cross-origin iframes. This will also unblock our ability to further simplify the dialog by removing the hostname indication and making the dialog more obviously a part of the page (and not the browser) by moving it to the center of the content area. These changes are blocked on removing cross-origin support for JS dialogs, since otherwise these subframes could pretend their dialog is coming from the parent page.
Solution
Send message via Window.postMessage() from iframe to parent and show dialog via parent page. It is very elegant hack and shame on Google because before Chrome version 92 client saw alert dialog e.g. An embedded page iframe.com" says: ...
(which was correct - client see real domain which invoked alert) but now with postMessage solution client will see a lie e.g. The page example.com" says: ...
but alert was not invoked by example.com. Stupid google decision caused them to achieve the opposite effect - client will be much more confused now. Google's decision was hasty and they didn't think about the consequences. In case of prompt() and confirm() it is a little bit tricky via Window.postMessage() because we need to send result from top back to iframe.
What Google will do next? Disable Window.postMessage()? Déjà vu. We are back in Internet Explorer era... developers waste time by doing stupid hacks.
TL;DR: Demo
https://domain-a.netlify.app/parent.html
Code
With code bellow you can use overridden native alert(), confirm() and prompt() in cross origin iframe with minimum code change. There is no change for alert() usage. I case of confirm() and prompt() just add "await" keyword before it or feel free to use callback way in case you can not switch easily your sync functions to async functions. See all usage examples in iframe.html bellow.
Everything bad comes with something good - now I gained with this solution an advantage that iframe domain is not revealed (domain from address bar is now used in dialogs).
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>
dialogs.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);
})();
这篇关于跨源 iframe 中的 JavaScript 对话框 alert()、confirm() 和 prompt() 不再起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!