Тема
Я создаю расширение Chrome, которое требует от меня отправки сообщения со страницы моего события в сценарий содержимого, который выполняется в определенном кадре.
Операция передачи сообщений вызывается при щелчке по опции контекстного меню. Указанный вариант доступен только в том случае, если элемент в фокусе отнесен к категории editable
в chrome.contextMenus
API. Мне нужно отправить его в самый внутренний фрейм, в котором содержится элемент. (чтобы я мог изменить его содержимое, обратившись к объекту document.activeElement
)
Начиная с Chrome 41, вы можете отправить сообщение в определенный фрейм, заполнив значение параметра frameID
аргумента options
в _ 6_.
Однако Chrome 41 в настоящее время находится в стадии бета-тестирования, поэтому я, очевидно, не могу ожидать, что эта функция будет совместима с браузерами моих пользователей.
Итак, я планировал отправить сообщение всем фреймам на вкладке (путем регистрации слушателей в сценарии содержимого, у которого был all_frames
задано значение true). Оказавшись внутри сценария содержимого, я мог проверить, правильный ли это кадр или нет, сравнив значение window.location.href
со значением, которое я ожидал.
Проблема
Похоже, что у меня было неправильное представление о том, как работают либо прослушиватели событий, либо передача сообщений. В документации довольно ясно сказано, что chrome.tabs.sendMessage()
должен отправлять сообщение в каждый кадр на вкладке, но похоже, что для каждого сообщения может быть вызван только один chrome.runtime.onMessage()
прослушиватель событий, даже если зарегистрировано много прослушивателей. Какой из них на самом деле вызывается, кажется, каким-то образом продиктовано начальной загрузкой страницы.
Это можно продемонстрировать с помощью упрощенной версии моего расширения:
manifest.json:
{
"manifest_version":2,
"name":"Demo",
"description":"This is a demonstration",
"version":"1.0",
"permissions":[
"<all_urls>",
"contextMenus"
],
"background": {
"page":"background.html",
"persistent":false
},
"content_scripts":[{
"matches":["<all_urls>"],
"js":["content.js"],
"all_frames":true
}]
}
background.html:
<!DOCTYPE text/html>
<html>
<head>
<textarea id="temp"></textarea>
<script src="event.js"></script>
</head>
<body>
</body>
</html>
event.js:
chrome.contextMenus.onClicked.addListener(requestHandler);
chrome.contextMenus.create({
"title":"Click Me!!",
"contexts":["editable"],
"id":"1"
});
function requestHandler(info, tab)
{
chrome.tabs.sendMessage(tab.id, {"destination":info.frameUrl});
//this should get sent to every frame in the tab
}
content.js:
chrome.runtime.onMessage.addListener(handleRequest);
alert("Hi, i'm: " + window.location.host);
function handleRequest(message)
{
alert("You tried to send a message to: " + message.destination);
alert("I am: " + window.location.href);
}
Если вы посещаете страницу с большим количеством фреймов, например youtube, вы должны заметить следующее поведение:
Вы получаете много предупреждений при начальной загрузке страницы, указывающих на то, что загружается много фреймов (все со слушателями)
Когда вы нажимаете опцию контекстного меню, появляется только одно предупреждение, указывающее, что только один кадр получил сообщение
Фрейм, который получает сообщение, остается постоянным при нескольких щелчках контекстной опции, но, вероятно, изменится при обновлении страницы. Это указывает на то, что порядок, в котором фреймы изначально загружаются, определяет, какой фрейм получит ваше сообщение.
Итак, как я могу гарантировать, что фрейм, с которым я пытаюсь поговорить, действительно получит мое сообщение, не прибегая к опции frameID
, доступной только в chrome 41?
Сначала я подумал, что могу отправить ответ из сценария содержимого, указав, попало ли сообщение в нужное место. Я мог бы просто продолжать делать это в цикле while на странице события, пока я не верну сообщение, предлагающее мне остановиться, но, как я сказал ранее, сообщение всегда, кажется, переходит в тот же фрейм, пока страница не обновится, поэтому этот подход ничего не делает для меня.
дополнительные детали
Еще я заметил, что если на странице есть два фрейма с одинаковым происхождением, например, plus.google.com, оба фрейма будут отвечать на сообщение, если это набор фреймов, в котором сообщение было доставлено. к. Таким образом, получатель, похоже, основан на происхождении, а не на каком-то уникальном идентификаторе кадра.
Вот ссылка на снятое мной видео о явлениях, о которых я говорю:
Обратите внимание, что изначально я получал оповещения от: youtube.com, accounts.google.com, plus.google.com и client6.google.com, но только client6 получил мое сообщение
"permissions":["contextMenus"]
и"all_frames":true
. Пожалуйста, попробуйте свой код перед тем, как опубликовать вопрос, чтобы убедиться, что код действительно представляет вашу проблему. - person Rob W   schedule 07.02.2015