У меня есть iframe с атрибутом src attr, установленным на https://example.com/path . Этот URL-адрес перенаправляет 301 на https://othersite.com/path . Эта страница загружает некоторый JS и создает BroadcastChannel с прослушивателем. Затем откроется новое окно https://othersite.com/path2. На этой странице загружается некоторый JS, который создает BroadcastChannel с тем же именем и выполняет postMessage. Проблема: он никогда не попадает в iframe. Странно то, что это работает, если источник iframe не является перенаправлением. Но это не имеет смысла, поскольку, несмотря на перенаправление, источник определяется страницей, которая в конечном итоге загружается. Итак, происхождение одно и то же (да, я это проверил), и имена BroadcastChannel одинаковы. Так почему это не работает??
Обновлено: я нашел сценарий воспроизведения:
Хорошо, у меня есть сценарий воспроизведения. Запустите этот код на своем компьютере (сначала установите экспресс npm init
и npm i express
). Затем добавьте следующие хосты в свой /etc/hosts
(домены важны, кажется, это работает, когда они являются поддоменами одного и того же домена): 127.0.0.1 api.rubbish.wow accounts.rubbish.wow google.wow my.rubbish.wow my.site.wow
и запустите это:
const express = require('express');
// Server 1 (Port 4000)
const server1 = express();
server1.get('/', (req, res) => {
res.send(`
<html>
<body>
<iframe src = "http://api.rubbish.wow:4001" width = "600" height = "400"></iframe>
</body>
</html>
`);
});
server1.listen(4000, () => {
console.info('Server 1 listening on port 4000');
});
// Server 2 (Port 4001)
const server2 = express();
server2.get('/', (req, res) => {
res.redirect(301, 'http://accounts.rubbish.wow:4002/iframe-content');
});
server2.listen(4001, () => {
console.info('Server 2 listening on port 4001');
});
// Server 3 (Port 4002)
const server3 = express();
server3.get('/iframe-content', (req, res) => {
res.send(`
<html>
<body>
<h1>Iframe Content</h1>
<div id = "messages"></div>
<button onclick = "openNewWindow()">Open New Window</button>
<script>
function openNewWindow() {
window.open('http://google.wow:4003');
}
const channel = new BroadcastChannel('auth');
channel.onmessage = (event) => {
const messageDiv = document.getElementById('messages');
const newMessage = document.createElement('div');
newMessage.textContent = 'Received message: ' + event.data;
messageDiv.appendChild(newMessage);
};
</script>
</body>
</html>
`);
});
server3.get('/new-window', (req, res) => {
res.send(`
<html>
<body>
<h1>New Window</h1>
<button id = "sendMessageButton">Send Message</button>
<script>
const channel = new BroadcastChannel('auth');
document.getElementById('sendMessageButton').addEventListener('click', () => {
channel.postMessage('Authentication successful');
});
</script>
</body>
</html>
`);
});
server3.listen(4002, () => {
console.info('Server 3 listening on port 4002');
});
// Server 4 (Port 4003)
const server4 = express();
server4.get('/', (req, res) => {
res.redirect('http://accounts.rubbish.wow:4002/new-window');
});
server4.listen(4003, () => {
console.info('Server 4 listening on port 4003');
});
Откройте http://my.site.wow:4000 в браузере откройте окно и попробуйте отправить сообщение -> FAIL Откройте http://my.rubbish.wow:4000 в браузере откройте окно и попробуйте отправить сообщение -> УСПЕХ
Поэтому, когда источник iframe и источник страницы верхнего уровня являются поддоменами одного и того же домена (rubbish.wow), это работает, в противном случае - нет, и я понятия не имею, почему бы и нет.
Итак, я только что протестировал на своей стороне, и он работает так, как ожидалось, когда iframe не изолирован в песочнице и заголовки COXP не отправляются. Поделитесь, пожалуйста, более подробной информацией о вашей фактической настройке.
@Kaiido, новое окно содержит поток Google OAuth2, в котором пользователь входит в систему и дает согласие на использование моего сервиса. Redirect_uri для этого потока перенаправляет на othersite.com/path2 . У iframe нет атрибута sandbox
, и окно открывается с помощью window.open
. Я попробую сделать репродукцию.
@Kaiido, что такое заголовки COXP? Никогда раньше не слышал и не видел этого термина.
@Kaiido Я добавил сценарий воспроизведения к исходному вопросу
Под COXP я имел в виду все заголовки COOP, CORP, COEP. Так что я действительно могу воспроизвести в каждом браузере то, что вы испытываете, поэтому это должно быть «согласно спецификациям». Однако я должен признать, что я в полной растерянности. Для меня это просто не имеет смысла... Как я и думал, вам даже не нужен BroadcastChannel, потому что всплывающее окно и iframe имеют одинаковое происхождение и могут напрямую взаимодействовать друг с другом. opener.document.getElementById("messages").append("success");
из всплывающего окна напечатает «успех» в iframe. Так что я правда не понимаю, почему БК не совпадают... Поспрошу.
Причина, по которой это происходит, заключается в том, что BroadcastChannel
разделен сайтом документа верхнего уровня (через который внедрен ваш документ). Во всплывающем документе он не разбит на разделы.
Хотя это еще предстоит должным образом стандартизировать, в большинстве реализаций это уже реализовано таким образом.
Ты лучший. Это невозможно было отладить и найти причину.
Я бы подумал, что это действительно должно было сработать... Несколько вопросов: есть ли у iframe атрибут
sandbox
? Как именно открывается новая страницаpath2
? Судя по этому сценарию, вам даже не понадобится BroadcastChannel, поскольку и iframe, и открытое окно должны иметь возможность взаимодействовать напрямую.