Почему я не могу доставить это сообщение всем фреймам, работающим на вкладке?

Тема

Я создаю расширение 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 получил мое сообщение


person Luke    schedule 07.02.2015    source источник
comment
Невозможно воспроизвести в Chromium 40.0.2214.91 даже после исправления синтаксической ошибки в вашем коде и добавления "permissions":["contextMenus"] и "all_frames":true. Пожалуйста, попробуйте свой код перед тем, как опубликовать вопрос, чтобы убедиться, что код действительно представляет вашу проблему.   -  person Rob W    schedule 07.02.2015
comment
@RobW извините, я исправил ошибки, но что происходит в вашей системе? по моему, поведение такое, как я описал. Когда страница загружается, вы должны получать оповещения с: youtube.com, accounts.google.com, plus.google.com и, возможно, некоторых других. Затем, когда вы нажмете эту опцию, вы должны заметить, что только один из фреймов отвечает на сообщение.   -  person Luke    schedule 07.02.2015
comment
Здесь я вижу несколько предупреждений, когда захожу на youtube.com и выбираю пункт контекстного меню в окне поиска YouTube. Какую версию Chrome вы используете?   -  person Rob W    schedule 07.02.2015
comment
@RobW Version 40.0.2214.94 (64-bit) Я собираюсь закрыть экран и вставить видео   -  person Luke    schedule 07.02.2015
comment
@RobW убедитесь, что кадры, загружаемые при нажатии на контекстное сообщение, говорят, что вы пытались ... а я ... а не только кадры, которые загружаются с опозданием (скажем, привет, я ... )   -  person Luke    schedule 07.02.2015
comment
Я получаю 2 "Я" и 2 "Вы" на домашней странице YTs в совершенно новом профиле Chrome, в котором нет ничего, кроме рассматриваемого расширения.   -  person Rob W    schedule 07.02.2015
comment
Позвольте нам продолжить это обсуждение в чате.   -  person Luke    schedule 07.02.2015


Ответы (1)


Ваш chrome.runtime.onMessage прослушиватель событий вызывает alert. Этот метод блокирует время выполнения до закрытия диалогового окна. По какой-то причине это также мешает Chrome правильно запускать события onMessage в других фреймах (отображается как crbug.com/456482).

Обходной путь - использовать console.log для отладки вместо alert или заключить код в setTimeout для асинхронной обработки события onMessage. Например.

chrome.runtime.onMessage.addListener(function(message) {
    setTimeout(function() {
        alert('Message: ' + message);
    });
});
person Rob W    schedule 07.02.2015