Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   chrome.tabs.sendMessage (https://javascript.ru/forum/misc/65784-chrome-tabs-sendmessage.html)

Артист 07.11.2016 20:34

chrome.tabs.sendMessage
 
Подскажите пожалуйста, как мне отправить сообщение из 2го в 1й скрипт?

Смотрел вот тут описание, но без примера я ничего не понимаю...

Есть 2 скрипта, 1й(background.js) висит в приложении постоянно, 2й(maps.js) подключается только к определённым страницам.

В background.js
chrome.runtime.onMessage.addListener // Отлов сообщений
(
	function(request, sender, sendResponse)
	{
		switch(request.id) // Это типа идентификатор сообщений
		{
			case 2: // GET запрос
			{
				GetHttpRequest(request.href, {rid: request.rid})
				break
			}
		}
	}
)

Это дополнительная информация из background.js
function GetHttpRequest(href, arg) // Запрос
{
	var xhr = new XMLHttpRequest()
	xhr.withCredentials = true
	xhr.onreadystatechange = function(e)
	{
		e.data = arg
		ProcessRequestChange.apply(this, arguments)
	}
	xhr.open('GET', href, true)
	xhr.send(null)
}
function ProcessRequestChange(e) // Обработчик запроса
{
	var xhr = this

	if(xhr.readyState != 4) return
	if(xhr.status == 200)
	{
		var html = xhr.responseText
		
		switch(e.data.rid) // Это типа идентификатор запроса
		{
			case 3: // Покопаться в ответе на 'http://www.site.ru/bla-bla.php'
			{
				// Некий код который записывает необходимую информацию в str убрал для экономии
				var str = 'ответ' // Пусть будет это ответом
				
				chrome.tabs.query // Вроде это какое - то событие открытия(или получения) вкладок?
				(
					{active: true, currentWindow: true}, // Х.з
					function(tabs)
					{
						chrome.tabs.sendMessage // Это то, что отправляет ответное сообщение
						(
							tabs[0].id, // Х.з, наверное идентификатор вкладки, куда отправить?
							{greeting: "hello"}, // Это я так понял и есть ответ?
							function(response) // А это вообще не нужно вроде...
							{
								console.log(response.farewell);
							}
						)
					}
				)
				break
			}
		}
	}
}

В maps.js:
chrome.runtime.sendMessage // Отправить сообщение в background.js
(
	{id: 2, href: 'http://www.site.ru/bla-bla.php', rid: 3}, // Сделать запрос на адрес
	function(response) // Функция ответа не может быть вызвана, т.к. ответ не отправляется из background.js
	{
		document.querySelector('#fades').style.display = 'inline'
		document.querySelector('#guild').style.display = 'inline'
		document.querySelector('#mercenary').innerHTML = 'jhg'
	}
)


Так вот, вся проблема заключается в том, что в background.js сначала нужно послать запрос, получить ответ, покопаться в ответе, и только потом отправить ответ на сообщение в maps.js.

Aetae 07.11.2016 20:42

*!*
-                GetHttpRequest(request.href, {rid: request.rid})
*/!*
+                GetHttpRequest(request.href, {rid: request.rid, response: sendResponse});

*!*
-                chrome.tabs.query // Вроде это какое - то событие открытия(или получения) вкладок?
                (
                    {active: true, currentWindow: true}, // Х.з
                    function(tabs)
                    {
                        chrome.tabs.sendMessage // Это то, что отправляет ответное сообщение
                        (
                            tabs[0].id, // Х.з, наверное идентификатор вкладки, куда отправить?
                            {greeting: "hello"}, // Это я так понял и есть ответ?
                            function(response) // А это вообще не нужно вроде...
                            {
                                console.log(response.farewell);
                            }
                        )
                    }
                )
*/!*
+                e.data.response(str);

Артист 07.11.2016 21:33

Не получается получить текст ответа.

Сделал так, только передал строку с идентификатором:
В background.js в обработчике запроса:
e.data.response({value: str})


В maps.js:
chrome.runtime.sendMessage
(
	{id: 6, href: 'http://www.site.ru/bla-bla.php', rid: 3},
	function(response)
	{
		alert('ответ пришел\n' + response.value)
	}
)


Что - то ругается:
Цитата:

Error in event handler for (unknown): TypeError: Cannot read property 'value' of undefined
at chrome-extension://gmiiicnncmloicakdbhhdnclnbebgeik/maps.js:152:38

Aetae 07.11.2016 21:44

chrome.runtime.onMessage.addListener // Отлов сообщений
(
    function(request, sender, sendResponse)
    {
        switch(request.id) // Это типа идентификатор сообщений
        {
            case 2: // GET запрос
            {
                GetHttpRequest(request.href, {rid: request.rid, response: sendResponse})
                break
            }
        }
        *!*return true;*/!*
    }
)
Цитата:

Note: The sendResponse callback is only valid if used synchronously, or if the event handler returns true to indicate that it will respond asynchronously. The sendMessage function's callback will be invoked automatically if no handlers return true or if the sendResponse callback is garbage-collected.
Ненавижу WebExtensions. Мерзость. :-E

Артист 07.11.2016 22:16

Спасибо ))
Всё работает.

Артист 07.11.2016 22:47

Блин, только что всё работало, и вдруг стало выдавать ошибку в background.js (e.data.response({value: str})):

Цитата:

Uncaught TypeError: Cannot read property 'response' of undefined(…)
Ничего не менял, только из XMLHttpRequest ответа нужный текст копировал и записал в str...


background.js:

function GetHttpRequest(href, arg)
{
	var xhr = new XMLHttpRequest()
	xhr.withCredentials = true
	xhr.onreadystatechange = function(e)
	{
		e.data = arg
		ProcessRequestChange.apply(this, arguments)
	}
	xhr.open('GET', href, true)
	xhr.send(null)
}
function ProcessRequestChange(e)
{
	var xhr = this

	if(xhr.readyState != 4) return
	if(xhr.status == 200)
	{
		var html = xhr.responseText
		
		switch(e.data.rid)
		{
			/*-------*/
			case 3: // Гильдия наёмников
			{
				if(html.indexOf('возвращайся с победой') > -1 || (html.indexOf('у нас нет сейчас заданий') > -1 && html.indexOf('Вы получаете') < 0)) break

				var s = html.indexOf('align=left rowspan=2>') + 21
				var e = html.indexOf('</td>', s)
				var str = html.substring(s, e)
				
				e.data.response({value: str})
				break
			}
			/*-------*/
			case 0: break // Без обработки
		}
	}
}
chrome.runtime.onMessage.addListener
(
	function(request, sender, sendResponse)
	{
		switch(request.id)
		{
			/*-------*/
			case 6: // Гн 
			{
				GetHttpRequest(request.href, {rid: request.rid, response: sendResponse})
				return true
				break
			}
			/*-------*/
		}
		return false
	}
)


maps.js:

chrome.runtime.sendMessage
(
	{id: 6, href: 'http://www.site.ru/bla-bla.php', rid: 3},
	function(response)
	{
		//alert('ответ пришел\n' + response.value)
		document.querySelector('#fades').style.display = 'inline'
		document.querySelector('#guild').style.display = 'inline'
		document.querySelector('#mercenary').innerHTML = response.value
	}
)

Артист 07.11.2016 22:55

А вот так всё работает:
//var s = html.indexOf('align=left rowspan=2>') + 21
//var e = html.indexOf('</td>', s)
var str = 'errfsdf'//html.substring(s, e)

Aetae 07.11.2016 22:57

Вот эта строчка:
var e = html.indexOf('</td>', s)
тут вы переназначаете переменную "e", соответственно "e.data" уже ничего не содержит.)

Артист 07.11.2016 23:04

От блин, невнимательность, спасибо ))

Артист 09.11.2016 20:40

Вложений: 1
Блин, задолбала уже эта ошибка:
Цитата:

Error in event handler for (unknown): TypeError: Cannot read property 'value' of undefined.
Если у chrome.runtime.sendMessage назначена функция(в которую ответ приходит), то она всегда что ли будет вызываться?

Просто я сделал одну фигню всплывающим окном(вставил часть другой страницы), через localStorage убрал лишние запросы на сайт...

Вложение 3343

Вот это всё равно вызывается:
chrome.runtime.sendMessage
(
	{id: 6, href: 'http://www.site.ru/bla-bla.php', rid: 3},
	function(response)
	{
		alert('ответ')
		document.querySelector('#fades').style.display = 'inline'
		document.querySelector('#guild').style.display = 'inline'
		document.querySelector('#mercenary').innerHTML = response.value
	}
)

А это обработчик(ну или прослушиватель, как его там правильно) этого сообщения.
Тут всё нормально с return'ами break'ами? Просто я не привык к этой дикости в свитче...
chrome.runtime.onMessage.addListener
(
	function(request, sender, sendResponse)
	{
		switch(request.id)
		{
			case 4: // Получить значение localStorage
			{
				var sid = request.sid
				sendResponse({sid: sid, value: localStorage[sid]})
				break
			}
			case 6: // Обработчик 
			{
				var l = localStorage['mercenary_guild']

				if(l != undefined && l > parseInt(+ new Date() / 1000)) break

				alert('запрос')
				GetHttpRequest(request.href, {rid: request.rid, response: sendResponse})
				return true
				break
			}
		}
		return false
	}
)


Когда срабатывает это условие:
if(l != undefined && l > parseInt(+ new Date() / 1000)) break
Появляется ошибка. Я понял, что нет параметра value в ответе(он вообще пустой).

Не понятно какого хрена он вообще приходит, ведь там нет sendResponse()...

Если всё так и должно работать, можно хотя бы как - то проверить, что ответ пустой?

Ostic 09.11.2016 21:18

Цитата:

Сообщение от Артист (Сообщение 434583)
Когда срабатывает это условие:
if(l != undefined && l > parseInt(+ new Date() / 1000)) break

а оно вообще должно сработать?
проверить на тип undefined лучше
typeof L === "undefined"
или
!!(L)

еще лучше проверять на конкретный тип
typeof L === "number"

второе условие вообще безобразно - чем вам так объект Date не понравился, что вы его на 1000 делите? ))
если по-вашему, то
if ( !(typeof === "undefined") &&  L > Math.round(Date.now()/1000)  )

Ostic 09.11.2016 21:23

Цитата:

Если у chrome.runtime.sendMessage назначена функция(в которую ответ приходит), то она всегда что ли будет вызываться?
да, называется функция обратного вызова
не назначайте ее, если не надо или function(){} укажите

Ostic 09.11.2016 21:29

Цитата:

Есть 2 скрипта, 1й(background.js) висит в приложении постоянно, 2й(maps.js) подключается только к определённым страницам
background.js не рекомендовано, также как и "висит в приложении постоянно". кстати, в каком приложении то, о чем речь?
maps.js каким образом подключается к страницам?

Aetae 09.11.2016 21:30

Цитата:

Сообщение от Артист (Сообщение 434583)
Не понятно какого хрена он вообще приходит, ведь там нет

Всё та же цитата, что я уже приводил, только теперь другая её часть:
Цитата:

Note: The sendResponse callback is only valid if used synchronously, or if the event handler returns true to indicate that it will respond asynchronously. The sendMessage function's callback will be invoked automatically if no handlers return true or if the sendResponse callback is garbage-collected.
Может стоит всё же читать документацию?

Ostic 09.11.2016 21:45

это eventPage.js (по-вашему background)
list = ['google.ru','mail.ru','yandex.ru'];
//chrome.webNavigation.onDOMContentLoaded.addListener(onNavigate);
chrome.webNavigation.onCompleted.addListener(onNavigate);
function onNavigate(details){
	var len = list.length;
	var tabID = details.id;
	var tabUrl = details.url.replace(/^https?\:\/\/(www\.)?/, '');
	for (var i=0; i < len; i++){
		if( tabUrl.startsWith(list[i]) ){
			if (details.frameId != 0) break; /*иначе, например, bing "съедает" две загрузки, те msg будет показан 2*/
			var siteName = list[i];
			var msg = list[i];
			chrome.tabs.insertCSS(tabID, {file:"css/content_style.css"});
				chrome.tabs.executeScript(tabID, {file: "js/content_script.js"});
				tabConnect({msg_:msg, name_:siteName});
						
	};
};
function tabConnect(msg) {
	chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
		var port = chrome.tabs.connect(tabs[0].id);
		port.postMessage(msg);
		port.onMessage.addListener(function getResp(response){
			if(response.close_){ 
				sessionStorage.setItem(response.name_, 3);
			};
		});
	});
};

а это в том самом скрипте, который вживляется в указанные в массиве сайты нексолькими строками выше, так называемый content_script.js
var	siteName;
var portName;
var div = document.createElement('div');

chrome.runtime.onConnect.addListener(function(port) {
		portName = port;
		port.onMessage.addListener(function(msg) {
			if(msg.msg_) showMessage(msg.msg_);
			if(msg.name_) siteName = msg.name_;
		});
});
function showMessage(msg){
		div.className = "injection";
		div.innerHTML = '<p class="inj_message">' + msg + '</p>';
		div.addEventListener('click', clickHandler); 
		document.body.appendChild(div);
};	
function clickHandler(){
	document.body.removeChild(div);
	portName.postMessage({name_:siteName, close_:true});
};

я, конечно, свой код покрамсал и поэтому могут быть ошибки, но суть такова: сообщение - ответ.
если надо длительное общение поддерживать, то лучше создавать именованный канал и не забывать его закрывать, а то скрипты так и будут висеть.

Артист 09.11.2016 23:30

Цитата:

Сообщение от Ostic (Сообщение 434590)
а оно вообще должно сработать?

Работает, я же проверял )

Цитата:

Сообщение от Ostic (Сообщение 434590)
проверить на тип undefined лучше
typeof L === "undefined"
или
!!(L)

еще лучше проверять на конкретный тип
typeof L === "number"

второе условие вообще безобразно - чем вам так объект Date не понравился, что вы его на 1000 делите? ))
если по-вашему, то
if ( !(typeof === "undefined") &&  L > Math.round(Date.now()/1000)  )

Учту, спасибо.
Math.round(Date.now()/1000)
Я уже поменял, просто писал это до того как посмотрел другую тему.

Цитата:

Сообщение от Ostic (Сообщение 434592)
background.js не рекомендовано, также как и "висит в приложении постоянно". кстати, в каком приложении то, о чем речь?
maps.js каким образом подключается к страницам?

Ну вроде не всегда висит, оно освобождается из памяти, чего - то там, и используется только при вызове чего либо...
Вешается на "невидимую" вкладку.
Он нужен, т.к. приложение - бот для онлайн игры.
Он работает даже когда нет открытых вкладок с самой игрой.

maps.js подключается в манифесте через указанный адрес.

Цитата:

Сообщение от Aetae (Сообщение 434593)
Всё та же цитата, что я уже приводил, только теперь другая её часть: Может стоит всё же читать документацию?

Да я читал раз 10.
Просто мало того, что там всё по английски(гугл перевод конченный), так ещё терминами непонятными сыпят.
Ничего не понятно, я не могу ничего там найти, из того, что мне нужно...

Спасибо, понял теперь в чём дело. ))

Цитата:

Сообщение от Ostic (Сообщение 434596)
если надо длительное общение поддерживать, то лучше создавать именованный канал и не забывать его закрывать, а то скрипты так и будут висеть.

Да я даже не лезу в именованный :D
Ну и в принципе мне обычного хватит вполне.
Т.к. страницы открываются/закрываются...

Суть вот в чём, открываю страницу сайта("карта", к ней и подгружается maps.js), получаю 3 ячейки из фонового скрипта(background.js) с настройками.
Далее несколько проверок(есть ли задания гильдии наёмников, они либо висят доступные либо сданы и нужно подождать минут 20 до следующего) - запрос на другую страницу(самой гильдии, там пишут доступно или нет и сколько ждать), проверка, вывод посередине страницы части страницы из запроса.

Вот я сейчас на этапе "оптимизации" этого не единственного функционала приложения.

На странице карты можно узнать взято или нет задание, а остальное только запросом.
Чтобы не делать каждый раз запрос(при обновлении карты, а она постоянно в работе), я создал ячейку в памяти и храню в ней время когда будет доступно задание(1й запрос всё - таки делается, чтобы узнать это время).
Ну и вот, только на клиентской стороне всё считается...

Артист 09.11.2016 23:34

Во, всё заработало:
chrome.runtime.onMessage.addListener
(
	function(request, sender, sendResponse)
	{
		switch(request.id)
		{
			case 4: // Получить значение localStorage
			{
				var sid = request.sid
				sendResponse({sid: sid, value: localStorage[sid]})
				return false
				break
			}
			case 6: // Гн 
			{
				var l = localStorage['mercenary_guild']

				if(l != undefined && l > Math.round(Date.now() / 1000)) break

				alert('запрос')
				GetHttpRequest(request.href, {rid: request.rid, response: sendResponse})
				break
			}
		}
		return true
	}
)

Артист 12.11.2016 11:38

А если нужно несколько раз вернуть ответ(асинхронно), то никак не получится?
Т.е. я 1 раз посылаю сообщение в другой скрипт, а тот что - то там делает, и отправляет ответы, при необходимости...

Цитата:

Примечание: Если несколько страниц прослушивание событий OnMessage, только первый вызвать SendResponse () для конкретного события удастся отправить ответ. Все остальные ответы на это событие будет игнорироваться.

Долгоживущее соединение не поможет?

Aetae 12.11.2016 17:39

Артист, таким образом - да.
Для более сложного "постоянного" общения есть connect.

Артист 12.11.2016 21:52

Спасибо )

Артист 04.12.2016 00:56

Вроде понял, нашел где по русски объясняют...

Так нужно отправлять сообщения из background.js в maps.js

chrome.tabs.query // Запрос браузеру на получение id всех вкладок
(
	{url: "http://www.site.ru/*"}, // Параметры(фильтры)
	function(tabs) // Функция вызывающаяся после получения
	{
		var l

		if((l = tabs.length) != 0) return // Если нет подходящих вкладок

		var i = -1

		while(++i < l) chrome.tabs.sendMessage(tabs[l].id, {greeting: "hello"}) // Отправка сообщения вкладкам
	}
)


В maps.js должна быть функция chrome.runtime.onMessage.addListener.

Я хочу сделать на какой вкладке был запрос, туда и слать ответные сообщения.
Т.е. я из maps.js отправляю один запрос в background.js, и уже в этом запросе передаю нужный ид вкладки.
В background.js уже будет отослано этой вкладке несколько "ответов".

Как получить id текущей вкладки?

Артист 05.12.2016 00:35

Нашел:
chrome.tabs.getSelected
(
	null,
	function(tab)
	{
		console.log(tab)
	}
)


Оказалось, что из контент скрипта нет доступа к апи хрома.

Уродство.


Часовой пояс GMT +3, время: 15:48.