Mozilla: ImgLikeOpera (images, cached images only, no images)
Ниже я попробую рассказать о том, как я пытался написать extension для Mozilla, позволяющий «по-Оперному» управлять загрузкой графических объектов, и почему эта попытка не удалась.
Следует отметить, что я мало знаком с JavaScript и XUL, поэтому некоторые утверждения могут быть не совсем верными.
Будем считать, что эта кнопка уже есть (думаю, что прикручивание такого элемента к интерфейсу Mozilla больших сложностей не вызовет). Кнопка стоит в положении «Show cached images only», пользователь запросил ресурс, начинает работать скрипт, который должен сделать следующее:
- Отключить графику в начале загрузки страницы.
- Включить графику после окончания загрузки.
- Обойти все картинки и собрать значения атрибута
src
. - Выяснить, есть ли картинка в кэше браузера и будет ли он задействован при её показе (expiration time):
- да: показать;
- нет: повесить на картинку обработчик события (aka «Load image»).
Рассмотрим более подробно.
Управление загрузкой графики:
const preferencesService =
Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("");
preferencesService.setIntPref("network.image.imageBehavior", "2");
- 2 — не загружать картинки;
- 1 — загружать только с родительского (originating) сайта;
- 0 — загружать все картинки.
Обход картинок и сверка с кэшем:
// Текущее время
timeNow = parseInt( (new Date()).getTime() / 1000 );
imgObjs = window.content.document.images;
for( i=0; i<imgObjs.length; i++ )
{
// Смотрим в кэш
if (getExpirationTime(imgObjs[i].src) < timeNow)
{
// В кэше картинки нет или она устарела: вешаем обработчик.
// "click" — для примера; можно сделать
// пункт в контекстном меню на правой кнопке мыши.
imgObjs[i].addEventListener("click",showNewImg,true);
}
else
{
// Показываем
imgObjs[i].parentNode.replaceChild(imgObjs[i],imgObjs[i]);
}
}
Для работы с кэшем:
const nsICacheService =
Components.interfaces.nsICacheService;
const cacheService =
Components.classes["@mozilla.org/network/cache-service;1"].getService(nsICacheService);
var httpCacheSession = cacheService.createSession("HTTP", 0, true);
httpCacheSession.doomEntriesIfExpired = false;
function getExpirationTime(url)
{
try
{
var cacheEntryDescriptor = httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_READ, false);
if(cacheEntryDescriptor)
return cacheEntryDescriptor.expirationTime;
}
catch(ex) {}
return null;
}
Обработчик для новой, ранее не загруженной картинки:
function showNewImg()
{
this.removeEventListener("click", showNewImg, true);
this.parentNode.replaceChild(this,this);
return;
}
Осталось лишь привязаться к событиям начала и окончания загрузки документа. События «onBeforeLoad» нет, аналог можно сделать таким методом (впервые увидел здесь):
const NOTIFY_ALL =
Components.interfaces.nsIWebProgress.NOTIFY_ALL;
const STATE_STOP =
Components.interfaces.nsIWebProgressListener.STATE_STOP;
const STATE_START =
Components.interfaces.nsIWebProgressListener.STATE_START;
var docDone = false;
var imageLoadListener = {
QueryInterface : function(aIID)
{
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onStateChange:function(aProgress,aRequest,aFlag,aStatus)
{
// Начало загрузки документа
if (aFlag & STATE_START)
{
preferencesService.setIntPref("network.image.imageBehavior", 2);
docDone = false;
}
// Документ загружен
if (aFlag & STATE_STOP)
{
if (docDone == false)
{
preferencesService.setIntPref("network.image.imageBehavior", 0);
// .. .. ..
// Здесь обходим картинки
// .. .. ..
docDone = true;
}
}
},
}
function loadImgHide()
{
window.getBrowser().addProgressListener(imageLoadListener, NOTIFY_ALL);
}
function unloadImgHide()
{
window.getBrowser().removeProgressListener(imageLoadListener);
}
function setImagePrefOnWindowLoad()
{
preferencesService.setIntPref("network.image.imageBehavior", 2);
}
function setImagePrefOnWindowUnload()
{
preferencesService.setIntPref("network.image.imageBehavior", 0);
return;
}
window.addEventListener("load", setImagePrefOnWindowLoad, false);
window.addEventListener("unload", setImagePrefOnWindowUnload, false);
document.addEventListener("load", loadImgHide, true);
document.addEventListener("unload", unloadImgHide, true);
Это даже почти работает. :)
Чего не хватает? Собственно самой кнопки; показа картинки в другом (уже открытом) табе, если она была загружена в текущем; более корректной работы с пользовательскими настройками; обработки не только img
, но и background-image
; etc. Всем этим можно было бы заняться после решения двух вопросов:
- как отлавливать начало загрузки таба (особенно при
browser.tabs.loadInBackground
= true)? Мой английский хромает, может быть я не понял чего? - если значение
network.image.imageBehavior
носит глобальный характер, то как менять его «локально», лишь в области видимости таба?
Вот те две вещи, которые мне остались непонятны. Если они решаемы, то…
О багах. Работоспособность скрипта проверялась на localhost, могут быть проблемы с «внешним миром» на тонких каналах. Ну и с табами проблемы, да. Желающие могут скачать (zip, 9kb) и посмотреть, но я не несу ответственности за возможный ущерб, причиной которого будет использование этого extension (лично у меня иногда зависал браузер). Делалось лишь из любопытства.
Для тех, кто захочет посмотреть в работе и установит: тест. Первая страница без картинок, начинать с неё; ссылку на вторую страницу открывать в текущем табе.
Update: Xpoint.ru
Update 2: ILO FF1.0-only
Categories: dHtml, Soft, Usability | comments: (3)
Комментарии
1. kukutz 29th August 2004 - 09:36
Может, анонсировать в
http://www.livejournal.com/community/ru_mozilla/
http://www.livejournal.com/community/mozilla_ru/
http://www.livejournal.com/community/ru_mozdev/
?
А также в форумах mozillazine & extensionmirror?
С вопросами, как быть с табами.
Mash:
LJ: даже не знаю как писать в community. Там нужно быть его членом, да?
mozillazine/extensionmirror: мой английский. :(
2. kukutz 29th August 2004 - 13:24
Да, сперва вступить в сообщество на странице его профиля, потом /update.bml и выбрать там журнал, в который писать.
Английский — ну, я готов помочь вычитать сообщение. Но вообще да, это проблема.
Mash:
ru_mozdev, mozilla_ru. Спасибо.
3. ventzy 17th January 2005 - 14:29
is there update for FF 1.0?
Mash:
No, because FF1.0 has bug.
I can write this extension without some features and thinking about it. Maybe after 1-2 weeks.
(later) Here. Buggy now, but work.