tag:blogger.com,1999:blog-29941470351524526452024-03-13T06:50:59.972-07:00Расширения ChromeUnknownnoreply@blogger.comBlogger34125tag:blogger.com,1999:blog-2994147035152452645.post-85385386776957431922014-02-23T21:21:00.001-08:002014-02-23T23:18:48.583-08:00Silver bird<p><a href="http://lh3.ggpht.com/-DQBsbx_lNR8/UwrW0u32ilI/AAAAAAAADFI/v6Kcb9QAMJw/s1600-h/silverbird%25255B1%25255D.jpg"><img title="silverbird" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="silverbird" src="http://lh5.ggpht.com/-JRV12RL2S9o/UwrW1QWB7rI/AAAAAAAADFM/_E0xsIbBnX0/silverbird_thumb%25255B1%25255D.jpg?imgmax=800" width="642" height="484"></a></p> <a name='more'></a> <p>Это расширение для работы с твиттером, замещающее его стандартный интерфейс. Есть пара приятных мелочей, например возможность создавать отдельные вкладки в окне расширения, в которых будут выводится результаты поиска по определенному слову. </p> <p>Судя по отзывам, есть еще пара глюков, что-то там с картинками</p> <p>Но в целом ничего особенного</p> <p>Мне оно интересно с другой точки зрения.</p> <ul> <li>это довольно сложное приложение, и судя по косвенным признакам, большая его часть написана одним человеком и достаточно быстро. На эту мысль наводит прежде всего полное отсутствие комментариев. <li>Реализован если не полный, то очень близко к тому функционал для работы с твиттером. Это неплохая возможность поковыряться в чужих исходниках, подсмотреть как оно делается. <li>Некоторые штрихи, которые меня сходу удивляют, я не до конца понимаю смысл этих манипуляций. Например смысл следующего объявления в манифесте: "default_popup": "popup.html?popup".</li></ul> <p>Еще один меня смущают скромные масштабы использования расширений. Твиттер – весьма популярный сервис. Это расширение легко находится в магазине гугла, фактически в первой строчке, оно бесплатно, и сколько же установок у этого расширения? 4300. Это ничтожно мало, но может быть оно появилось недавно? Смотрим весь список, самое популярное – 23.000. Чуть лучше, но прямо скажем, тоже не фонтан. А платных расширений нет вообще. Что как бы намекает – разработка расширений если и монетизируется, то как-то совсем по другому.</p> <h2>Update</h2> <p>Есть более новый вариант, Silver Bird Plus, который также имеет некоторые проблемы, и которому уже 2 года и за это время набралось 10447 пользователей. Очень не хватает информации о дате последнего обновления расширений и вообще истории. Отзывы этого не дают</p> <p>А еще, я оказывается не туда раньше смотрел и неправильно оценивал количество пользователей. У предыдущей версии более 200 тысяч и особый прикол в том, что эта информация не влезает в заголовок окна с информацией о приложении и ее можно посмотреть только просмотром кода элемента. Я в шоке. У самого популярного приложения для твиттера соответственно почти миллион, но оно на самом деле как бы не совсем для твиттера – это приложение которое делает скриншоты.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-69961824957990012852014-02-23T08:17:00.001-08:002014-02-23T08:25:18.717-08:00Облачный фотохостинг NeePic<p align="justify">Это просто место куда можно положить картинки. Зачем? А х.з…. Ну просто надо вам быстро опубликовать картинку, чтобы потом можно было только ссылку скинуть. Лимиты на размер почты, например, есть в некоторых организациях. Или тотальный запрет на пересылку в почте картинок. Но вообще-то в таких случаях есть дропбокс, гуглдиск, яндексдиск и еще туева хуча мест куда можно что-то положить, и при этом не обязательно открывать доступ для всех. А если нужен доступ для всех, то опять же, любой бесплатный блогохостинг (ЖЖ, блогспот) справится с этой задачей не хуже. А если… да как ни крути, нет в этом сервисе ничего, что выделило б его из массы аналогичных и во многом более продвинутых и популярных сервисов.</p> <a name='more'></a> <p align="justify">Подозреваю, что это просто проба сил какого-то студента, который пытается выглядеть командой разработчиков. Что там делать целой команде? Когда-то я так сделал файловый хостинг – хотел проверить насколько ограничено безлимитное дисковое пространство на BlueHost’е <img class="wlEmoticon wlEmoticon-smile" style="border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none" alt="Улыбка" src="http://lh6.ggpht.com/-l3LPiou2b50/UwofC5hAqXI/AAAAAAAADEs/v17NfUCGEBE/wlEmoticon-smile%25255B2%25255D.png?imgmax=800">. Дело прошлое, безлимитный объем довольно быстро закончился <img class="wlEmoticon wlEmoticon-openmouthedsmile" style="border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none" alt="Широкая улыбка" src="http://lh6.ggpht.com/-PXJMc0E3gB0/UwogIamiJRI/AAAAAAAADE4/KhN6UAECtsI/wlEmoticon-openmouthedsmile%25255B2%25255D.png?imgmax=800"></p> <p align="justify">Собственно заинтересовался я им лишь потому к <strike>этим нанотехнологиям</strike> этому облачному хостингу прилагается расширение для Chrome. Вот его то я скачал для того чтобы пощупать. И первым делом учинил поиск, куда же оно поставилось, хотел исходники посмотреть. Расширение, кстати, не работает. </p> <p align="justify">Тут меня ожидал небольшой облом, я хотел простым поиском найти все crx файлы. Их не должно быть так уж много, и далее методом тыка определить какой мне нужен. Сюрприз заключается в том что Chrome не обязательно хранит расширения в crx – я нашел лишь стандартные приложения, еще чего-то по мелочам.</p> <p align="justify">Это меня обескуражило, но не надолго. В конечном итоге пропажа была найдена – все расширения мой браузер держит в <strong>C:\Documents and Settings\Alex\Local Settings\Google\Chrome\User Data\Default\Extensions</strong>. Нашел нужное расширение, проглядел исходники и немного обалдел. Расширение загружает фоновую страницу с полем для загрузки файла. При нажатии на иконку расширения оно пытается изобразить клик по этому полю, через jQuery. Все бы ничего, только фоновая страница невидимая и первое что я заподозрил, что именно по этой причине результатов не воспоследует. Вообще никаких.</p> <p align="justify">Может на линуксе, или маках, или девелоперской версии хрома это не так, но вот на обычном браузере под виндой это не работает. Надежнее было бы сделать обычное всплывающее окно.</p> <p align="justify">Для очистки совести я еще немножко поковырялся с исходным кодом и обалдел вторично – метод click имеет какие-то свои нюансы: если я создам атрибут onclick для поля загрузки файла, пропишу там что-то простое, вроде alert(‘hello’), то это выполнится и при вызове метода click() и при нажатии на кнопку выбрать файл. Но окно выбора файла откроется только во втором случае! Сюрприз!</p> <p align="justify"><strong>Update</strong>: разработчики подтвердили – раньше работало, после обновления Chrome перестало. Вот за это я нежно люблю гугл – в любой момент все может поломаться.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-47809589100966022652014-02-22T21:11:00.001-08:002014-02-22T21:11:46.177-08:00OAuth<p>Не так уж давно, каждый веб-сервис требовал отдельной регистрации, но все большая часть из них дает возможность пользователю использовать для авторизации свой аккаунт в соцсетях. Реализуется эта возможность благодаря протоколу OAuth. Это открытый протокол для авторизации, который позволяет пользователю предоставить доступ к своим данным, без риска засветить свой логин и пароль.</p> <a name='more'></a> <p>При написании этой статьи предполагается, что вы уже имеете представление о токам устроено расширение браузера и как работает OAuth. Начать знакомство с этой темой можно <a href="http://ru.wikipedia.org/wiki/OAuth" rel="nofollow">со статьи в википедии</a>.</p> <h2>Быстрый старт</h2> <p>Для начала вам понадобится: </p> <ul> <li>страница редиректа и скрипт ее инициализации <strong><a href="https://developer.chrome.com/extensions/examples/extensions/oauth_contacts/onload.js">onload.js</a></strong></li> <li>два скрипта с библиотеками </li> <ul> <li><strong><a href="https://developer.chrome.com/extensions/examples/extensions/oauth_contacts/chrome_ex_oauth.js">chrome_ex_oauth.js</a></strong> <li><strong><a href="https://developer.chrome.com/extensions/examples/extensions/oauth_contacts/chrome_ex_oauthsimple.js">chrome_ex_oauthsimple.js</a></strong></li></ul></ul> <p>Протокол OAuth включает в себя три шага:</p> <ul> <li>получение первоначального токена запроса</li> <li>авторизация пользователя и подтверждение им права доступа к запрошенным данным</li> <li>получение токена доступа</li></ul> <p>С расширением все немного сложнее. Тут нет URLа на который можно перенаправить пользователя после завершения авторизации и подтверждения доступа. Так что гугл, совместно с некоторыми другими компаниями немного доработал OAuth для использования с приложениями. Вместо URL ресурса используется имя приложения. Конечный результат тот же самый: фоновая страница запрашивает токен, открывает отдельную вкладку для авторизации и подтверждения доступа, и, наконец, выполняет асинхронный запрос для получения токена доступа.</p><pre>var oauth = ChromeExOAuth.initBackgroundPage({<br /> 'request_url': 'https://www.google.com/accounts/OAuthGetRequestToken',<br /> 'authorize_url': 'https://www.google.com/accounts/OAuthAuthorizeToken',<br /> 'access_url': 'https://www.google.com/accounts/OAuthGetAccessToken',<br /> 'consumer_key': 'anonymous',<br /> 'consumer_secret': 'anonymous',<br /> 'scope': 'https://docs.google.com/feeds/',<br /> 'app_name': 'My Extension name'<br />});</pre><br /><p>Вот примерно так выглядит инициализация библиотеки. Для работы с другими OAuth-сервисами, не гуглом, нужно смотреть их документацию для разработчиков. URL будут другие, общий принцип такой же.</p><br /><p>В манифесте должны быть соответтсвующие разрешения:</p><pre>"permissions": [ "tabs", "https://docs.google.com/feeds/*",<br /> "https://www.google.com/accounts/OAuthGetRequestToken",<br /> "https://www.google.com/accounts/OAuthAuthorizeToken",<br /> "https://www.google.com/accounts/OAuthGetAccessToken"<br />]</pre><br /><p>Начинаем процесс:</p><pre>oauth.authorize(function() {<br /> // ... Ready to fetch private data ...<br />});</pre><br /><p>Будет открыта новая вкладка, пользователь там авторизуется, и разрешит доступ. Когда этот процесс завершится будет вызвана callback функция. Нам нет необходимости заботится о хранении токена и прочих нюансах – все это спрятано в библиотеке. Она хранит токен в localStorage и если мы повторно запросим авторизацию, но новая вкладка не откроется, а будет использован уже имеющийся токен. Callback функция будет вызвана в любом случае.</p><br /><blockquote><br /><p>Возникают вопросы: </p><br /><ol><br /><li>приложение может запрашивать разный объем прав доступа. Где и как это задается? </li><br /><li>Как распознается ситуация, когда пользователь отказался предоставить доступ, или не смог авторизоваться (забыл пароль например)?</li><br /><li>Что будет если сервис авторизации в данный момент недоступен?</li></ol></blockquote><br /><p>После получения доступа мы получаем возможность отправлять подписанные запросы:</p><pre>function callback(resp, xhr) {<br /> // ... Process text response ...<br />};<br /><br />function onAuthorized() {<br /> var url = 'https://docs.google.com/feeds/default/private/full';<br /> var request = {<br /> 'method': 'GET',<br /> 'parameters': {'alt': 'json'}<br /> };<br /><br /> // Send: GET https://docs.google.com/feeds/default/private/full?alt=json<br /> oauth.sendSignedRequest(url, callback, request);<br />};<br /><br />oauth.authorize(onAuthorized);</pre><br /><p>Более сложный пример:</p><pre>function onAuthorized() {<br /> var url = 'https://docs.google.com/feeds/default/private/full';<br /> var request = {<br /> 'method': 'POST',<br /> 'headers': {<br /> 'GData-Version': '3.0',<br /> 'Content-Type': 'application/atom+xml'<br /> },<br /> 'parameters': {<br /> 'alt': 'json'<br /> },<br /> 'body': 'Data to send'<br /> };<br /><br /> // Send: POST https://docs.google.com/feeds/default/private/full?alt=json<br /> oauth.sendSignedRequest(url, callback, request);<br />};</pre>Существуют еще различные вариации и скользкие моменты, для их прояснения придется хорошо порыться в примерах, документации и исходниках чужих расширений Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2994147035152452645.post-58625189388816808262014-02-22T00:49:00.001-08:002014-02-22T00:49:12.835-08:00Перенос расширений на 2ую версию манифеста<p>Актуально ли это? Уже полтора года как Web Store не принимает расширения с 1ой версией (с августа 2012). Уже год как он не дает их обновлять (с марта 2013). Полгода, как они перестали показываться в результатах поиска, категориях и т.д. А с января этого года браузер вообще перестал их поддерживать.</p> <p>Все кто брался в течение этого времени за разработку – сразу прописывали 2ой, в первой версии не было ничего такого, за что стоило держаться. Все новые разработчики естественно используют 2ой манифест, просто по умолчанию. Им нечего переносить.</p> <p>Резюме: переносить давно уже нечего. Все кто мог и хотел – уже перенесли. Найти исходники полезных расширений, не перенесенных их авторами – не слишком реально.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-81142682645458901012014-02-21T23:36:00.001-08:002014-02-21T23:37:01.178-08:00Обмен сообщениямиПоскольку <a href="http://chrome-ext.blogspot.com/2014/02/content-scripts.html">контентный скрипт</a> работает в контексте веб-страницы, а не расширения, он должен как-то обмениваться данными с основной частью расширения. Например расширение для чтения RSS должно определять наличие RSS на странице с помощью контентного скрипта и уведомлять об этом фоновую страницу чтобы та в свою очередь добавила иконку <a href="http://chrome-ext.blogspot.com/2014/02/page-actions.html">page action</a>. Как же это сделать?<br />
<a name='more'></a>Есть два варианта – простой API для однократной передачи сообщений (самое-то для рассмотренного примера) и более сложный, позволяющий создать постоянный канал обмена сообщениями.<br />
<h2>
Простая передача сообщений</h2>
Если нужно просто отправить сообщение и, опционально, получить ответ, используется вызов методов <strong>runtime.sendMessage</strong> или <strong>tabs.sendMessage</strong>. <br />
Из контентного скрипта фоновой странице:<br />
<pre>chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});</pre>
<br />
Из фоновой страницы контекстному скрипту:<br />
<pre>chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});</pre>
<br />
На принимающей стороне должен быть установлен обработчик <strong>runtime.onMessage</strong>. Следующий код будет работать и в контентном скрипте и в фоновой странице:<br />
<pre>chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});</pre>
<br />
<div align="justify">
<strong>Примечание:</strong> если много страниц имеют обработчики <strong>runtime.onMessage</strong> и в них не предусмотрен механизм отделения “своих” сообщений от “чужих”, то это кончится плохо – отработают все обработчики, но только первый из них успешно отправит ответ. Остальные обломаются. </div>
<br />
<h2>
Постоянное соединение</h2>
<br />
<div align="justify">
Иногда сообщениями нужно обмениваться постоянно. Чтобы не создавать лишнюю нагрузку на процессор – каждое сообщение вызывает активацию обработчиков на всех страницах, есть другой механизм. Для этого используются методы <a href="https://developer.chrome.com/extensions/runtime.html#method-connect">runtime.connect</a> и <a href="https://developer.chrome.com/extensions/tabs.html#method-connect">tabs.connect</a></div>
<pre>var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question == "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question == "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
});</pre>
<br />
И на другой стороне:<br />
<pre>chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke == "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer == "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer == "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});</pre>
<br />
Таким образом создается именованный канал и все последующие сообщения передаются в рамках этого канала. Напомню – пока канал открыт, фоновая страница не может быть деактивирована.Так что если сообщения передаются редко, то лучше все же использовать первый механизм. А когда необходимость в канале отпала – закрывать его вызовом <strong>runtime.Port.disconnect</strong>. Этот метод может быть вызван с любой стороны, при этом на другой стороне возникает событие <strong>runtime.Port.onDisconnect</strong>.<br />
<br />
<h2>
Обмен сообщениями между расширениями</h2>
<br />
Кроме обмена сообщениями между компонентами вашего расширения, можно обмениваться сообщениями и с другими расширениями. Для приема таких сообщений есть события <strong>runtime.onMessageExternal</strong> и <strong>runtime.onConnectExternal</strong>, соответственно для разовых сообщений и для создания постоянного канала:<br />
<pre>// For simple requests:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.id == blacklistedExtension)
return; // don't allow this extension access
else if (request.getTargetData)
sendResponse({targetData: targetData});
else if (request.activateLasers) {
var success = activateLasers();
sendResponse({activateLasers: success});
}
});
// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
port.onMessage.addListener(function(msg) {
// See other examples for sample onMessage handlers.
});
});</pre>
<br />
А вот чтобы отправить сообщение, нужно знать идентификатор расширения:<br />
<pre>// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
});
// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);</pre>
<br />
Если я ничего не путаю, идентификатор расширения фиксируется при его загрузке в Google Store. Значит для расширений не опубликованных там возможность использования данного механизма под вопросом.<br />
<br />
<h2>
Отправка сообщений с веб-страницы</h2>
<br />
Подобным же образом расширение может получать сообщения от веб-страницы. Для этого нужно прописать такую возможность в манифесте:<br />
<pre>"externally_connectable": {
"matches": ["*://*.example.com/*"]
}</pre>
<br />
Чтобы обмениваться сообщениями с расширением веб страница должна знать его ID:<br />
<pre>// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});</pre>
<br />
А расширение принимает их через тот же интерфейс, что и от других расширений:<br />
<pre>chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
openUrl(request.openUrlInEditor);
});</pre>
<br />
<blockquote dir="ltr" style="margin-right: 0px;">
<br />
<strong>Примечание</strong>: как быть с тем, что нам заранее не известен ID расширения (жаба душит отдавать 5$ за регистрацию на ГуглСтор)? Можно передать странице ID расширения контентным скриптом, через DOM.</blockquote>
<br />
<h2>
Обмен сообщениями с нативными приложениями</h2>
<br />
Нативное приложение должно зарегистрировать соответствующую возможность (<em>native messaging host</em>) и указать с какими расширениями оно может работать:<br />
<pre>{
"name": "com.my_company.my_application",
"description": "My Application",
"path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe",
"type": "stdio",
"allowed_origins": [
"chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"
]
}</pre>
<br />
Манифест приложения должен содержать следующие параметры:<br />
<table class="simple"><tbody>
<tr><th width="145">Имя</th><th width="703">Описание</th></tr>
<tr><td width="145"><strong>name</strong></td><td width="703">Имя хоста. Клиентское расширение передает это значение в методы runtime.connectNative или runtime.sendNativeMessage.</td></tr>
<tr><td width="145"><strong>description</strong></td><td width="703">Краткое описание приложения</td></tr>
<tr><td width="145"><strong>path</strong></td><td width="703">Путь к бинарному файлу приложения. В системах Linux and OSX путь должен быть абсолютным. На Windows все не как у людей – путь может быть относительным, указывать месторасположение бинарного файла относительно каталога где лежит манифест.</td></tr>
<tr><td width="145"><strong>type</strong></td><td width="703">Тип интерфейса для обмена сообщениями. На текущий момент он единственный: <strong>stdio</strong>. </td></tr>
<tr><td width="145"><strong>allowed_origins</strong></td><td width="703">Список ID расширений которым дозволено работать с этим приложением. Фактически это означает что нельзя предоставить публичный интерфейс приложения всем и каждому. Разработчик приложения должен явно предоставить доступ к своему приложению разработчику расширения.</td></tr>
</tbody></table>
<br />
Местонахождение манифеста зависит от платформы:<br />
<br />
<ul><br />
<li>Windows: где угодно. Приложение должно создать ключ реестра <strong>HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\<em>com.my_company.my_application</em></strong> или <strong>HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\<em>com.my_company.my_application</em></strong>, и установить в значении ключа по умолчанию абсолютный путь к файлу манифеста.</li>
<li>OSX: <strong>/Library/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json</strong>, или <strong>~/Library/Application Support/Google/Chrome/NativeMessagingHosts/<em>com.my_company.my_application</em>.json</strong></li>
<li>Linux:<strong> /etc/opt/chrome/native-messaging-hosts/com.my_company.my_application.json</strong> или <strong>~/.config/chrome/NativeMessagingHosts/com.my_company.my_application.json</strong>.</li>
</ul>
<br />
Chrome запускает каждый нативный хост в отдельном процессе и взаимодействует с ним через стандартный ввод/вывод. Сообщения сериализуются в формате JSON, кодировка UTF-8, в начале сообщения 32битная длина, порядок байтов соответствует платформе.<br />
<br />
Когда используется runtime.connectNative, Chrome запускает нативный хост и держит его активным пока соединение не будет закрыто. Если используется runtime.sendNativeMessage, то браузер запускает новый процесс для каждого сообщения. От хоста в этом случае ожидается ответ на исходное сообщение, а все последующие сообщения, если таковые будут – игнорируются.<br />
<br />
Пример использования <strong>runtime.connectNative</strong>:<br />
<pre>var port = chrome.runtime.connectNative('com.my_company.my_application');
port.onMessage.addListener(function(msg) {
console.log("Received" + msg);
});
port.onDisconnect.addListener(function() {
console.log("Disconnected");
});
port.postMessage({ text: "Hello, my_application" });</pre>
<br />
Пример использования <strong>runtime.sendNativeMessage</strong>:<br />
<pre>chrome.runtime.sendNativeMessage('com.my_company.my_application',
{ text: "Hello" },
function(response) {
console.log("Received " + response);
});</pre>
Unknownnoreply@blogger.com13tag:blogger.com,1999:blog-2994147035152452645.post-67153367659738863702014-02-21T03:54:00.001-08:002014-02-21T03:54:48.245-08:00Локализация<p>Чтобы перевести ваше расширение на много языков, необходимо создать папку _locales, в ней отдельные папки для каждого языка, например en, ru, fr и так далее. В каждой из этих папок должен быть файл message.json, в котором будут храниться переводы всех сообщений.</p> <p>Список локалей, которые поддерживает Chrome можно найти <a href="https://developers.google.com/chrome/web-store/docs/i18n?hl=ru&csw=1#localeTable" rel="nofollow">тут</a>.</p> <a name='more'></a> <p>Содержимое message.json выглядит примерно вот так:</p><pre>{<br /> "name": {<br /> "message": "My extension name",<br /> "description": "Name of extension."<br /> },<br /> "search_string": {<br /> "message": "hello%20world",<br /> "description": "The string we search for. Put %20 between words that go together."<br /> },<br /> ...<br /> }</pre><br /><p>Имя – ему соответствует значение и описание. Описание – чисто для справки, чтоб не забыть самому что это и зачем. Далее заданное значение будет подставляться везде где оно необходимо, но не автоматически – замену нужно указать явно.</p><br /><p>В манифесте и css: </p><pre>__MSG_<em>messagename</em>__</pre><br /><p>В скриптах: </p><pre>chrome.i18n.getMessage("<em>messagename</em>")</pre><br /><p>Про локализацию HTML в документации ни сказано ни слова. Но в принципе это возможно через манифест и скрипты. Нужно создавать отдельные html файлы на разных языках и указывать имя файла через локализованное значение. Например использовать __MSG_<em>popupname</em>__ вместо явного указания имени файла для всплывающего окна. А в файлах локализации задавать popup_ru.html, popup_en.html и т.д.</p><br /><p>В манифесте обязательно должна быть указана локаль по умолчанию, например:</p><pre>{<br /> "name" : "__MSG_name__"<br /> "default_locale" : "en"<br />}</pre><br /><p>Вместо __MSG_name__ будет подставлено значение “My extension name” на текущем языке, а если он не указан – на английском</p><br /><h2>Предопределенные сообщения</h2><br /><table><br /><tbody><br /><tr><br /><th>Имя</th><br /><th>Описание</th></tr><br /><tr><br /><td><strong>@@extension_id</strong> </td><br /><td>Идентификатор расширения. Может быть использован для формирования URL. Например контентным скриптом чтобы вставить картинку из расширения в текущую веб-страницу:<br><pre>body {<br /> <b>background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');</b><br /> }</pre><b>Примечание:</b> Нельзя использовать в манифесте</td></tr><br /><tr><br /><td><strong>@@ui_locale</strong> </td><br /><td>Текущая локаль. </td></tr><br /><tr><br /><td><strong>@@bidi_dir</strong> </td><br /><td>Направление текста ltr/rtl </td></tr><br /><tr><br /><td><strong>@@bidi_reversed_dir</strong> </td><br /><td>значение обратное <strong>@@bidi_dir</strong></td></tr><br /><tr><br /><td><strong>@@bidi_start_edge</strong> </td><br /><td>Если <strong>@@bidi_dir</strong> = "ltr", тогда "left" иначе "right". </td></tr><br /><tr><br /><td><strong>@@bidi_end_edge</strong> </td><br /><td>Если <strong>@@bidi_dir</strong> = "ltr", тогда "right" иначе "left". </td></tr></tbody></table><br /><h2>Поиск локализованного сообщения</h2><br /><ol><br /><li>Сначала ищется сообщение для локали точно соответствующей локали пользователя. Например “en_US”. <br /><li>Если не найдено, и локаль имеет суффикс (в данном случае “_US)” – сообщение ищется в локали без суффикса – “en”. <br /><li>Если не найдено ничего, тогда берется локаль по умолчанию, прописанная в манифесте.</li></ol><br /><h2>Плейсхолдеры</h2><br /><p>Предположим, вы хотите локализовать приветствие:</p><br /><blockquote><br /><p>“Здравствуйте [имя пользователя]. Сегодня [Такое-то число]. Добро пожаловать на наш сайт!”. </p></blockquote><br /><p>Это что же, отдельное сообщение для здравствуйте, еще одно для “сегодня”, и еще одно для “Добро пожаловать…” ? А ведь на другом языке фраза может быть скомпонована по другому, но в нее нужно вставить те же значения – имя пользователя и сегодняшнее число. Как быть? Для этого есть placeholders.</p><pre>welcome: { <br /> message: "Здравствуйте $username$. Сегодня $day$. Добро пожаловать на наш сайт!",<br /> description: "Приветствие пользователю",<br /> placeholders: {<br /> username : {<br /> content: "$1",<br /> example: "Вася Пупкин"<br /> }<br /> day : {<br /> content: "$2",<br /> example: "30 февраля"<br /> }<br /> } <br />}</pre><pre>function getMessage() {<br /> var message = chrome.i18n.getMessage("welcome", ["Иван", "1 мая"]);<br /> document.getElementById("welcometext").innerHTML = message;<br /> }</pre> Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-2994147035152452645.post-83427192870989220072014-02-21T01:03:00.001-08:002014-02-21T01:03:24.490-08:00Отладка<h2>Информация о расширении</h2> <p>Отлаживать будем расширение Hello World, описанное в <a href="http://chrome-ext.blogspot.com/2014/02/blog-post.html">быстром старте</a>. Создайте и подключите его к браузеру, не забудьте на странице <strong>chrome://extensions </strong>включить<strong> “Режим разработчика”.</strong> Там же можно узнать идентификатор расширения. Выглядит он примерно так: <strong>ID: maihdofgpghpnnjniclajfepbgdhabpg</strong>.</p> <a name='more'></a> <h2>Исследуем всплывающее окно</h2> <p>Для этого кликаем правой клавишей мыши по иконке расширения и выбираем пункт “Просмотреть всплывающее окно”. Если вы уже отлаживали скрипты с помощью встроенных в Chrome инструментов разработчика, то открывшееся окно вас не удивит. Все привычные возможности присутствуют.</p> <p><a href="http://lh3.ggpht.com/-jXbQ6duMiK0/UwcWUXxiGkI/AAAAAAAADEA/t8_ux8RBqjQ/s1600-h/popup%25255B3%25255D%25255B1%25255D.jpg"><img title="popup[3]" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="popup[3]" src="http://lh4.ggpht.com/-ns4kIAGzEgw/UwcWVL5IfcI/AAAAAAAADEI/umt0NJFHGik/popup%25255B3%25255D_thumb%25255B1%25255D.jpg?imgmax=800" width="684" height="493"></a></p> <p>Теперь справа вверху жмем кнопку <img src="https://developer.chrome.com/static/images/console-button.gif">- внизу откроется консоль для ввода команд. Теперь в ваших скриптах можно расставить точки останова и командой <strong>location.reload(true)</strong> перезагрузить всплывающее окно – это позволит проследить всю логику работы хоть с самой первой строчки.</p> <p><a href="http://lh3.ggpht.com/-eRRPi428Q-s/UwcWV_rYM-I/AAAAAAAADEQ/oHFY5gz26zs/s1600-h/script%25255B1%25255D.jpg"><img title="script" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="script" src="http://lh4.ggpht.com/-5S_y9lpohRQ/UwcWWjoqdZI/AAAAAAAADEY/cw34r8DUeYY/script_thumb%25255B1%25255D.jpg?imgmax=800" width="686" height="494"></a></p> <p>Справа можно наблюдать локальные переменные и управлять процессом выполнения.</p> <p>Все это радостно, но как быть с отладкой контекстных меню, омнибокса, фоновых и контекстных скриптов, когда они не имеют кнопки на панели инструментов? Ответы придется искать где-то <a href="https://developers.google.com/chrome-developer-tools/?hl=ru" rel="nofollow">в руководстве для разработчиков</a>.</p> <p>Подсказка – доступ к фоновой странице можно получить (открыть ее в режиме отладки) на странице <strong>chrome://extensions</strong>, а все остальные возможности (омнибокс, меню) инициализируются с нее. Также можно добраться до любой страницы расширения через его id: <strong>chrome-extensions://id/имяфайла</strong>, после чего этот файл можно открыть в отладчике через контекстное меню.</p> <p>Очень полезно внимательно просмотреть вот это видео:</p> <p><iframe height="315" src="//www.youtube.com/embed/IP0nMv_NI1s" frameborder="0" width="560" allowfullscreen></iframe></p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-47133703574482868122014-02-20T23:04:00.001-08:002014-02-20T23:04:22.633-08:00Кросс-сайтовые запросы<p>Обычные веб-страницы используют XMLHttpRequest чтобы обмениваться данными с удаленным сервером, но они ограничены <strong>same origin policy</strong>. Это правило разрешает обмен только с тем сайтом, откуда была загружена страница. Расширение браузера имеет менее строгие ограничения. Оно может обмениваться данными с любыми сайтами, указанными в свойстве <strong>permissions</strong>.</p> <a name='more'></a> <h2>Origin расширения</h2> <p>Каждое запущенное расширение работает в собственной песочнице. Без явного указания необходимых привилегий в манифесте, оно может использовать запросы XMLHttpRequest только для доступа к собственным файлам:</p><pre>var xhr = new XMLHttpRequest();<br />xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.<br />xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);<br />xhr.send();</pre><br /><p>файл config.json находится в папке config_resources, которая лежит в папке(или архиве) с расширением.</p><br /><p>Запросы к любым сайтам будут блокированы. Будут блокированы также любые другие <a href="http://chrome-ext.blogspot.ru/2014/02/blog-post_20.html">запросы к сетевым ресурсам</a> (загрузка скриптов, стилей, изображений и т.д.)</p><br /><p>Чтобы это стало можно, нужно прописать соответствующие разрешения в манифесте:</p><pre>{<br /> "name": "My extension",<br /> ...<br /> <b>"permissions": [<br /> "http://www.google.com/"<br /> ]</b>,<br /> ...<br />}</pre><br /><p>Могут быть указаны как полные адреса сайтов, так и паттерны:</p><br /><blockquote><br /><p>паттерн <a href="http://*/">http://*/</a> разрешает кроссайтовые запросы к любым сайтам. Не злоупотребляйте этой возможностью без необходимости</p></blockquote><br /><p>Паттерны похожи на те что используются для контентных скриптов, но используется только часть задающая хост, путь игнорируется.</p><br /><h2>Вопросы безопасности</h2><br /><p>В первую очередь, нам рекомендуют не использовать eval, как-то забывая о том что по умолчанию использование eval запрещено <a href="http://chrome-ext.blogspot.com/2014/02/blog-post_20.html">CSP</a>. Видимо это осталось с тех пор, когда использовался манифест первой версии. Если вы разрабатываете расширение сейчас, и используете рекомендованную версию манифеста, 2ую, то eval у вас просто не выполнится.</p><br /><p>Второй момент – не вставляйте полученные данные как готовый HTML код, через innerHTML. От сторонних сайтов вы должны получать только данные, а разметку формировать сами. Но опять же, CSP запрещает загрузку любых внешних ресурсов и использование встроенных скриптов. И вредоносный код вставленный через innerHTML просто не будет работать. Так что актуальность этого совета тоже под большим вопросом. Однако, все же лучше придерживаться подобной практики – это формирует правильный подход к разработке.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-35704287691130418032014-02-20T08:03:00.001-08:002014-02-20T08:07:16.671-08:00Политика безопасности<p>Content Security Policy (<a href="http://en.wikipedia.org/wiki/Content_Security_Policy">CSP</a>) — стандарт, определяющий HTTP-заголовки Content-Security-Policy и Content-Security-Policy-Report-Only. Эти заголовки сообщают браузеру с какими хостами он может работать. Белый список, черный список.</p> <p>Поддерживается всеми современными браузерами.</p> <p>Т.е. если на загруженной странице вдруг затесались ссылки на сайты, работа с которыми не разрешена, то загружаться они не будут. Картинки, скрипты, фреймы – все это будет заблокировано. Это значит что даже если злоумышленник прорвал первый рубеж обороны и как-то внедрил свой злонамеренный код, то до HTTP-заголовков добраться намного сложнее. Более того, браузер немедленно уведомит веб-сервер о нарушении.</p> <p>И этот же стандарт вполне применим для расширений. Он дополняет механизм <a href="http://chrome-ext.blogspot.com/2014/02/blog-post_19.html">permissions</a>, который позволяет явно указывать какими возможностями может пользоваться расширение.</p> <a name='more'></a> <p>Можно явно указать правила безопасности для расширения, но в большинстве случаев годятся те что прописаны по умолчанию. Чтобы они использовались, в манифесте должна быть указана версия манифеста 2. Вот так они выглядят:</p><pre>script-src 'self'; object-src 'self'</pre><br /><p>не слишком понятно? Попробуем разобраться. </p><br /><p>Это правило накладывает три ограничения:</p><br /><p>1. Запрещено явное и неявное использование функции <strong>eval</strong>. Нельзя каким-либо образом передать исполняемому скрипту текст чтобы он интерпретировал и выполнил его как скрипт. </p><br /><p>Это наиболее часто используемая уязвимость и по умолчанию она закрыта. У этого есть один явный недостаток – программы шифрования исходного JavaScript кода не могут функционировать без этой возможности. </p><br /><p>2. Запрещено использование встроенного JavaScript. Мухи отдельно, котлеты отдельно. Вся логика должна быть в js файлах, а в html – только html, никаких скриптов.</p><br /><p>3. Расширением загружаются только локальные скрипты и ресурсы.</p><br /><p>Хотите использовать jQuery? Пожалуйста. Но включите его в комплект. Никаких CDN и т.п. Таким образом обеспечивается гарантия, что ни вы, ни кто-то другой, не подменит входящий в расширение скрипт другим. </p><br /><blockquote><br /><p>Как это можно было бы сделать? Давайте рассмотрим такую ситуацию: провайдер может перенаправить запросы DNS на свой сервер, который можно настроить таким образом, чтобы для заданных сайтов он возвращал неправильные адреса – перенаправлял вас на фальшивки. Таким образом jQuery или Bootstrap у вас будет загружаться не с гугловского CDN, а с сервера ФСБ. Слегка модифицированный.</p><br /><p>Ну ладно, ФСБ этим заниматься не станет – у них есть другие методы получения информации. А вот админ вашего провайдера вполне может такую штуку провернуть, хотя бы и от скуки. А также от желания добыть имена и пароли от вашего банк-клиента и данные кредиток, которыми вы рассчитываетесь на е-бэе.</p></blockquote><br /><blockquote><br /><p>Не верите что такое возможно? Если у вас стоит Windows</p></blockquote><br /><ul><br /><ul><br /><li>найдите файл hosts (с:\windows\system32\drivers\etc\hosts)</li><br /><li>введите в командной строке ping yandex.ru, запомните IP адрес на который будет ломится ваш пинг</li><br /><li>откройте hosts и добавьте строчку: IPяндекса www.google.com</li><br /><li>сохраните и набирайте в браузере www.google.com</li></ul></ul><br /><blockquote><br /><p>Возможно вас это удивит, но попадете вы на яндекс, хотя в адресной строке будет гугл. Вот примерно также, легко и просто ваш провайдер может подменить один сайт другим. И если яндекс от гугла вы отличите сразу, то если злоумышленник позаботится скопировать дизайн и поведение оригинала… то поделку отличить будет невозможно.</p><br /><p>P.S. Не забудьте вернуть все на место <img class="wlEmoticon wlEmoticon-winkingsmile" style="border-top-style: none; border-left-style: none; border-bottom-style: none; border-right-style: none" alt="Подмигивающая рожица" src="http://lh5.ggpht.com/-3CjJSgmB1_Q/UwYoMdVlRPI/AAAAAAAADDw/fIk9xpz178s/wlEmoticon-winkingsmile%25255B2%25255D.png?imgmax=800"></p></blockquote><br /><p>Что-то я отвлекся :)</p><br /><p>Собственно это практически все. Ограничения можно ужесточить, или наоборот ослабить, но большого смысла в этом нет. Eval, встроенные скрипты и локальные ресурсы – это все что вам нужно помнить о политике безопасности расширений.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-83069593823145755222014-02-20T06:13:00.001-08:002014-02-20T06:13:58.915-08:00a11y<p align="right">a11y = A[ccessibilit]y (доступность)</p> <p align="justify">Очень характерно, что страница википедии посвященная вопросу “Как сделать компьютеры доступнее для людей с ограниченными возможностями” есть только на английском и некоторых других языках. И начисто отсутствует на русском. Российские слабослышащие, слабовидящие, люди с нарушениями двигательных функций никому кроме себя у нас не нужны. Переход через дорогу, магазин, или программное обеспечение, даже больницы – все у нас проектируется в расчете на здоровых и сильных, и пусть радуются что вообще хоть что-то делается. А по правде сказать – ничего у нас вообще не проектируется <em>для удобства людей</em>. Просто здоровым и сильным это не так заметно.</p> <p align="justify">Рассмотрим, как же эта тема касается разработки расширений для браузера Chrome</p> <a name='more'></a> <p align="justify">Есть множество способов облегчить использование браузера людям с ограниченными возможностями. Некоторые из них могут оказаться полезными и тем кто полностью здоров. Они ускоряют и упрощают работу, делают возможной работу в необычной обстановке – одной рукой, без мыши, или без клавиатуры, в условиях недостаточной или наоборот, избыточной освещенности, шума и т.д. и т.п.</p> <p align="justify">Сложно угадать что именно потребуется, но следование простым правилам упростит адаптацию вашего расширения к любым нестандартным потребностям и ситуациям.</p> <h2 align="justify">Используйте элементы интерфейса с поддержкой a11y</h2> <p align="justify">Стандартные элементы HTML форм давно уже разрабатываются с учетом требований доступности. Они легко масштабируются, управляются с помощью клавиатуры и нормально распознаются специальными устройствами чтения. Не злоупотребляйте нестандартными элементами. Да, иногда можно сделать красиво и оригинально, но лучше сделать просто и удобно.</p> <p align="justify">Если же вы разрабатываете что-то свое, нестандартное, то стоит познакомиться со спецификацией ARIA, это стандарт консорциума W3C. Самое простое что можно сделать, это добавить в свою разметку HTML указание о назначении ее элементов. Вот так:</p><pre><div role="toolbar" tabindex="0" aria-activedescendant="button1"><br /> <img src="buttoncut.png" role="button" alt="cut" id="button1"><br /> <img src="buttoncopy.png" role="button" alt="copy" id="button2"><br /> <img src="buttonpaste.png" role="button" alt="paste" id="button3"><br /></div></pre><br /><p>Из такой разметки специальное устройство или программа может понять что перед нами панель инструментов, с тремя кнопками, причем не обязательно их видеть чтобы понять назначение – изображения на кнопках дублируются кратким текстовым описанием.</p><br /><h2>Доступ с клавиатуры</h2><br /><p>Представьте что вы не можете использовать мышь или другое подобное устройство. Возможно ли использование вашего расширения, когда есть только клавиатура?</p><br /><h2>Фокус</h2><br /><p>Логичная передача фокуса, возможность сделать это с помощью горячей клавиши очень важна не только для людей с нарушениями моторики. Обратите внимание, соответствует ли расположение элементов на экране порядку в котором передается фокус, все ли необходимые элементы доступны таким образом? Хорошо ли видно где находится фокус?</p><br /><h2>Горячие клавиши</h2><br /><p>Они заданы? Вы используете стандартные, привычные пользователю сочетания? Есть подсказки, какие клавиши можно использовать?</p><br /><h2>Доступный контент</h2><br /><p>Хороший способ проверить гибкость вашего пользовательского интерфейса – увеличить размер текста на 200%. Удобно? Все помещается, верстка не съехала? Если так, вам плюс в карму и есть шансы, что следующую жизнь вы проведете не баобабом.</p><br /><p>Крайне нежелательно делать текст картинкой. Простой текст легко и неограниченно масштабируется, может быть выведен разными цветами и даже преобразован в шрифт Брайля, или прочитан. Возможности преобразования картинки намного хуже.</p><br /><h2>Цвет</h2><br /><p>Минимум из того что можно сделать – предоставить пользователю возможность выбрать подходящую тему. Наряду с обычной должна быть тема с повышенной контрастностью.</p><br /><h2>Звук, видео, картинки</h2><br /><p>Любая существенная информация должна дублироваться в текстовом виде. Подписи к картинкам, транскрипция для аудио и видео записей. Кроме всего прочего, это бывает очень удобно. Пробежать глазами текст трехчасовой лекции можно гораздо быстрее чем за три часа. Найти в тексте нужный фрагмент тоже быстрее и проще.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-37621321898864445222014-02-20T04:42:00.001-08:002014-02-20T04:42:25.141-08:00activeTab<p>Дает временный доступ к текущей вкладке, когда пользователь явно вызывает для нее расширение: </p> <ul> <li>путем нажатия иконки <a href="http://chrome-ext.blogspot.com/2014/02/browser-actions.html">browser action</a></li> <li><a href="http://chrome-ext.blogspot.com/2014/02/page-actions.html">page action</a></li> <li>вызова <a href="http://chrome-ext.blogspot.com/2014/02/blog-post_18.html">контекстного меню</a>. </li> <li>через горячую клавишу</li> <li>через <a href="http://chrome-ext.blogspot.com/2014/02/omnibox.html">omnibox</a></li></ul> <p>Временный – значит до тех пор пока вкладка не будет закрыта, или пока в ней не будет перехода на другую страницу.</p> <p>Хорошо тем, что пользователю не выдается никаких предупреждений и при этом он сам решает активировать расширение для конкретной страницы или нет. </p> <a name='more'></a> <h2>Пример</h2><pre>{<br /> "name": "Page Redder",<br /> "version": "2.0",<br /> "permissions": [<br /> "activeTab"<br /> ], <br /> "background": {<br /> "scripts": ["background.js"],<br /> "persistent": false<br /> }, <br /> "browser_action": {<br /> "default_title": "Make this page red"<br /> }, <br /> "manifest_version": 2<br />}</pre><br /><h3>background.js</h3><pre>chrome.browserAction.onClicked.addListener(function(tab) {<br /> console.log('Turning ' + tab.url + ' red!');<br /> chrome.tabs.executeScript({<br /> code: 'document.body.style.backgroundColor="red"'<br /> }); <br />});</pre><br /><h2>Что позволяет</h2><br /><ul><br /><li>Вызывать <strong>tabs.executeScript</strong> и <strong>tabs.insertCSS</strong></li><br /><li>получать URL, заголовок, иконку текущей страницы</li></ul> Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2994147035152452645.post-51003139069654740732014-02-20T03:28:00.001-08:002014-02-20T22:48:17.676-08:00Content ScriptsЭто JavaScript выполняемый в контексте веб-страницы. Используя DOM он может получать данные из этой страницы или модифицировать ее. <br />
<a name='more'></a>Примеры того что может контентный скрипт:<br />
<ul>
<li>найти ключевые слова на странице и преобразовать их в гиперссылки </li>
<li>найти разметку <a href="http://ru.wikipedia.org/wiki/%D0%9C%D0%B8%D0%BA%D1%80%D0%BE%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82" rel="nofollow">микроформата</a> и извлечь информацию </li>
<li>изменить стиль страницы – увеличить шрифты, сделать их более контрастными, убрать рекламные блоки и т.д.</li>
</ul>
Однако есть ограничения. Контентный скрипт не может<br />
<ul>
<li>использовать chrome.* API </li>
<li>использовать переменные или функции определенные на странице или другими контентными скриптами </li>
<li>использовать переменные или функции определенные на других страницах расширения</li>
</ul>
Но такой скрипт может обмениваться сообщениями с расширением и делать все перечисленное косвенным образом. Он также может выполнять кросс-сайтовые запросы – те что прописаны в разрешениях расширения и взаимодействовать со скриптами на странице через DOM.<br />
<h2>
Манифест</h2>
<pre>{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"js": ["jquery.js", "myscript.js"]
}
],
...
}</pre>
<br />
Есть альтернативный способ вставить свой код : <br />
<pre>{
"name": "My extension",
...
"permissions": [
"tabs", "http://www.google.com/*"
],
...
}</pre>
<br />
Используя свойство content_scripts можно вставлять много скриптов. Каждый элемент массива может содержать следующие свойства:<br />
<table><tbody>
<tr><th width="143">Имя</th><th width="106">Тип</th><th width="581">Описание</th></tr>
<tr><td width="143"><strong>matches</strong></td><td width="106">array of strings</td><td width="581"><em>Обязательно. Указывает на какие страницы будет вставляться контент-скрипт</em>.</td></tr>
<tr><td width="143"><strong>exclude_matches</strong></td><td width="106">array of strings</td><td width="581"><em>Опционально.</em> Исключения – на какие страницы скрипт вставляться не будет.</td></tr>
<tr><td width="143"><strong>css<strong></strong></strong></td><td width="106">array of strings</td><td width="581"><em><em>Опционально</em>.</em> Список файлов CSS, которые будут подключаться к страницам. Подключаются в порядке объявления.</td></tr>
<tr><td width="143"><strong>js<strong></strong></strong></td><td width="106"><nobr>array of strings</nobr></td><td width="581"><em><em>Опционально</em>.</em> Список файлов JavaScript, которые будут подключаться к страницам. Подключаются в порядке объявления.</td></tr>
<tr id="run_at"><td width="143"><strong>run_at<strong></strong></strong></td><td width="106">string</td><td width="581"><br />
<em><em>Опционально</em>.</em> Задает когда js файлы будут подключаться:<br />
<br />
<ul><br />
<li>document_start до того как будут запущены любые другие скрипты </li>
<li>document_end когда сам документ загружен и его скрипты запущены, но до того как загрузятся картинки и фреймы </li>
<li>document_idle – после события window.onload</li>
</ul>
<br />
Какой вариант лучше зависит от того как долго загружается документ (на самом деле лучше – значение по умолчанию). По умолчанию document_idle </td></tr>
<tr><td width="143"><strong>all_frames<strong></strong></strong></td><td width="106">boolean</td><td width="581"><em><em>Опционально</em>.</em> Будет ли скрипт загружаться во всех фреймах или только в главном. <br />
По умолчанию false – скрипт запускается только в главном фрейме.</td></tr>
<tr><td width="143"><strong>include_globs</strong></td><td width="106">array of string</td><td width="581"><em><em>Опционально</em>.</em> Применяется после <strong>matches</strong> чтобы скрипт подключался только для URL которые входят сюда. Эмулирует поведение <strong>@include</strong> в <a href="http://ru.wikipedia.org/wiki/Greasemonkey" rel="nofollow">Greasemonkey</a>. .</td></tr>
<tr><td width="143"><strong>exclude_globs</strong></td><td width="106">array of string</td><td width="581"><em><em>Опционально</em>.</em> Аналогичным образом эмулирует <strong>@exclude</strong>.</td></tr>
</tbody></table>
<br />
<h2>
Паттерны</h2>
<br />
Скрипт подключается если он входит в <strong>matches</strong> или <strong>include_globs</strong> и при этом не входит в <strong>exclude_matches</strong> или <strong>exclude_globs</strong>. <strong>matches</strong> – обязательное свойство, прочие используются только чтобы уточнить к каким страницам скрипт будет подключаться.<br />
<br />
Пусть в matches задано <strong>[http://*.nytimes.com/*]</strong><br />
<br />
<ul><br />
<li>Если <strong>exclude_matches</strong> <strong>["*://*/*business*"]</strong>, тогда скрипт подключится к "http://www.nytimes.com/health" но не к "http://www.nytimes.com/business". </li>
<li>Если <strong>include_globs</strong> <strong>["*nytimes.com/???s/*"]</strong>, тогда он подключится к "http:/www.nytimes.com/arts/index.html" и "http://www.nytimes.com/jobs/index.html" но не к "http://www.nytimes.com/sports/index.html". </li>
<li>Если <strong>exclude_globs</strong> is <strong>["*science*"]</strong>, тогда скрипт подключится к "http://www.nytimes.com" но не к "http://science.nytimes.com" или "http://www.nytimes.com/science".</li>
</ul>
<br />
Вопрос на засыпку: подключится ли скрипт http://www.nytimes.com/jobs/business.html ?<br />
<br />
Свойства <strong>include_globs </strong>и <strong>exclude_globs </strong>имеют более расширенный синтаксис по сравнению с <strong>matches</strong> и <strong>exclude_matches. *</strong> означает произвольную последовательность символов, включая пустую строку, а знак вопроса – одиночный символ.<br />
<br />
<h2>
Программное внедрение скриптов</h2>
<br />
Контент-скрипт можно внедрить на страницу программно – когда логика определения страницы куда необходимо внедрить скрипт выходит за рамки паттернов. Для этого требуется разрешение кросс-скриптинга в отношении этого сайта и на работу с вкладками, или “activeTab” – для внедрения в любой сайт в текущей вкладке. Выше был приведен примеры манифеста который позволяет внедрять контентные скрипты на сайт гугла. Первый пример – статическое внедрение, скрипт подключается всегда, второй – программное.<br />
<br />
Делается это методами<br />
<br />
<ul><br />
<li>tabs.executeScript </li>
<li>tabs.insertCSS</li>
</ul>
<br />
Пример:<br />
<pre>chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript({
code: 'document.body.style.backgroundColor="red"'
});
});</pre>
<br />
При нажатии на иконку в панели инструментов, в текущую открытую страницу будет внедрен скрипт меняющий цвет фона на красный.<br />
<br />
Обычно удобнее хранить скрипт в файле, метод executeScript позволяет это:<br />
<pre>chrome.tabs.executeScript(null, {file: "content_script.js"});</pre>
<br />
<h2>
Среда исполнения</h2>
<br />
Контентные скрипты выполняются в специальном, изолированном, окружении. Они имеют доступ к DOM страницы, куда внедрен скрипт, но не к переменным и скриптам этой страницы. Выглядит это так, будто скрипт работает на странице в гордом одиночестве. То же верно и в обратную сторону – скрипты на странице не видят внедренный. Хотя иногда это неудобно – хотелось бы вмешиваться в процессы происходящие на странице более агрессивно, но в общем случае это защищает нас от жуткой путаницы:<br />
<br />
<blockquote>
<br />
Что будет, если контент скрипт загружает одну версию популярного js-фреймворка, а страница – другую? При этом имена многих функций и переменных совпадают, но отличаются количеством параметров и реализацией.<br />
<br />
Но все же, лучше бы такую возможность, подключаемую отдельной опцией манифеста, сделали.</blockquote>
<br />
Вы можете назначить собственные обработчики событий – они будут работать параллельно с родными. Исполняются обработчики в том порядке, в каком они были назначены.<br />
<br />
Но взаимодействие возможно через DOM. Проще говоря, если скрипт на странице знает о существовании расширения, они могут обмениваться командами через механизм сообщений.<br />
<br />
ContentScript.js<br />
<pre>var port = chrome.runtime.connect();
window.addEventListener("message", function(event) {
// We only accept messages from ourselves
if (event.source != window)
return;
if (event.data.type && (event.data.type == "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);</pre>
<br />
Page.html<br />
<pre>document.getElementById("theButton").addEventListener("click",
function() {
window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);</pre>
<br />
В этом примере вебстраница назначает обработчик нажатия кнопки, который пошлет сообщение контентному скрипту, а тот отправит его дальше, расширению (background или event странице).<br />
<br />
<h2>
Безопасность</h2>
<br />
<ul><br />
<li>Будьте аккуратны с кросс-сайтовым скриптингом. Если вы берете какой-то контент на стороннем сайте и внедряете его на текущую страницу, то ответственность за безопасность лежит на вас. Особенно когда речь идет о https страницах, на которых, предположительно, может оказаться конфиденциальная информация. </li>
<li>Хотя ваш скрипт изолирован от скриптов страницы, все же следует принимать меры предосторожности, в частности не использовать eval, а передавать только данные в формате JSON.</li>
</ul>
<br />
<h2>
Обращение к файлам расширения</h2>
<br />
Выполняется следующим образом:<br />
<pre><em>//Code for displaying <extensionDir>/images/myimage.png:</em>
var imgURL = <b>chrome.extension.getURL("images/myimage.png")</b>;
document.getElementById("someImage").src = imgURL;</pre>
Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-2994147035152452645.post-60197843209652322502014-02-19T21:53:00.001-08:002014-02-19T21:53:26.512-08:00Страницы управляемые событиями (Event Pages)<p>Это разновидность фоновых (Background) страниц, которые не хранятся в памяти постоянно, а загружаются только когда возникают связанные с ними события. Есть еще фоновые страницы, которые <a href="http://chrome-ext.blogspot.com/2014/02/background-pages.html">хранятся в памяти постоянно</a> (persistent). Хотя и те и другие “фоновые”, но обычно, если в документации речь идет про фоновые, то имеются ввиду постоянные. </p> <p>Гугл рекомендует разработчикам переносить свои наработки на event-страницы, что как бы намекает, что в один прекрасный день останутся только они.</p> <a name='more'></a> <h2>Манифест</h2><pre>{<br /> "name": "My extension",<br /> ...<br /> "background": {<br /> "scripts": ["eventPage.js"],<br /> "persistent": false<br /> },</pre><br /><p>По умолчанию свойство persistent = true и фоновая страница является постоянной.</p><br /><h2>Жизненный цикл</h2><br /><p>Страница типа event загружается при необходимости и уничтожается когда она не нужна. Строго говоря – не обязательно. Если быть точным, браузер может освободить занимаемые ей ресурсы при необходимости. А может и не освобождать если таковой не возникло. Логика может быть достаточно изощренной, например учитывать как часто возникают события связанные со страницей, но нам она неизвестна. Страница загружается когда:</p><br /><ul><br /><li>Приложение(или расширение) установлено, или обновилось на новую версию. При этом регистрируются обработчики событий <br /><li>Возникло событие <br /><li>Странице отправлено сообщение контентным скриптом или другим расширением <br /><li>Другой элемент расширения, например всплывающее окно вызвало метод <strong>runtime.getBackgroundPage</strong>.</li></ul><br /><p>Будучи загруженной, страница остается в памяти пока она активна. Например, пока ожидает результатов вызовов API или сетевых запросов. Она также остается загруженной, пока активны видимые элементы расширения (всплывающие окна) или каналы сообщений. Заметьте – открытие всплывающего окна не обязательно приводит к загрузке event-страницы, но препятствует ее закрытию. Вы можете наблюдать как страница загружается и когда освобождается в диспетчере задач браузера. Кстати, заглянув туда первый раз вы охренеете, сколько памяти жрут казалось бы простые странички.</p><br /><p>Если страница неактивна несколько секунд, возникает событие <strong>runtime.onSuspend</strong>. После этого у страницы есть еще несколько секунд, если за это время что-то успеет случится, возникает событие <strong>runtime.onSuspendCanceled</strong>.</p><br /><p>Нам эти детали в общем-то не должны быть интересны</p><br /><h2>Регистрация событий</h2><br /><p>Когда страница загружается, она регистрирует обработчики событий вызовом <strong>addListener</strong>. Если она удалит их с помощью <strong>removeListener</strong> – страница будет выгружена из памяти. При каждой загрузке страницы обработчики событий регистрируются заново</p><br /><p>Если нужно выполнить какую-то инициализацию при установке расширения, то для этого необходимо регистрировать обработчик события <strong>runtime.onInstalled</strong>.</p><br /><h2>Переход от использования постоянных страниц к event-страницам</h2><br /><ol><br /><li>Добавляем свойство “persistent” : false в манифесте</li><br /><li>Если вы используете window.setTimeout() или window.setInterval(), заменяем их на вызовы <strong>chrome.alarms API</strong>. Не забываем, что использование этого API требует разрешения <strong><a href="http://chrome-ext.blogspot.com/2014/02/blog-post_19.html">alarms</a></strong></li><br /><li>Аналогично, все прочие асинхронные вызовы должны заменяться на вызовы chrome API</li><br /><li>Вызовы <strong>extension.getBackgroundPage</strong> необходимо заменить на <strong>runtime.getBackgroundPage</strong>. Разница между ними в том, что второй является асинхронным – страницу ведь надо еще загрузить.</li></ol><br /><h2>Советы</h2><br /><ul><br /><li>Регистрируйте все обработчики которые могут быть интересны вашему расширению. Страница загружается один раз, когда расширение устанавливается. Потом она будет загружена только при возникновении зарегистрированного события.</li><br /><li>Если вам нужно провести какую-то инициализацию после установки расширения, то добавьте обработчик события <strong>runtime.onInstalled</strong>. Это подходящее место для того чтобы задать правила <strong>declarativeWebRequest</strong>, или создать контекстное меню.</li><br /><li>Используйте storage API или IndexedDB, если вам нужно что-то хранить</li><br /><li>Фильтруйте события</li><br /><li>Ставьте обработчик на <strong>runtime.onSuspend</strong>, если хотите что-то почистить перед тем как страница будет выгружена из памяти (на мой взгляд – плохая идея). Но вместо этого гугл советует периодически сохранять данные, иначе, если расширение внезапно накроется медным тазом, не успев получить <strong>runtime.onSuspend</strong>, то ваши данные могут пропасть. А еще лучше – в принципе не храните важных данных в переменных скрипта.</li><br /><li>Если вы обрабатываете сообщения, не оставляйте открытыми неиспользуемые порты. Это будет препятствовать звкрытию страницы</li><br /><li>Если вы используете контекстное меню, не используйте параметр <strong>onclick</strong>, а передавайте строковый параметр id и анализируйте его в обработчике <strong>contextMenus.onClicked</strong></li><br /><li>Типичные ошибки</li><br /><ul><br /><li>выполнение ненужной работы при каждой загрузке страницы (например контекстное меню можно создать один раз – при установке)</li><br /><li>Повторная установка алармов при каждой загрузке – они сбрасываются и начинают считать с начала.</li><br /><li>Забытые обработчики событий: некоторые события могут быть обработаны двояко – путем создания обработчика или путем передачи callback-функции. Второй способ для event-страницы череват проблемами.</li></ul></ul> Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2994147035152452645.post-10016809094035270282014-02-19T04:40:00.001-08:002014-02-19T04:42:46.058-08:00Руководство разработчика<p>Прежде всего, прочитайте <a href="http://chrome-ext.blogspot.com/2014/02/blog-post.html">Быстрый старт</a> и <a href="http://chrome-ext.blogspot.com/2014/02/blog-post_17.html">Обзор возможностей</a>.</p> <a name='more'></a> <h2>Изменение внешнего вида браузера</h2> <ul> <li><a href="http://chrome-ext.blogspot.com/2014/02/browser-actions.html">Browser Actions</a> добавление кнопки на панель инструментов <li><a href="http://chrome-ext.blogspot.com/2014/02/rich-notifications.html">Desktop Notifications</a> уведомление пользователя о важных событиях <li><a href="http://chrome-ext.blogspot.com/2014/02/omnibox.html">Omnibox</a> добавление ключевых слов в адресную строку <li><a href="http://chrome-ext.blogspot.com/2014/02/blog-post_6971.html">Options Pages</a> настройки расширения <li><a href="http://chrome-ext.blogspot.com/2014/02/override-pages.html">Override Pages</a> подмена стандартных страниц браузера, таких как новая вкладка <li><a href="http://chrome-ext.blogspot.com/2014/02/page-actions.html">Page Actions</a> добавление временной кнопки внутри адресной строки <li><a href="http://chrome-ext.blogspot.com/2014/02/blog-post_3638.html">Themes</a> изменение внешнего вида браузера</li></ul> <h2>Другие способы взаимодействия с браузером</h2> <ul> <li>Bookmarks создание, упорядочивание и прочие манипуляции с закладками <li>Cookies операции с куками <li>Developer Tools добавление своих возможностей в инструменты разработчика <li>Events работа с событиями <li>History взаимодействие с историей посещений <li>Tabs управления вкладками <li>Windows управление окнами</li></ul> <h2>Внутренняя реализация</h2> <ul> <li>Accessibility (a11y) сделайте свое расширение удобным для людей с ограниченными возможностями <li>Event Pages поместите весь общий код расширения в одно место <li>Content Scripts запуск скриптов в контексте веб-страницы <li>Cross-Origin XHR использование XMLHttpRequest для <li>Internationalization локализация <li>Message Passing взаимодействие через сообщения <li>Optional Permissions права доступа <li>NPAPI Plugins загрузка нативного бинарного кода</li></ul> <h2>Релиз и дистрибуция</h2> <ul> <li>Autoupdating автоматическое обновление расширения <li>Hosting размещение на серверах гугла или собственном <li>Other Deployment Options распространение расширения в составе другого ПО, или через собственную сеть (не через Google Store) <li>Packaging создание <code>.crx</code> файлов</li></ul> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-10069873681450546492014-02-19T04:35:00.001-08:002014-02-19T04:35:38.186-08:00Темы<p><a href="http://lh5.ggpht.com/-tjhD6JJWPxk/UwSlFZJ3cvI/AAAAAAAADDY/KuVQQD_PMCA/s1600-h/2.jpg"><img title="2" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: left; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="2" align="left" src="http://lh5.ggpht.com/-vh9Lg6ZlbXI/UwSlF2kJwQI/AAAAAAAADDg/48BaU4BUf2I/2_thumb.jpg?imgmax=800" width="244" height="154"></a>Тема – это расширение особого сорта, которое изменяет внешний вид браузера. Они не содержат HTML или скриптов</p><br clear="all"> <a name='more'></a> <h2>Манифест</h2><pre>{<br /> "version": "2.6",<br /> "name": "camo theme",<br /> "theme": {<br /> "images" : {<br /> "theme_frame" : "images/theme_frame_camo.png",<br /> "theme_frame_overlay" : "images/theme_frame_stripe.png",<br /> "theme_toolbar" : "images/theme_toolbar_camo.png",<br /> "theme_ntp_background" : "images/theme_ntp_background_norepeat.png",<br /> "theme_ntp_attribution" : "images/attribution.png"<br /> },<br /> "colors" : {<br /> "frame" : [71, 105, 91],<br /> "toolbar" : [207, 221, 192],<br /> "ntp_text" : [20, 40, 0],<br /> "ntp_link" : [36, 70, 0],<br /> "ntp_section" : [207, 221, 192],<br /> "button_background" : [255, 255, 255]<br /> },<br /> "tints" : {<br /> "buttons" : [0.33, 0.5, 0.47]<br /> },<br /> "properties" : {<br /> "ntp_background_alignment" : "bottom"<br /> }<br /> }<br />}</pre><br /><h3>colors</h3><br /><p align="justify">задает цвета различных элементов. Названия элементов можно найти где-то в исходниках chrome. Англоязычный <a href="https://developer.chrome.com/extensions/themes" rel="nofollow">оригинал</a> ссылается на <a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc" rel="nofollow">theme_service.cc</a>, однако там ничего нет – видимо все константы давно вынесли туда, где им и полагаетс быть – в *.h файлы. При большом желании их можно найти, взяв за отправную точку поиска указанный файл.<br /><h3>images</h3><br /><p>картинки должны входить в расширение. Возможные названия элементов нужно искать там же где и для цветов. Они должны начинаться с префикса “IDR_”, его необходимо отбросить и сконвертировать полученное название в нижний регистр.<br /><h3>properties</h3><br /><p>Задает параметры выравнивания, повторения и т.д. для фоновых картинок. За точными названиями этих параметров нас опять же отсылают ковыряться в исходниках<br /><h3><font style="font-weight: normal">tints</font></h3><br /><p>Задает изменение оттенков для отдельных частей интерфейса, таких как кнопки, фреймы и т.д. Google Chrome выделяет их оттенками, а не фоновыми картинками по каким-то глубоким, недоступным мне соображениям. За точными названиями параметров нас отправляют все в тот же файл <a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/themes/theme_service.cc" rel="nofollow">theme_service.cc</a>.<br /><p>Задается в формате HSL (тон, насыщенность и светлота), вещественными числами в диапазоне 0 - 1.0:<br /><blockquote><br /><p>Что такое HSL и с чем его едят <a href="http://ru.wikipedia.org/wiki/HSL" rel="nofollow">можно почитать в википедии</a>.</p></blockquote><br /><p>Можно использовать значение –1.0, которое означает отсутствие изменений.<br /><p><a href="http://code.google.com/p/chromium/wiki/ThemeCreationGuide" rel="nofollow">Руководство по созданию тем (англ)</a>. Здесь несколько больше информации, в частности приведены все те параметры которые вы безуспешно искали в исходниках и что они контролируют.</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-64828592581361234232014-02-19T03:11:00.001-08:002014-02-19T03:58:59.447-08:00Page Actions<p>Внешне не так уж сильно отличается от <a href="http://chrome-ext.blogspot.com/2014/02/browser-actions.html">Browser Actions</a>.</p> <p><a href="http://lh3.ggpht.com/-bRDvksaicuo/UwSRZrHgXII/AAAAAAAADDA/vGaK1mYJ6yg/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: none; padding-top: 0px; padding-left: 0px; margin-left: auto; display: block; padding-right: 0px; border-top-width: 0px; margin-right: auto" border="0" alt="image" src="http://lh6.ggpht.com/-PTYVmmvPp2o/UwSRaa0EDFI/AAAAAAAADDI/LqlT6JFowFw/image_thumb%25255B1%25255D.png?imgmax=800" width="398" height="68"></a></p> <p>Иконка расширения помещается не на панель инструментов, а в адресную строку. И выводится не на кадой вкладке, а только там где, в данном случае, обнаружен RSS. Вот и все.</p> <p>Давайте копнем глубже:</p> <a name='more'></a> <h2>Манифест</h2><pre>{<br /> "name": "My extension",<br /> ...<br /> "page_action": {<br /> "default_icon": { // optional<br /> "19": "images/icon19.png", // optional<br /> "38": "images/icon38.png" // optional<br /> },<br /> "default_title": "Google Mail", // optional; shown in tooltip<br /> "default_popup": "popup.html" // optional<br /> },<br /> ...<br /> }</pre><br /><p>Точно также задается иконка, всплывающее окно и подсказка.</p><br /><h2>Отличия</h2><br /><ul><br /><li>у них нет бэджей <br /><li>кнопка по умолчанию не показывается. Чтобы она появилась ее нужно явно показать методом show <br /><li>вместо методов enable/disable – show/hide.</li></ul><br /><h1>Справочник по chrome.pageAction</h1><br /><h2>Типы</h2><br /><h3>ImageDataType</h3><br /><p>Объект ImageData object </p><br /><h2>Методы</h2><br /><h3>show</h3><br /><p>chrome.pageAction.show(integer tabId)<br>Показывает иконку на указанной вкладке</p><br /><p>Параметры<br><em>tabId</em> (integer) Идентификатор вкладки</p><br /><h3>hide</h3><br /><p>chrome.pageAction.hide(integer tabId)<br>Скрывает иконку на указанной вкладке</p><br /><p>Параметры<br><em>tabId</em> (integer) Идентификатор вкладки</p><br /><h3>setTitle</h3><br /><p>chrome.pageAction.setTitle(object details)<br>Устанавливает всплывающую подсказку к иконке</p><br /><p>Параметры<br>details (object) <br><em>tabId</em> (integer) Идентификатор вкладки<br>title (string) Строка подсказки</p><br /><h3>getTitle</h3><br /><p>chrome.pageAction.getTitle(object details, function callback)<br>Получает всплывающую подсказку к иконке</p><br /><p>Параметры<br>details (object)<br><em>tabId</em> (integer) Идентификатор вкладки<br><em>callback</em> (function) функция вида function(string result) {...}, через которую возвращается результат</p><br /><h3>setIcon</h3><br /><p>chrome.pageAction.setIcon(object details, function callback)<br>Устанавливает новую иконку. Иконка м.б. указана как путь к файлу, либо как объект ImageData. </p><br /><p>Параметры<br><em>details</em> (object)</p><br /><blockquote>Свойства <br /><ul><br /><li><em>tabId</em> (integer) Идентификатор вкладки <br /><li>imageData(опционально, ImageDataType или object) ImageData или словарь {размер -> ImageData} <br /><li>path ( опционально, string или object ) путь к файлу с картинкой или словарь {размер -> путь}</li></ul></blockquote><br /><p><em>callback</em> (function) функция вида function() {...}, вызывается когда операция завершена</p><br /><h3>setPopup</h3><br /><p>chrome.pageAction.setPopup(object details)<br>Устанавливает html файл, который будет открываться как всплывающее окно, когда пользователь кликнет по иконке.</p><br /><p>Параметры<br>details (object)</p><br /><blockquote>Свойства <br /><ul><br /><li>tabId (integer) идентификатор вкладки <br /><li>popup (string) путь к файлу. Пустая строка – значит нет всплывающего окна.</li></ul></blockquote><br /><h3>getPopup</h3><br /><p>chrome.pageAction.getPopup(object details, function callback)<br>Возвращает путь к файлу всплывающего окна.</p><br /><p>Параметры<br><em>details</em> ( object )</p><br /><blockquote><br /><p>Свойства </p><br /><ul><br /><li>tabId (integer) идентификатор вкладки</li></ul></blockquote><em>callback</em> (function) функция вида function(string result) {...}, через которую возвращается результат<br><br /><h2>События</h2><br /><h3>onClicked</h3><br /><p>Возникает когда нажата иконка page action. Не возникает если назначено всплывающее окно.</p><br /><h3>addListener</h3><br /><p>Добавляет обработчик события<br>chrome.pageAction.onClicked.addListener(function callback)<br>Параметры<br>callback (function) функция вида function( tabs.Tab tab) {...}. Вызывается при возникновении события</p> Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2994147035152452645.post-17674590189968818312014-02-19T02:23:00.001-08:002014-02-19T02:25:03.367-08:00Override Pages<p>Это способ подменить стандартные страницы браузера собственными.</p> <a name='more'></a> <p>Расширение может подменить следующие страницы:</p> <ul> <li>менеджер закладок <b>chrome://bookmarks</b> <li>история <b>chrome://history</b> <li>новая вкладка <b>chrome://newtab</b></li></ul> <p>Одно расширение может подменить только одну из этих страниц. Если вы хотите подменить две, или три, придется делать соответственно 2 или 3 независимых расширения.</p> <p>Особняком стоит режим “инкогнито” – новая вкладка не может быть перекрыта в этом режиме. Чтобы можно было перекрыть в режиме “инкогнито” две другие страницы, в манифесте для свойства <strong>incognito</strong> должно быть установлено значение “spanning”.</p> <h2>Манифест</h2><pre>{<br /> "name": "My extension",<br /> ...<br /><br /> "chrome_url_overrides" : {<br /> "pageToOverride": "myPage.html"<br /> },<br /> ...<br />}</pre><br /><p>pageToOverride – одно из следующих значений:</p><br /><ul><br /><li>bookmarks</li><br /><li>history</li><br /><li>newtab</li></ul><br /><h2>Советы</h2><br /><ul><br /><li>Не усложняйте. Эти страницы используются постоянно, с вполне определенными целями и пользователи не привыкли ждать при выполнении таких простых вещей.</li><br /><li>Не следует использовать в этих страницах запросы к сетевым ресурсам. Эти страницы должны быстро открываться и при отсутствии соединения с сетью</li><br /><li>Используйте тэг <title>History(Bookmarks/New Tab)</title> иначе будет выводится имя файла подменяющего страницу, это вводит пользователей в заблуждение</li><br /><li>Фокус должен оставаться на адресной строке когда пользователь создает новую вкладку</li></ul> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-35451010651316852172014-02-19T01:18:00.001-08:002014-02-19T01:18:26.895-08:00Разрешения<p align="right">Шаг вправо, шаг влево – побег<br>Прыжок на месте – провокация</p> <p>На каждый чих вашему расширению требуется разрешение. По умолчанию – нельзя практически ничего.</p> <p>При установке расширения пользователь уведомляется о его аппетитах и может сделать осознанный выбор (хаха 3 раза), готов ли он предоставить требуемое. Если не готов, если расширение просит слишком много, а его автор или источник получения не внушает доверия, установку можно прервать. Кроме того, в любой момент на вкладке расширений можно проверить какие разрешения затребованы расширением и отключить его, если возникли какие-то подозрения.</p> <p>На практике, большинство пользователей ставят расширения не читая списка его требований, а если вчитаться и задуматься о возможных последствиях, то придется отключить практически все, уж очень подозрительно они выглядят.</p> <a name='more'></a> <p>Знакомство со списком всех возможных разрешений полезно хотя бы тем, что он наглядно иллюстрирует потенциальные возможности – что вообще можно делать с помощью расширений</p> <p>Нужно иметь ввиду, что некоторые возможности являются экспериментальными, их распространение через Google Store ограничено и в любой момент в API могут внести изменения или даже прекратить его поддержку.</p> <table cellspacing="0" cellpadding="2" width="800" border="1"> <tbody> <tr> <td valign="top" width="181">Параметр</td> <td valign="top" width="619">Описание</td></tr> <tr> <td valign="top" width="181">[scheme]:[host]/*</td> <td valign="top" width="619">Требуется чтобы взаимодействовать с кодом на страницах, чей URL совпадает с указанной маской. Например для кросс-сайтовых запросов</td></tr> <tr> <td valign="top" width="181">activeTab</td> <td valign="top" width="619">Позволяет расширению получить временный доступ к активной вкладке. Чтобы получить доступ пользователь должен явно активировать расширение, например путем нажатия его иконки на панели инструментов.</td></tr> <tr> <td valign="top" width="181">alarms</td> <td valign="top" width="619">Разрешает доступ к <strong>chrome.alarms</strong> API – создание разного рода “будильников”, напоминаний. </td></tr> <tr> <td valign="top" width="181">background</td> <td valign="top" width="619">Разрешает работу расширения в фоновом режиме, даже когда браузер не запущен</td></tr> <tr> <td valign="top" width="181">bookmarks</td> <td valign="top" width="619">Дает доступ к закладкам</td></tr> <tr> <td valign="top" width="181">browsingData</td> <td valign="top" width="619">Позволяет работать с <strong>chrome.browsingData</strong> API – автоматически удалять старые, ненужные данные. Полезность этого API для меня под большим вопросом</td></tr> <tr> <td valign="top" width="181">clipboardRead</td> <td valign="top" width="619">Позволяет получение данных из буфера обмена (paste)</td></tr> <tr> <td valign="top" width="181">clipboardWrite</td> <td valign="top" width="619">Позволяет записывать данные в буфер обмена (cut,copy)</td></tr> <tr> <td valign="top" width="181">contentSettings</td> <td valign="top" width="619">Разрешает работу с <strong>chrome.contentSettings</strong> API. С его помощью можно разрешать или запрещать использование cookies, JavaScript, and plug-ins на сайтах.</td></tr> <tr> <td valign="top" width="181">contextMenus</td> <td valign="top" width="619">Добавление своих элементов в контекстное меню</td></tr> <tr> <td valign="top" width="181">cookies</td> <td valign="top" width="619">Управление cookies </td></tr> <tr> <td valign="top" width="181">debugger</td> <td valign="top" width="619">Может быть полезно при отладке расширения на мобильных устройствах. Отладка осуществляется только при подключении через USB кабель</td></tr> <tr> <td valign="top" width="181">declarativeContent</td> <td valign="top" width="619">Разрешает использование <strong>chrome.declarativeContent</strong> API. Довольно экзотическая штука, которая показывает иконку Page Action в зависимости от контента страницы. Например можно задать правило, по которому иконка вашего расширения будет показана только для страниц на которых присутствует тег video.<br>Экспериментальная возможность.</td></tr> <tr> <td valign="top" width="181">declarativeWebRequest</td> <td valign="top" width="619">Позволяет перехватывать, блокировать или изменять запросы “на лету”.<br>Экспериментальная возможность.</td></tr> <tr> <td valign="top" width="181">desktopCapture</td> <td valign="top" width="619">Разрешает <strong>chrome.desktopCapture</strong> API. Позволяет делать снимок экрана, отдельного окна или вкладки.<br>Экспериментальная возможность.</td></tr> <tr> <td valign="top" width="181">dns</td> <td valign="top" width="619">Разрешает <strong>chrome.dns</strong> API – возможность настолько экспериментальная что на сайте гугла нет описания</td></tr> <tr> <td valign="top" width="181">downloads</td> <td valign="top" width="619">Позволяет расширению работать с загрузками</td></tr> <tr> <td valign="top" width="181">experimental</td> <td valign="top" width="619">Разрешает работу с chrome.experimental.* API. Кроме установки этого разрешения вам потребуется специальная версия браузера, в которой разрешено использование этих специальных возможностей</td></tr> <tr> <td valign="top" width="181">fileBrowserHandler</td> <td valign="top" width="619">API для выбора файлов для загрузки, работает только на <strong>Chrome OS</strong></td></tr> <tr> <td valign="top" width="181">fontSettings</td> <td valign="top" width="619">Разрешает манипуляции со шрифтами</td></tr> <tr> <td valign="top" width="181">gcm</td> <td valign="top" width="619">Разрешает использование <strong>Google Cloud Messaging for Android</strong>. Экспериментальная возможность</td></tr> <tr> <td valign="top" width="181">geolocation</td> <td valign="top" width="619">Разрешает работу с <strong>Geolocation API</strong></td></tr> <tr> <td valign="top" width="181">history</td> <td valign="top" width="619">Доступ к истории посещений</td></tr> <tr> <td valign="top" width="181">identity</td> <td valign="top" width="619">Авторизация через OAuth2</td></tr> <tr> <td valign="top" width="181">idle</td> <td valign="top" width="619">Возможность обнаружения перехода компьютера в ждущий режим. Например чтобы выполнять какие-то ресурсоемкие задачи пока им никто не пользуется</td></tr> <tr> <td valign="top" width="181">idltest</td> <td valign="top" width="619">Что-то экспериментальное и жутко секретное. А скорей всего умершее в экспериментальном статусе</td></tr> <tr> <td valign="top" width="181">infobars</td> <td valign="top" width="619">Позволяет добавить информационную панель под ярлычками вкладок. Экспериментальное</td></tr> <tr> <td valign="top" width="181">location</td> <td valign="top" width="619">Еще один вариант геолокации. Экспериментальный и завязанный на Chrome</td></tr> <tr> <td valign="top" width="181">management</td> <td valign="top" width="619">Разрешает использование API для управления приложениями и расширениями</td></tr> <tr> <td valign="top" width="181">notifications</td> <td valign="top" width="619">Разрешает <a href="http://chrome-ext.blogspot.com/2014/02/rich-notifications.html">работу с уведомлениями</a></td></tr> <tr> <td valign="top" width="181">pageCapture</td> <td valign="top" width="619">Позволяет сохранить страницу в одном файле формата MHTML</td></tr> <tr> <td valign="top" width="181">power</td> <td valign="top" width="619">Позволяет предотвратить переход системы в спящий режим</td></tr> <tr> <td valign="top" width="181">privacy</td> <td valign="top" width="619">Позволяет менять настройки приватности</td></tr> <tr> <td valign="top" width="181">processes</td> <td valign="top" width="619">Управление процессами браузера. Экспериментальная возможность.</td></tr> <tr> <td valign="top" width="181">proxy</td> <td valign="top" width="619">Управление настройками прокси</td></tr> <tr> <td valign="top" width="181">pushMessaging</td> <td valign="top" width="619">Позволяет использовать <strong>Google Cloud Messaging</strong>.</td></tr> <tr> <td valign="top" width="181">sessions</td> <td valign="top" width="619">Экспериментальная возможность. Только для разработчиков</td></tr> <tr> <td valign="top" width="181">signedInDevices</td> <td valign="top" width="619">Экспериментальная возможность. Только для разработчиков</td></tr> <tr> <td valign="top" width="181">storage</td> <td valign="top" width="619">Разрешает <strong>chrome.storage</strong> API – аналог localStorage но несколько больше возможностей</td></tr> <tr> <td valign="top" width="181">system.cpu</td> <td valign="top" width="619">Получение информации о процессоре и загрузке системы</td></tr> <tr> <td valign="top" width="181">system.display</td> <td valign="top" width="619">Получение информации о мониторе</td></tr> <tr> <td valign="top" width="181">system.memory</td> <td valign="top" width="619">Получение информации о памяти</td></tr> <tr> <td valign="top" width="181">system.storage</td> <td valign="top" width="619">Получение информации о накопителях и их подключении/отключении</td></tr> <tr> <td valign="top" width="181">tabCapture</td> <td valign="top" width="619">Взаимодействие с потоковыми данными (аудио, видео) на вкладках. </td></tr> <tr> <td valign="top" width="181">tabs</td> <td valign="top" width="619">Какая-то особо изощренная работа со вкладками. В чем именно она заключается понять не удалось</td></tr> <tr> <td valign="top" width="181">topSites</td> <td valign="top" width="619">Получение списка наиболее посещаемых пользователем сайтов.</td></tr> <tr> <td valign="top" width="181">tts</td> <td valign="top" width="619">Преобразование текста в речь.</td></tr> <tr> <td valign="top" width="181">ttsEngine</td> <td valign="top" width="619">Продвинутое преобразование текста в речь.</td></tr> <tr> <td valign="top" width="181">unlimitedStorage</td> <td valign="top" width="619">Дает неограниченный размер хранимых данных (обычное localStorage только 5Мб). С оговорками.</td></tr> <tr> <td valign="top" width="181">webNavigation</td> <td valign="top" width="619">Получение уведомлений о событиях связанных с загрузкой данных</td></tr> <tr> <td valign="top" width="181">webRequest</td> <td valign="top" width="619">Тоже самое что webNavigation, но на более более продвинутом уровне</td></tr> <tr> <td valign="top" width="181">webRequestBlocking</td> <td valign="top" width="619">Тоже самое что webRequest + возможность прерывать, блокировать и модифицировать запросы</td></tr></tbody></table> <h2>Пример</h2><pre>"permissions": [<br /> "tabs",<br /> "bookmarks",<br /> "http://www.blogger.com/",<br /> "http://*.google.com/",<br /> "unlimitedStorage"<br />],</pre> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-73078561781950273572014-02-18T17:56:00.001-08:002014-02-18T17:56:04.360-08:00Фоновые страницы (Background Pages)<p align="justify">Судя по всему это отмирающий инструмент. В новой документации разработчика, они не входят в “Основы” и вообще отошли на второй план. Новая парадигма – использование страниц типа Event. Гугл рекомендует использовать именно их.</p> <p>Что же это такое Background Pages:</p> <a name='more'></a> <p><a href="http://lh3.ggpht.com/-uIySGpAQIPY/UwQPL4qDdOI/AAAAAAAADCo/QMKqwn6dUbE/s1600-h/image%25255B2%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: left; padding-top: 0px; padding-left: 0px; margin: 0px 20px 20px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" align="left" src="http://lh3.ggpht.com/-6MP5UvYVoXc/UwQPMoWsxwI/AAAAAAAADCw/GaINCHUc06I/image_thumb.png?imgmax=800" width="244" height="184"></a>Расширению часто требуется общий для всех вкладок скрипт, который обеспечивает их взаимодействие, хранит состояние. В конце концов, объединяет в одном месте весь используемый код, вместо того чтобы загружать его для каждой вкладки индивидуально.</p> <p align="justify">Фоновая страница – это невидимая страница, которая как раз и содержит такой скрипт. Их бывает два типа: persistent (постоянные) и event (управляемые событиями). Persistent, как можно догадаться, загружена постоянно, а event-страница загружается только при необходимости, когда возникает связанное с ней событие.</p> <p>Когда в документации упоминается background page, то речь обычно идет о persistent background page. А Event background page называют просто event page. Ниже речь пойдет про постоянные страницы.</p> <p><strong>Примечание:</strong></p> <blockquote> <p>для режима “инкогнито” может быть создана своя копия фоновой страницы, в зависимости от некоторых настроек.</p></blockquote> <p>Для взаимодействие между отдельными страницами можно использовать прямые вызовы скриптов, как это делается при взаимодействии фреймов. Метод <strong>extension.getViews</strong> возвращает список всех открытых окон для каждой страницы где активно ваше расширение, а метод <strong>extension.getBackgroundPage</strong> позволяет обратиться к фоновой странице.</p> <h2>Манифест</h2> <p>В общем случае фоновая страница не содержит никакой HTML-разметки. Зачем? Она же невидимая. Поэтому в манифесте мы указываем только скрипты которые нужно на нее загрузить, а пустой документ браузер создает автоматически. </p><pre>{<br /> "name": "My extension",<br /> ...<br /> "background": {<br /> "scripts": ["background.js"]<br /> },<br /> ...<br />}</pre><br /><p>Но можно загрузить и HTML. Например разместить там какие-то шаблоны, ну или просто вам так привычнее.</p><pre>{<br /> "name": "My extension",<br /> ...<br /> "background": {<br /> "page": ["background.html"]<br /> },<br /> ...<br />}</pre><br /><p>В некоторых случаях еще нужно указать "background" permission. Это приведет к тому что ваш браузер будет загружен постоянно, в фоновом режиме. Он загрузится сразу после включения компьютера и останется в памяти даже если вы закрыли все окна. Это необходимо, например, <a href="http://chrome-ext.blogspot.com/2014/02/rich-notifications.html">для вывода уведомлений</a>.</p><br /><h2>Пример</h2><br /><p>Демонстрирует как фоновая страница обрабатывает события и может взаимодействовать с другими страницами в расширении. У этого расширения есть одна фоновая страница и неограниченное количество страниц созданных с помощью <strong>tabs.create</strong> из файла <strong>image.html</strong>. <br /><p>В данном примере можно без проблем перейти от использования постоянной страницы к странице управляемой событиями, потому что собственно так event-страница и работает – регистрирует обработчик события, а никакой информации между отдельными вызовами не хранится, либо хранится в другом месте (в данном случае на вкладках созданных из image.html).<br /><h3>background.js</h3><pre>// Регистрируем обработчик нажатия на иконку расширения.<br />chrome.browserAction.onClicked.addListener(function(tab) {<br /> var viewTabUrl = chrome.extension.getURL('image.html');<br /> var imageUrl = /* тут мы как-то определяем URL загружаемой картинки */;<br /><br /> // Ищем – может мы уже открывали вкладку для загрузки картинок?<br /> var views = chrome.extension.getViews();<br /> for (var i = 0; i < views.length; i++) {<br /> var view = views[i];<br /><br /> // Если так и есть, но картинка там сейчас отсутствует <br /> if (view.location.href == viewTabUrl && !view.imageAlreadySet) {<br /><br /> // вызываем скрипт из этой вкладки и передаем ему адрес картинки.<br /> view.setImageUrl(imageUrl);<br /> view.imageAlreadySet = true;<br /> break; // телемаркет<br /> }<br /> }<br /> // Ничего не найдено, тут должно быть создание новой вкладки с помощью tab.create<br />});</pre><br /><h3>image.html</h3><pre><html><br /> <script><br /> function setImageUrl(url) {<br /> document.getElementById('target').src = url;<br /> }<br /> </script><br /><br /> <body><br /> <p><br /> Image here:<br /> </p><br /><br /> <img id="target" src="white.png" width="640" height="480"><br /><br /> </body><br /></html></pre> Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-2994147035152452645.post-35534822084109392362014-02-18T16:43:00.001-08:002014-02-18T16:43:17.490-08:00Перемен к лучшему не бывает<p align="justify">Еще вчера документация по разработке расширений для Chrome выглядела иначе. Справа было оглавление и из любого документа можно было перейти к любой части документации. А также составить общее представление, что же там собственно есть.</p> <p align="justify">Сегодня с утра гугол преподнес сюрприз. Оглавление разделилось на две части и переехало вверх и влево. И стало гораздо менее подробным. Некоторые разделы оттуда исчезли и для меня неочевидно где же теперь их искать (и можно ли вообще найти).</p> <p align="justify">Осталась только очень поверхностная информация, на уровне “Hello world” и справочная информация по API, без разъяснения как и для чего им можно пользоваться. Исчезли как раз статьи, популярно разъясняющие все возможности.</p> <p align="justify">В пору стать конспирологом и предположить что таким нехитрым фортелем гугл хочет ограничить количество разработчиков, увеличив сложность изучения данного предмета.</p> <p align="justify">Хотя скорей всего все банальнее – хотели сделать как лучше, а получилось как обычно</p> Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-2994147035152452645.post-65221503300909793842014-02-18T08:37:00.001-08:002014-02-18T08:37:19.121-08:00Страницы настроек<p>Чтобы пользователь мог настроить ваше расширение нужна страница где он будет задавать все настройки. Если вы ее создадите, то она будет доступна по ссылке на странице управления расширениями chrome://extensions.</p> <a name='more'></a> <p>Первым делом нужно указать ее в манифесте</p><pre>{<br /> "name": "My extension",<br /> ...<br /> "options_page": "options.html",<br /> ...<br />}</pre><br /><p>И, естественно, нужно создать саму страницу. Вот примерное содержимое файла options.html</p><pre><html><br /><head><title>My Test Extension Options</title></head><br /><br /><body><br /><br />Favorite Color:<br /><select id="color"><br /> <option value="red">red</option><br /> <option value="green">green</option><br /> <option value="blue">blue</option><br /> <option value="yellow">yellow</option><br /></select><br /><br /><br><br /><div id="status"></div><br /><button id="save">Save</button><br /></body><br /><br /><script src="options.js"></script><br /><br /></html></pre><br /><p>Могут быть использованы любые html теги для оформления и любые элементы форм для ввода данных. Логика вынесена в файл options.js:</p><pre>// Saves options to localStorage.<br />function save_options() {<br /> var select = document.getElementById("color");<br /> var color = select.children[select.selectedIndex].value;<br /> localStorage["favorite_color"] = color;<br /><br /> // Update status to let user know options were saved.<br /> var status = document.getElementById("status");<br /> status.innerHTML = "Options Saved.";<br /> setTimeout(function() {<br /> status.innerHTML = "";<br /> }, 750);<br />}<br /><br />// Restores select box state to saved value from localStorage.<br />function restore_options() {<br /> var favorite = localStorage["favorite_color"];<br /> if (!favorite) {<br /> return;<br /> }<br /> var select = document.getElementById("color");<br /> for (var i = 0; i < select.children.length; i++) {<br /> var child = select.children[i];<br /> if (child.value == favorite) {<br /> child.selected = "true";<br /> break;<br /> }<br /> }<br />}<br />document.addEventListener('DOMContentLoaded', restore_options);<br />document.querySelector('#save').addEventListener('click', save_options);</pre>Для хранения опций используется локальное хранилище. При открытии страницы мы восстанавливаем содержимое всех полей в соответствии с сохраненными данными. При нажатии кнопки Save – сохраняем изменения. Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-26271715217612947222014-02-18T07:36:00.001-08:002014-02-18T07:36:12.961-08:00Omnibox<p><a href="http://lh4.ggpht.com/-mk1HxZmvgEI/UwN96ADEXsI/AAAAAAAADCQ/oOowMI-H2Ns/s1600-h/image%25255B2%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: left; padding-top: 0px; padding-left: 0px; margin: 0px 20px 20px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" align="left" src="http://lh6.ggpht.com/-uG96J1dOX20/UwN96273AZI/AAAAAAAADCY/4G5M03P4zu4/image_thumb.png?imgmax=800" width="244" height="110"></a></p> <p>Позволяет задать ключевое слово, после введения которого в адресной строке управление получает ваше расширение и может предлагать свои варианты. И в конечном итоге получает окончательный выбор пользователя.</p> <p>Зачем это надо, что можно сделать на основе такого API?<br clear="all"></p> <a name='more'></a> <p>Пример:</p> <p>после ввода ключевого слова “торрент” активируется расширение для поиска по трекерам. Оно подключается к сервису для метапоиска по трекерам и передает туда пользовательский ввод, а в ответ получает варианты популярных запросов, содержащих те же слова. После окончательного ввода расширение инициирует переход к найденному торренту, а если ничего не найдено – к поиску гугла.</p> <p>Аналогично можно сделать расширение для поиска по магазинам, социальным сетям и т.д.</p> <p>Тоже самое можно сделать просто запуском приложения в панели приложений, но возможно кому-то будет удобнее так</p> <h2>Манифест</h2><pre>{<br /> "name": "Aaron's omnibox extension",<br /> "version": "1.0",<br /> "omnibox": { "keyword" : "aaron" },<br /> "icons": {<br /> "16": "16-full-color.png"<br /> },<br /> "background": {<br /> "persistent": false,<br /> "scripts": ["background.js"]<br /> }<br />}</pre><br /><p>Требуется задать параметр omnibox – в нем указывается ключевое слово. И иконку 16х16.</p><br /><h1>Справочник по API</h1><br /><h2>Типы</h2><br /><p>SuggestResult результат предположения<br>Свойства</p><br /><ul><br /><li>content ( string ) Текст, который помещается в строку адреса и будет отправлен расширению если пользователь его выберет</li><br /><li>description ( string ) Текст который будет отображаться в выпадающем боксе. Может содержать XML разметку, теги url, match (для подсветки текста совпадающего с вводом пользователя) и dim (хз зачем).</li></ul><br /><h2>Методы</h2><br /><h3>setDefaultSuggestion</h3><br /><p>chrome.omnibox.setDefaultSuggestion(object suggestion)<br>Устанавливает первое предположение, которое выводится, когда пользователь ввел только само ключевое слово</p><br /><p>Параметры</p><br /><p><em>suggestion</em> ( object ) усеченный SuggestResult объект – без свойства content.</p><br /><h2>События</h2><br /><h3>onInputStarted </h3><br /><p>Пользователь ввел ключевое слово. Посылается один раз, в начале сеанса и до того как произойдет хоть одно событие onInputChanged.</p><br /><h3>addListener</h3><br /><p>Метод добавляющий обработчик событий. </p><br /><p>chrome.omnibox.onInputStarted.addListener(function callback)<br>Параметры</p><br /><p>callback ( function ) функция вида function() {...}</p><br /><h3>onInputChanged</h3><br /><p>Пользователь ввел что-то еще в строке поиска</p><br /><h3>addListener</h3><br /><p>chrome.omnibox.onInputChanged.addListener(function callback)<br>Параметры</p><br /><p>callback ( function ) функция вида function(string text, function suggest) {...}</p><br /><ul><br /><li>text ( string ) введенное значение</li><br /><li>suggest ( function ) функция чтобы отправить предположение</li></ul><br /><h3>onInputEntered</h3><br /><p>Пользователь принял предложенный вариант</p><br /><h3>addListener</h3><br /><p>chrome.omnibox.onInputEntered.addListener(function callback)<br>Параметры</p><br /><p>callback ( function ) функция вида function(string text, string disposition) {...}. </p><br /><ul><br /><li>text – окончательный вариант ввода</li><br /><li>disposition м.б. "currentTab", "newForegroundTab", или "newBackgroundTab" – рекомендованный вариант где открывать результат, в текущей вкладке или в новой.</li></ul><br /><h3>onInputCancelled</h3><br /><p>Пользователь завершил ввод, но ни одного предложенного варианта не принял</p><br /><h3>addListener</h3><br /><p>chrome.omnibox.onInputCancelled.addListener(function callback)<br>Параметры</p><br /><p>callback ( function ) функция вида function() {...}</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-4178865498396797202014-02-18T06:29:00.001-08:002014-02-18T06:29:26.649-08:00Rich Notifications<p><a href="http://lh3.ggpht.com/-D8VaZaYcK5Q/UwNuQXFm76I/AAAAAAAADB4/Hf8mYFfUV_8/s1600-h/image%25255B3%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: left; padding-top: 0px; padding-left: 0px; margin: 0px 20px 20px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" align="left" src="http://lh4.ggpht.com/-PARdeyWOpdA/UwNuRIzgZiI/AAAAAAAADCA/U5cy0XBdAEE/image_thumb%25255B1%25255D.png?imgmax=800" width="277" height="439"></a>Есть два способа добиться похожего результата.</p> <ul> <li>web notifications API, стандартизованный <strong>W3C</strong> <li><strong>chrome.notifications</strong> API для браузера Chrome.</li></ul> <p align="justify">Возможности второго несколько шире, и коль скоро мы разрабатываем расширение именно для Chrome, то вопрос кросс-браузерности нас не слишком волнует.</p> <p align="justify"> </p><br clear="all"> <a name='more'></a> <h2>Пример использования web notifications API</h2> <p>Прежде всего, в манифесте прописывается разрешение:</p><pre>{<br /> "name": "My extension",<br /> "manifest_version": 2,<br /> ...<br /> "permissions": [<br /> "notifications"<br /> ],<br /> ...<br /> // Примечание: Из-за ошибки №134315, необходимо явно указать все изображения<br /> // которые вы хотите использовать при вызове createNotification().<br /> "web_accessible_resources": [<br /> "48.png" ],<br />}<br /></pre><br /><p>Далее используем объект webkitNotifications для создания уведомления:</p><br /><p>// Нет необходимости использовать webkitNotifications.checkPermission().<br>// Поскольку в манифесте мы прописали их использование, то они всегда разрешены</p><br /><p>// Создаем простое текстовое уведомление<br>var notification = webkitNotifications.createNotification(<br> '48.png', // относительный URL иконки<br> 'Привет!', // Заголовок<br> 'Уведомляем вас, что уведомлять нам не о чем' // Текст<br>);</p><br /><p>// В оригинале был еще пример создания HTML-уведомления, но поскольку этот метод deprecated, я его выпилил.</p><br /><p>// Отправляем.<br>notification.show();</p> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-2994147035152452645.post-30962180503836258712014-02-18T04:41:00.001-08:002014-02-18T04:41:37.686-08:00Контекстные меню<p><strong>Кратко:</strong> chrome.contextMenus API позволяет добавлять элементы в контекстное меню. Можно выбрать для каких типов объектов эти элементы будут показываться, например картинки, гиперссылки или страница целиком. А также на каких сайтах. Для использования этой возможности в манифесте должно быть прописано разрешение.</p> <a name='more'></a> <h2>Использование</h2> <p>Созданные вашим расширением пункты контекстного меню могут быть показаны в любом документе. Будь то веб-страница, локальный документ (file://) или встроенная страница браузера (chrome://). Это можно изменить с помощью поля documentUrlPatterns при вызове метода create() или update(). Можно создавать сколько угодно контекстных меню, но если активно более чем одно, браузер автоматически объединяет их вместе.</p> <h2>Манифест</h2> <p>Чтобы пользоваться контекстными меню, необходимо</p> <ul> <li>указать contextMenus в правах доступа <li>включить в проект иконку размером 16х16</li></ul><pre>{<br /> "name": "My extension",<br /> ...<br /> "permissions": [<br /> <b>"contextMenus"</b><br /> ],<br /> "icons": {<br /> <b>"16": "icon-bitty.png",</b><br /> "48": "icon-small.png",<br /> "128": "icon-large.png"<br /> },<br /> ...<br />}</pre><br /><p>Примеры использования можно найти <a href="https://developer.chrome.com/extensions/samples.html#contextMenus" rel="nofollow">тут</a></p><br /><h1>Справочник по API</h1><br /><h2>Типы</h2><br /><p>OnClickData – информация, которая передается при активации элемента меню</p><br /><p>Свойства</p><br /><ul><br /><li>menuItemId ( integer or string ) идентификатор элемента меню</li><br /><li>parentMenuItemId ( optional integer or string ) идентификатор самого меню</li><br /><li>mediaType ( optional string ) 'image', 'video', или 'audio' если меню было вызвано для такого типа элементов</li><br /><li>linkUrl ( optional string ) URL ссылки, если меню было вызвано для ссылки</li><br /><li>srcUrl ( optional string ) URL из поля src (например для картинки)</li><br /><li>pageUrl ( optional string ) URL страницы. Не устанавливается если контекстное меню вызывалось без текущей с страницы (не совсем понял как это, но так и быть, верю разработчикам на слово)</li><br /><li>frameUrl ( optional string ) URL фрейма, если меню вызвано для фрейма</li><br /><li>selectionText ( optional string ) текст выделенного фрагмента, если меню вызывалось для выделения.</li><br /><li>editable ( boolean ) флаг, который показывает что элемент может редактироваться, например input, textarea и т.д.</li><br /><li>wasChecked ( optional boolean ) показывает состояние чекбокса или радиокнопки перед тем как для вызвали контекстное меню</li><br /><li>checked ( optional boolean ) показывает состояние чекбокса или радиокнопки, которое должно быть после того как вызвали контекстное меню. </li></ul><br /><p>Последние два пункта необходимо пояснить – пункт контекстного меню может быть чекбоксом или радиокнопкой. Именно он имеется в виду, а не элемент на странице. см. метод create</p><br /><h2>Методы</h2><br /><h3>create</h3><br /><p>integer or string chrome.contextMenus.create(object createProperties, function callback)<br>Создает новый элемент контекстного меню.</p><br /><p>Параметры</p><br /><p><em>createProperties</em> ( object )</p><br /><p>Свойства</p><br /><ul><br /><li>type ( optional enum of "normal", "checkbox", "radio", or "separator" ) тип элемента меню. По умолчанию normal</li><br /><li>id ( optional string ) уникальный, в пределах расширения, ID элемента. Обязательно для страниц типа event. </li><br /><li>title ( optional string ) текст отображаемый в элементе. Не нужен только для разделителя. Если вызывается для выделения можно использовать %s для включения выделенного текста. Например, "Перевести '%s' “ – в контекстное меню будет подставлен кусок текста, который предполагается перевести. <em>(Нужно будет попробовать выделить страницу текста и посмотреть что вставится в меню.)</em></li><br /><li>checked ( optional boolean ) Начальное значение чекбокса или радиокнопки. True для выбранного, false для невыбранного. </li><br /><li>contexts ( optional массив значений из набора "all", "page", "frame", "selection", "link", "editable", "image", "video", "audio", или "launcher" ) список ситуаций в которых будет показываться создаваемый пункт контекстного меню. По умолчанию “page”, т.е. для любого элемента страницы ,куда ни ткни. “all” = все варианты кроме “launcher”. (Чем это отличается от page?) “launcher” – поддерживается только для приложений и используется чтобы добавить пункт в контекстное меню на страницу/панель запуска приложений. Имеет различные ограничения в зависимости от платформы</li><br /><li>onclick ( optional function ) функция, которая вызывается когда пользователь кликнул на этот элемент контекстного меню. Не используется для страниц типа “event” вместо этого такая страница должна зарегистрировать обработчик chrome.contextMenus.onClicked. Напомню – это фоновая страница, которая не загружена постоянно, а создается только при необходимости (при возникновении события) Функция должна иметь два параметра:</li><br /><ul><br /><li>info ( OnClickData ) информация о активированном элементе меню и где он был активирован</li><br /><li>tab ( tabs.Tab ) вкладка на которой это произошло <em>(можно было включить это в OnClickData )</em></li></ul><br /><li>parentId ( optional integer or string ) идентификатор родительского элемента, если данный элемент является потомком ранее добавленного. </li><br /><li>documentUrlPatterns ( optional array of string ) маска(и) URL страницы или фрейма. Элемент будет в меню только для страниц(фреймов) удовлетворяющих условию см. описание паттернов.</li><br /><li>targetUrlPatterns ( optional array of string ) маска(и) URL атрибута src тэга img/audio/video или атрибута href ссылки.</li><br /><li>enabled ( optional boolean ) по умолчанию true – разрешает или запрещает этот элемент меню</li></ul><br /><p><em>callback</em> ( optional function ) функция вида function() {...}. Вызывается после того как элемент меню был создан. Если возникли какие-то проблемы, chrome.runtime.lastError будет содержать подробности<br></p><br /><h3>update</h3><br /><p>chrome.contextMenus.update(integer or string id, object updateProperties, function callback)<br>Обновляет ранее созданный элемент контекстного меню</p><br /><p>Параметры</p><br /><p><em>id</em> ( integer or string ) идентификатор обновляемого элемента</p><br /><p><em>updateProperties</em> ( object ) новый набор свойств элемента – все тоже самое что для метода create. Нельзя только в качестве родительского элемента указывать собственный id, или id своих потомков (при создании это естественно не получится)<em>callback</em> ( optional function ) тоже что для create</p><br /><h3>remove</h3><br /><p>chrome.contextMenus.remove(integer or string menuItemId, function callback)<br>Удаляет элемент из контекстного меню.</p><br /><p>Параметры</p><br /><p><em>menuItemId</em> ( integer or string ) идентификатор элемента<br><em>callback</em> ( optional function ) функция вида function() {...}, вызывается когда элемент был удален</p><br /><h3>removeAll</h3><br /><p>chrome.contextMenus.removeAll(function callback)<br>Удаляет все определенные расширением элементы контекстного меню.</p><br /><p>Параметры</p><br /><p><em>callback</em> ( optional function ) функция вида function() {...}, вызывается когда элементы были удалены</p><br /><h2>События</h2><br /><h3>onClicked</h3><br /><p>Возникает когда пользователь выбрал элемент контекстного меню</p><br /><h3>addListener</h3><br /><p>Метод, добавляющий обработчик события</p><br /><p>chrome.contextMenus.onClicked.addListener(function callback)<br>Параметры</p><br /><p><em>callback</em> ( function ) функция вида function(OnClickData info, tabs.Tab tab) {...}, вызывается при возникновении события</p><br /><ul><br /><li>info ( OnClickData ) информация о событии</li><br /><li>tab ( optional tabs.Tab ) вкладка где оно произошло</li></ul><br /><p>Примечательно что обработчик назначается не элементу, а является общим для всех элементов. Если у вас несколько элементов контекстного меню, то разделяются они уже внутри обработчика.</p> Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-2994147035152452645.post-25886670901642019212014-02-17T20:58:00.001-08:002014-02-17T22:14:32.658-08:00Browser Actions<p align="justify">Используется для того чтобы добавить иконку на главную панель инструментов браузера, справа от адресной строки. Может иметь всплывающую подсказку, бэджик и всплывающее окно. </p> <a name='more'></a> <p align="justify">Объявляется в манифесте следующим образом: <pre>"browser_action": { <br /> "default_icon": { <br /> "19": "images/icon19.png", <br /> "38": "images/icon38.png" }, <br /> "default_title": "Google Mail", <br /> "default_popup": "popup.html" <br />} <br /></pre><br /><p><a href="http://lh4.ggpht.com/-yfukLzkEs3I/UwLoZKzdoqI/AAAAAAAADBM/AzL31FKbod4/s1600-h/1%25255B1%25255D.jpg"><img title="1" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="1" src="http://lh4.ggpht.com/-ruD90Ii8DjQ/UwLoZh37-PI/AAAAAAAADBU/UlBsjVbpbds/1_thumb%25255B1%25255D.jpg?imgmax=800" width="519" height="152"></a></p><br /><p>Вот это все расширения с <strong>browser action</strong>. При нажатии на иконку расширения под ней открывается всплывающее окно.</p><br /><p>Если вы хотите чтобы иконка отображалась не всегда, используйте <strong>page action</strong>.</p><br /><h2>Иконка</h2><br /><p>Можно использовать один размер, и указывать ее в старом стиле:</p><pre>"default_icon": "images/icon19.png" <br /></pre><br /><p align="justify">В этом случае, при необходимости, браузер масштабирует картинку до нужного размера. При этом результат может получится не такой красивый, как вам хотелось бы. </p><br /><p>Можно использовать форматы BMP, PNG, JPEG, GIF, ICO. Для несжатых расширений картинки <u>должны</u> быть в формате PNG.</p><br /><blockquote><br /><p>Интересный пассаж. Т.е. при отладке используйте PNG, а потом, при релизе, из каких-то непонятных соображений можете поменять картинки на другие. </p><br /><p>Машина может быть любого цвета, если она черная(с)</p></blockquote><br /><p align="justify">Теоретически, браузер подбирает картинку с наиболее подходящим размером в зависимости от разрешения экрана, фактически сейчас поддерживаются два размера – 19 и 38</p><br /><p align="justify">Кроме использования статической картинки, иконку можно сформировать динамически через элемент <strong>canvas</strong>. Это имеет смысл если вы собираетесь ее анимировать. Для этого служит метод <strong>setIcon</strong>. </p><br /><h2>Всплывающая подсказка (tooltip)</h2><br /><p>Задается параметром <strong>default_title </strong>в манифесте или вызовом метода <strong>setTitle</strong>. </p><br /><h2>Бэдж (badge)</h2><br /><p><a href="http://lh5.ggpht.com/-EKtuLBCAky8/UwLoaUytSaI/AAAAAAAADBY/LRxhxruUmJs/s1600-h/image%25255B6%25255D.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: left; padding-top: 0px; padding-left: 0px; margin: 0px 20px 20px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" align="left" src="http://lh3.ggpht.com/-LXN7BPl2KSQ/UwLobP9c18I/AAAAAAAADBg/26XooYjnjuM/image_thumb%25255B2%25255D.png?imgmax=800" width="347" height="134"></a></p><br /><p align="justify">Бэдж, это небольшой текст, который накладывается на иконку расширения. Как можно догадаться, много туда просто не поместится, размер бэджа не более 4ех символов. Поэтому если вы выводите число, как в данном примере, то уместно округлять и применять сокращения. Например 10К вместо 10167.</p><br /><p align="justify">Формируется динамически, в манифесте отсутствует. Можно задать текст бэджа методом <strong>setBadgeText</strong> и его цвет, методом <strong>setBadgeBackgroundColor</strong>.</p><br clear="all"><br /><h2>Всплывающее окно (popup)</h2><br /><p align="justify">Появляется когда пользователь кликает по иконке расширения. Может содержать произвольный HTML код и автоматически подгоняет свой размер под содержимое. Задается параметром <strong>default_popup </strong>в манифесте, или вызовом метода <strong>setPopup</strong>.</p><br /><h2>Советы</h2><br /><ul><br /><li>Используйте <strong>browser actions</strong> для добавления возможностей, которые реально нужны на большей части страниц <br /><li>Если это не так, используйте <strong>page actions</strong>. <br /><li>Не поленитесь сделать большие, красивые иконки. Пользователь не любит когда у него перед глазами, на панели инструментов, торчат унылые какашки. <br /><li>Не пытайтесь делать иконки в той же цветовой гамме, что и родные иконки браузера – их внешний вид все равно зависит от темы и хрен вы угадаете. <br /><li>Используйте прозрачный фон, тогда ваши иконки будут выглядеть хорошо в любой теме (если вы не подгадаете с основным цветом под цвет темы. Тогда они будут выглядеть ужасно) <br /><li>Не злоупотребляйте анимацией. Это раздражает</li></ul><br /><h2>Примеры</h2><br /><p>Вы можете найти их тут: <a href="http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/browserAction/" rel="nofollow">examples/api/browserAction</a>. Или тут: <a href="https://developer.chrome.com/extensions/samples.html" rel="nofollow">Samples</a>. <br /><h1>Справочник по chrome.browserAction</h1><br /><h2>Типы</h2><br /><ul><br /><li><strong>ColorArray</strong> массив целых чисел <br /><li><strong>ImageDataType</strong> картинка. Должен быть объектом ImageData (например, из элемента <strong>canvas</strong>).</li></ul><br /><h2>Методы</h2><br /><h3>setTitle</h3><br /><p>chrome.browserAction.setTitle(object details) </p><br /><p>Устанавливает текст, который будет показываться во всплывающей подсказке к иконке расширения.</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>title ( string ) текст подсказки <br /><li>tabId ( optional integer ) задает конкретную вкладку для которой изменится подсказка. Автоматически сбрасывается, когда вкладка закрывается</li></ul></blockquote><br /><h3>getTitle</h3><br /><p>chrome.browserAction.getTitle(object details, function callback)</p><br /><p>Получает подсказку, общую, или для указанной вкладки. Зачем-то ее сделали асинхронной.</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>tabId ( optional integer ) задает вкладку. Если не указан, возвращается общая подсказка </li></ul></blockquote><br /><p><em>callback</em> ( function ) задает функцию, которая вызывается для получения результата. Функция должна принимать строковый параметр: function(string result) {...};<a name="property-callback-result"></a> </p><br /><h3>setIcon</h3><br /><p>chrome.browserAction.setIcon(object details, function callback)</p><br /><p>Устанавливает новую иконку. Иконка м.б. указана как путь к файлу, либо как объект ImageData, либо как словарь тех или других. Словарь имеет тот же смысл что и в манифесте – задается несколько вариантов картинки для разных разрешений экрана</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>imageData ( optional <a href="https://developer.chrome.com/extensions/browserAction.html#type-ImageDataType">ImageDataType</a> or object ) ImageData или словарь {размер -> ImageData} <br /><li>path ( optional string or object ) путь к файлу с картинкой или словарь {размер -> путь} <br /><li>tabId ( optional integer ) задает вкладку на которой меняется иконка. Автоматически сбрасывается когда она закрывается. </li></ul></blockquote><br /><p><em>callback</em> ( optional function ) функция вида function() {...}, которая вызывается после смены иконки. Сложно сказать зачем это надо, можно ли вызвать новую смену иконки раньше чем отработала предыдущая и что из этого получится. </p><br /><h3>setPopup</h3><br /><p>chrome.browserAction.setPopup(object details)</p><br /><p>Устанавливает html файл, который будет открываться как всплывающее окно, когда пользователь кликнет по иконке.</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>tabId ( optional integer ) можно задать разные попапы для разных вкладок <br /><li>popup ( string ) путь к файлу. Пустая строка – значит нет всплывающего окна. </li></ul></blockquote><br /><h3>getPopup</h3><br /><p>chrome.browserAction.getPopup(object details, function callback)</p><br /><p>Возвращает путь к файлу всплывающего окна.</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>tabId ( optional integer ) можно получить попап для указанной вкладки, или общий, если параметр не задан </li></ul></blockquote><br /><p><em>callback</em> ( function ) функция вида function(string result) {...} для получения результата </p><br /><h3>setBadgeText</h3><br /><p>chrome.browserAction.setBadgeText(object details)</p><br /><p>Устанавливает текст бэджа – общий, или для указанной вкладки</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>text ( string ) Текст бэджа. Размер текста м.б. любым, но только 4 символа поместятся. <br /><li>tabId ( optional integer ) задает вкладку. </li></ul></blockquote><br /><h3>getBadgeText</h3><br /><p>chrome.browserAction.getBadgeText(object details, function callback) </p><br /><p>Возвращает тест бэджа.</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>tabId ( optional integer ) задает вкладку. </li></ul></blockquote><br /><p><em>callback</em> ( function ) функция вида function(string result) {...}; </p><br /><p><br /><h3>setBadgeBackgroundColor</h3><br /><p>chrome.browserAction.setBadgeBackgroundColor(object details)</p><br /><p>Устанавливает цвет бэджа</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>color ( string or <a href="https://developer.chrome.com/extensions/browserAction.html#type-ColorArray">ColorArray</a> ) массив из четырех целых чисел в диапазоне 0-255, задающих цвет по модели RGBA. Либо строка со значением в формате, принятом в CSS <br /><li>tabId ( optional integer ) задает вкладку. </li></ul></blockquote><br /><h3>getBadgeBackgroundColor</h3><br /><p>chrome.browserAction.getBadgeBackgroundColor(object details, function callback)</p><br /><p>Возвращает цвет бэджа</p><br /><p>Параметры:</p><br /><p><em>details</em> ( object ) </p><br /><blockquote>Свойства: <br /><ul><br /><li>tabId ( optional integer ) задает вкладку. </li></ul></blockquote><br /><p><em>callback</em> ( function ) функция вида function(<a href="https://developer.chrome.com/extensions/browserAction.html#type-ColorArray">ColorArray</a> result) {...} </p><br /><h3>enable</h3><br /><p>chrome.browserAction.enable(integer tabId) </p><br /><p>Разрешает browser action для вкладки. По умолчанию оно и так разрешено.</p><br /><p>Параметры:</p><em>tabId</em> ( integer ) задает вкладку <br /><h3>disable</h3><br /><p>chrome.browserAction.disable(integer tabId)</p><br /><p>Запрешает browser action для вкладки.</p><br /><p>Параметры:</p><em>tabId</em> ( integer ) задает вкладку <br /><h2>События</h2><br /><h3>onClicked</h3><br /><p>Возникает, когда пользователь кликает по иконке расширения. Но только в том случае, если не назначено всплывающее окно.</p><br /><h3>addListener</h3><br /><p>Метод для добавления своего обработчика</p><br /><p>chrome.browserAction.onClicked.addListener(function callback)</p><br /><p>Параметры</p><em>callback</em> ( function ) задает функцию вида function(tabs.Tab tab) {...}, т.е. мы получаем идентификатор текущей вкладки <br /><h2>Вопросы для самостоятельного размышления</h2><br /><p align="right">А…э…так то, дружок (с)</p><br /><ol><br /><li>Если задать что либо (иконку, бэдж, всплывающее окно, подсказку) для конкретной вкладки, а потом вызвать тот же метод без указания конкретной вкладки? Изменится ли значение и на ней тоже, или к ней останется привязанным индивидуальное значение, которое будет перекрывать общее? <br /><li>Метод disable – делает иконку неактивной, или убирает ее с панели инструментов?</li></ol><br /><h2>Советы</h2><br /><p align="justify">Я не рекомендую использовать методы getXXX без веской причины. Вы должны и так знать, что вы там устанавливали и логика вашего расширения должна основываться на внутренней модели. Доверять можно только своим данным и своему коду. Опять же, асинхронные методы усложняют логику и открывают потенциальную возможность логических гонок и стало быть – трудноуловимых глюков.</p> Unknownnoreply@blogger.com0