本文介绍了跨源 iframe 中的 JavaScript 对话框 alert()、confirm() 和 prompt() 不再起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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.

解决方案

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

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() 不再起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-29 07:41