Ехал handler через handler, или почему всё тормозит

Прошло уже 10 лет с моего предыдущего разбора, не побоюсь этого слова, говнософта, как появилась очередная проблема, с которой мне захотелось разобраться и устранить её.

TLDR

С чего всё начиналось

Некоторое время назад случилось так, что телевизор стал единственным доступным способом вывода звука с моего ПК, и по сути, занял роль второго монитора. Ну, а раз он теперь оккупирован компьютером, то встал вопрос —, а как же мне смотреть на ютубе документалочки во время засыпания? Так как я слишком ленивый чтобы заранее составлять плейлисты, мне нужно было управление с телефона, через официальное приложение YouTube. Постоянно переключаться между HDMI и YouTube App на ТВ совсем не входило в мои интересы, я приступил к поиску решения, которое было найдено очень быстро — у платформы уже есть готовый интерфейс для ТВ в вебе, только была незадача: при открытии в браузере оно просто редиректит на главную.
Решение же этой проблемы не заставило себя долго ждать, для хрома имеется куча расширений, которые позволяют решить проблему с редиректом. В итоге мой выбор остановился на Youtube TV On PC, т.к. оно работало и имело относительно неплохой рейтинг. Но всё было не так красочно, как я мечтал…

Появление проблем

Далее по тексту под TV имеется ввиду веб версия YouTube TV

С первой проблемой я столкнулся спустя, наверное, неделю — приложение не могло подключиться к TV. А ещё сама загрузка TV могла занимать несколько минут. Но после этого всё успешно работало, пока снова не начиналось аналогичное поведение.
Я не сильно придавал этому значения, так как у меня используются другие расширения, в т.ч. и для блокировки рекламы, а в то время YouTube как раз активно замедлял работу сервиса для таких пользователей.

Со второй проблемой я столкнулся спустя несколько месяцев, когда вместо ПК использовал ноутбук — видео начало адски тормозить и хром начал выедать процессор. Связано это было с тем, что на YouTube у меня видео включались в максимальном качестве, а в большинстве случаев это 4k60. На эту проблему я тоже успешно забил, т.к. спустя несколько дней я опять вернулся к ПК, и проблем это не доставляло. До тех пор, пока у меня провайдер не решил то, что мне и 10Mbps хватит. Но и тогда проблема решилась разбирательством с провайдером.

Время шло, число сменилось, ни черта не изменилось

В общем, пострадал я таким образом около полугода, и мне это поднадоело. Потому что помимо ранее озвученных проблем, была ещё одна, самая бесючая — зависала не одна вкладка, зависал весь хром, даже диспетчер задач хрома зависал.
Желания разбираться в том, как же отлаживать ПО в Windows у меня не было никакого, но проблему нужно было как-то устранить. Решил через ProcessExplorer найти тот самый процесс, который подвисает и посмотреть в его стектрейс. Процесс был найден, на верхушке стека было что-то про …crash…. Ну вот, думаю я, ты и попался. Если что-то где-то крэшится, значит что-то должно падать в логи. Включаю логгирование в хроме, жду неделю, ловлю опять зависание, и… И в логах абсолютно ничего нет. Мониторил ещё несколько месяцев в надежде поймать что-нибудь.

80iyiaa22jxv44kbpcd2rke_d70.png

Последняя капля

В очередной раз всё подвисло на несколько минут, а т.к. я в это время был занят работой, меня это очень сильно разозлило, и я решил окончательно покончить с этим.
Думаю, а что будет если пристрелить процесс этого расширения? Shift+Esc, End process, зависание! Всё, подлец, попался!
Учитывая мою ленивость, я, наверное, должен был пойти и установить другое расширение, но нет, тут уже было дело принципа, нужно докопаться до истины. Поэтому, скачиваю CRX архив, наливаю кружку кваса и вперёд. А то мало ли, вдруг там майнер крутился всё это время, но оказалось всё гораздо проще и скучнее.

Исходники

Открыв расширение в архиваторе, моему взору предстал background.js следующего содержимого:

const userAgent = "Mozilla/5.0 (SMART-TV; LINUX; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) 69.0.3497.106.1/5.5 TV Safari/537.36";
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
	chrome.webRequest.onBeforeSendHeaders.addListener(function (details) {
		for (var i = 0; i < details.requestHeaders.length; ++i) {
			if (details.requestHeaders[i].name === "User-Agent") {
				details.requestHeaders[i].value = userAgent;
				break;
			}
		}
		return { requestHeaders: details.requestHeaders };
	}, { urls: ['*://www.youtube.com/tv*'] }, ['blocking', 'requestHeaders']);

	if (changeInfo.status == "complete" && tab.url.includes("youtube") && tab.url.includes("watch?v=")) {
		chrome.tabs.executeScript(tabId, {
			file: "/hd.js"
		});
	}
});

И тут я понимаю весь %%%ец происходящего, и откуда зависания, и откуда 4k60 везде где только можно. А теперь по порядку…

  1. Расширение добавляет хэндлер для всех событий всех табов! Да, у расширения как оказалось, в манифесте не указано ограничение по доменам, и в самом addListener не указан filter, который бы позволил отфильтровать ненужные события, как минимум по URL и типу события.

  2. Внутри хэндлера, который, я напомню, вызывается для всех табов и для всех типов событий, которых как минимум 12 штук, добавляется уже нужный обработчик webRequest.onBeforeSendHeaders, который хотя бы вызывается только со страницы www.youtube.com/tv.

  3. Проверяется что URL таба, от которого пришло событие содержит youtube и watch?v=, и выполняет скрипт, который и включает максимальное качество в плеере.

Если очень грубо посчитать, то мы имеем то, что при открытии браузера с 50 вкладками (не знаю как для вас, а для меня это нормальная ситуация), у нас добавляется примерно 200 хэндлеров для webRequest.onBeforeSendHeaders. Это если посчитать события смены url, favicon, заголовка страницы и статуса загрузки. Далее можно ещё добавить события, которые происходят во время пользования браузером, а как я выше говорил, зависания случались где-то спустя неделю после запуска. А ещё есть сайты, вроде VK, где при входящих сообщениях меняется favicon каждые несколько секунд.

hd.js не представляет особого интереса

var scriptTag = document.getElementById('ytTvHd');
if (scriptTag) {
	scriptTag.remove();
}
var script = document.createElement('script');
script.id = "ytTvHd";
script.type = 'text/javascript';
script.textContent = "var ytvPlayer = document.getElementById('movie_player') || document.querySelector('.html5-video-player');ytvPlayer.setPlaybackQualityRange('highres');" +
	"document.getElementsByTagName('body')[0].onkeydown = function(e) {if (e.keyCode == 428) {ytvPlayer.requestFullscreen();}};";
document.body.appendChild(script);

Выводы

Для себя я сделал очевидный вывод: проблемы всегда нужно искать на поверхности, и в первую очередь оценить пряморукость разработчиков простых вещей, а не пытаться найти проблему заходя с другого конца. А проблему с расширением решил путём сокращения кода и установки из локальной директории.

const userAgent = "Mozilla/5.0 (SMART-TV; LINUX; Tizen 5.5) AppleWebKit/537.36 (KHTML, like Gecko) 69.0.3497.106.1/5.5 TV Safari/537.36";

chrome.webRequest.onBeforeSendHeaders.addListener(function (details) {
	for (var i = 0; i < details.requestHeaders.length; ++i) {
		if (details.requestHeaders[i].name === "User-Agent") {
			details.requestHeaders[i].value = userAgent;
			break;
		}
	}
	return { requestHeaders: details.requestHeaders };
}, { urls: ['*://www.youtube.com/tv*'] }, ['blocking', 'requestHeaders']);

Вероятно, для решения моей проблемы подойдет любое другое расширение для смены HTTP-заголовков, но сколько ещё секретов они в себе могут таить

Habrahabr.ru прочитано 3547 раз