Xpoint
   [напомнить пароль]

Extension: images, cached images only, no images

Метки: [без меток]
[арх]
2004-08-29 17:12:32 [обр] Данил Иванов(5/30)[досье]

Люди привыкают к хорошему. Есть желание увидеть extension для Mozilla/Firefox, позволяющий работать с кэшированной графикой на том уровне, на котором это реализовано в Opera. Или удостовериться в том, что написать такой extension невозможно.

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

В голове каша, мало знаком с JavaScript/XUL/Mozilla, поэтому вполне допускаю возможную ошибочность некоторых высказываний и даже всего подхода в целом. Был бы признателен за указание этих ошибок.

спустя 6 часов [обр] Владимир Палант(434/4445)[досье]
Просмотрел... Есть способ получше: nsIContentPolicy. По этому принципу реализован AdBlock. Идея: реализуется XPCOM-компонента, имплементирующая этот интерфейс, и регистрируется в категории content-policy. Каждый раз, когда браузеру нужно будет загрузить внешний файл, он будет вызывать метод shouldLoad() этой компоненты - а там уже можно проверить наличие файла в кеше.
спустя 22 часа [обр] Данил Иванов(5/30)[досье]

Эх... Пришёл с работы, сел за ящик и пятый час пережёвываю написанное Вами, смотрю в AdBlock, чувствую себя полным кретином. Я лишь догадываюсь о тех вещах, о которых Вы говорите. Сама идея понятна, реализация — нет. registerFactory, addObserver, ..., тёмный лес. Вот посмотреть бы для начала какие-нибудь учебные, более компактные примеры, показывающие суть работы этих механизмов.

Где можно прочитать об этом в более-менее доступной форме? Уже скачал "Rapid Application Development with Mozilla", но терзают смутные сомнения, что сей труд мало поможет.

спустя 13 часов [обр] Владимир Палант(434/4445)[досье]

Да, в AdBlock сейчас всё очень запутано, потому что он регистрирует компоненту, которая не находится в подкаталоге mozilla/components, а это достаточно сложно. Я сам этот код и написал когда-то. Порылся у себя - нашёл самую первую версию, где это самая стандартная компонента: http://www.gtchat.de/trash/nsAdblock.js. В этом файле реализованы функции блокировки Adblock, всё остальное - лишь интерфейс для изменения установок.

Объект nsAdblock - реализация nsIContentPolicy (а также nsIObserver, но это только для того, чтобы узнавать об изменениях установок).

Объект nsAdblockModule - реализация nsIModule, отвечает за регистрацию компоненты. Кроме категории content-policy я добавляю её ещё в категорию в app-start - это чтобы в nsAdblock вызывался метод observe() после старта Mozilla (опять же, чтобы прочитать установки).

Объект nsAdblockFactory - реализация nsIFactory. Поскольку nsAdblock должен быть только один на всё приложение, то никаких новых объектов она не создаёт, всегда возвращает уже существующий.

NSGetModule() - экспортируемая функцию XPCOM-компонент, вызывается, чтобы получить nsIModule-объект.

Достаточно скопировать этот файл в каталог mozilla/components, при следующем старте он зарегистрируется автоматически. На всякий случай можно удалить compreg.dat, тогда все компоненты перерегистрируются.

Есть bug 71689, чтобы упростить написание XPCOM-компонент на JavaScript, и даже неплохое решение. Но что-то про это все забыли :-(

спустя 7 часов [обр] Данил Иванов(5/30)[досье]
Я сам этот код и написал когда-то.

Да, я видел.

Честно говоря, изначально я не задавался целью именно написать extension, мне больше было интересно почему подобное не написано до сих пор. Такое чувство, что вместе с установкой Mozilla всех бонусом подключают к толстым бесплатным каналам. Отсутствие "show cached images only" в этом браузере — одна из немногих причин, по которой при выборе "Opera vs. Mozilla" часть потенциальных пользователей выбирает первый браузер и сквозь пальцы смотрит на остальные вещи.

Таким образом, половину моего любопытства Вы удовлетворили. Большое спасибо за ответы и пример. Как только появится очередной кусок свободного времени, буду вдумчиво изучать. Во-первых, это достаточно интересно. Во-вторых, ...вдруг что-нибудь получится? :)

спустя 6 часов [обр] Сергей Чернышев(25/589)[досье]
Данил Иванов[досье]
Просто программисты (которые обычно и пишут extensions), обычно сидят как раз на хороших каналах, особенно здесь за бугром.
спустя 5 часов [обр] Данил Иванов(5/30)[досье]

Сергей Чернышев[досье]
С другой стороны, программисты обычно пишут не только для себя.

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

спустя 7 часов [обр] Владимир Палант(434/4445)[досье]
Да, я видел.
Опа... А я и не знал, что там моё имя в коде поставили :)
спустя 6 часов [обр] Сергей Чернышев(25/589)[досье]
Данил Иванов[досье]
неа - реально open source разработкой движет именно personal itch
спустя 1 час 51 минуту [обр] Данил Иванов(5/30)[досье]

Владимир Палант[досье]
adblock.js:

...
 * Contributor(s):
 * Henrik Aasted Sorensen
 * Stefan Kinitz
 * Wladimir Palant
 * rue
...

Сергей Чернышев[досье]
Но должны же быть моменты, когда "глас народа" переходит в этот самый "personal itch"? Наверное я романтик. :)

спустя 11 часов [обр] Владимир Палант(434/4445)[досье]
Данил Иванов[досье]
Тот народ, который умеет подавать голос - тоже программисты :)
спустя 18 часов [обр] Сергей Чернышев(25/589)[досье]
Данил Иванов[досье]
Глас народа всегда плохо переходит в peronal itch - в эти моменты мы и получаем казалось бы полезную вещь, но кривую и с плохим интерфейсом. ;)
спустя 3 часа 57 минут [обр] Данил Иванов(5/30)[досье]

Владимир Палант[досье]
Сергей Чернышев[досье]
Сдаюсь, убедили. Особенно после того, как

получаем казалось бы полезную вещь, но кривую и с плохим интерфейсом. ;)

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

Владимир Палант[досье]
Я хотел было бросить уже, но на второй вечер всё-таки дошло. Ещё раз спасибо. Сейчас тормозит лишь то, что при написании я узнаю много новых вещей, которые заставляют переписывать заново. Рекурсия. :)

спустя 3 часа 59 минут [обр] Владимир Палант(434/4445)[досье]
Ну, всего лишь два дня? Я по-моему явно больше времени угробил, когда это писал - это была моя первая XPCOM-компонента :)
спустя 1 час 16 минут [обр] Данил Иванов(5/30)[досье]

Два вечера лишь на то, чтобы понять, что мне дали на блюдечке почти всё, что я хотел. :) Суть работы этого кода понимаю, но "на пальцах", без нюансов и терминологии. Сам бы до такого не додумался точно, хотя бы потому, что внутренностями различных extensions заинтересовался лишь пару недель назад.

Не боюсь повториться, знаний в этой области мало, да и не программист я, но процесс "ткнуть сюда, посмотреть почему перестало работать там, починить, попытаться сделать лучше" порой доставляет жуткое удовольствие. =)

спустя 9 часов [обр] Сергей Чернышев(25/589)[досье]

Владимир Палант[досье]
Кстати, Владимир, а не могли бы вы описать процесс разработки - чем вы пользуетесь, какова последовательность действий при отладке тех или иных вещей, какова логика действий, на какие URL-ы ходите когда получаете то или иное сообщение об ошибке или когда встает та или иная проблема.

Я бы и сам написал, но у меня опыта мало - URLFix можно за день написать с нуля.

Это был бы очень полезный материал, потому что я чуствую, что нужно потратить очень много сил чтобы осознать нормальный процесс разработки под XUL/XPFE - думаю, что это могло бы дать серьезный толчок разработчикам. Если хотите, можете сразу в Mozdev.ru писать - организуем место, аккумулирующее мнения по правильному подходу к программированию Mozilla-приложений.

спустя 12 минут [обр] Данил Иванов(5/30)[досье]
Это был бы очень полезный материал
+1
спустя 15 часов [обр] Владимир Палант(434/4445)[досье]
Вряд ли я могу написать тут что-то полезное. Пользуюсь обыкновенным редактором, отлаживаю в основном alert'ами (поэтому как раз угробил массу времени на первый рабочий вариант компоненты, которую привёл выше - пока не понял, что единственный способ вывода информации, который работает во время регистрации/старта компоненты, это запись в файл). При проблемах иногда хожу на XULPlanet, иногда на Google, но чаще всего лезу в исходники. Иногда ещё ищу на Bugzilla, не сталкивался ли уже кто-то с подобным. К сожалению, процесс разработки XUL-приложений на данный момент далёк от идеала.
спустя 1 час 21 минуту [обр] Данил Иванов(5/30)[досье]

Владимир Палант[досье]

пока не понял, что единственный способ вывода информации, который работает во время регистрации/старта компоненты, это запись в файл

Вот для Вас сейчас это кажется обыденной вещью, а знаете как я сейчас узнаю нужную информацию, подстраивая под себя Ваш код?

shouldLoad: function(contentType, contentLocation, context, wnd)
{
    ...
    context.title = myDebugInfo;
    // а потом мышой по titles, да :)
    ...
}
спустя 1 час 37 минут [обр] Владимир Палант(434/4445)[досье]

Да уж, забыл сказать - в компонентах alert() в принципе не работает (у них нет объекта window), а обычно используют dump(). Нужно поставить установку browser.dom.window.dump.enabled на true, тогда dump будет печатать в консоль. Под Windows вывод в консоль включён только в дебаговых билдах, иначе он включается перенаправлением вывода в файл:

mozilla.exe > messages.file

Но проблема была в основном в том, что при регистрации компоненты и при старте Mozilla dump() тоже не работает...

спустя 13 часов [обр] Сергей Чернышев(25/589)[досье]
Понятно, а кроме компонент вы разьве ничего не пишете? ;) Может какие-то трюки расскажете?
спустя 11 часов [обр] Владимир Палант(434/4445)[досье]
Как раз наоборот - в основном я пишу расширения, о них и шла речь. И никаких особых трюков вроде бы не применяю.
спустя 2 часа 23 минуты [обр] Данил Иванов(5/30)[досье]

Очередная версия. В целом уже начинает походить на задуманное, но до идеала ещё топать и топать. Нет многих мелочей, реализация которых не доставит особых проблем (ой ли? :]), но есть несколько вопросов, найти ответ на которые пока не получилось.

Коротко о работе:

shouldLoad выдаёт true/false в зависимости от атрибута таба (iloTabLoadValue), которому принадлежит запрашиваемый объект. Значения атрибута лежат в диапазоне от 0 до 3. 0, 1, 2 — как и у network.image.imageBehavior (загружать всё; только для этого сайта [пока просто true, без проверки принадлежности к этому сайту]; не загружать), 3 — загружать только из кэша. Само значение выставляется двумя путями: либо при вызове popup-меню, расположенном в statusbar, либо в компоненте и тогда этот атрибут принимает значение по умолчанию (=imglikeopera.default).

Сами вопросы:

  1. Хочется сделать регистрацию компоненты как в Adblock, а не копировать её в каталог mozilla/components. Как я понимаю, в последнем случае она стартует при каждом запуске браузера, даже если сам extension выключен или удалён. В принципе, он при работе смотрит на переменную Enabled (=imglikeopera.enabled) и можно было бы сделать
//~ ::: imglikeopera.js :::
window.addEventListener("load", setEnabled, false);
// и выставляем imglikeopera.enabled = true
window.addEventListener("unload", setDisabled, false);
// и выставляем imglikeopera.enabled = false

но, во-первых, не всегда работа браузера завершается корректно, а во-вторых, компонента всё равно будет загружаться, забирая ресурсы (насколько много? можно пренебречь?). В adblock.js смотрел, пробовал, ничего не получилось, только голова разболелась. :) Буду пробовать ещё. Или можно по-другому?

  1. Хочется выставлять у новых табов значения iloTabLoadValue равными iloTabLoadValue того таба, из которого они были открыты. Т.е. что-то в духе
//~ ::: nsImgLikeOpera.js :::
if (!openTabs[i].iloTabLoadValue)
  openTabs[i].iloTabLoadValue = openTabs[i].OPENER.iloTabLoadValue;
  1. Таб, которому принадлежит запрашиваемая картинка, отлавливаю в цикле, пробегаясь по всем имеющимся табам во всех окнах и сравнивая их содержимое с context.ownerDocument. Как избавиться от этого жуткого(?) цикла?
  1. В statusbar хотелось бы вкрутить изображение-индикатор, показывающий установки для текущего таба. Как отслеживать перемещение пользователя по табам, чтобы в соответствии с ним менять это изображение?

И самое главное: может быть я опять что-то не так делаю в принципе? :)

спустя 16 часов [обр] Владимир Палант(434/4445)[досье]

Данил Иванов[досье]

  1. Регистрация компоненты в Adblock - громадный изврат, без особой надобности так лучше не делать. Вообще-то временно добавить компоненту не проблема. Но дело в том, что PolicyManager (@mozilla.org/layout/content-policy;1) читает список компонент, зарегистрированных в категории content-policy, только при старте Mozilla. Любая компонента, добавленная позже, просто игнорируется. Поэтому Adblock временно заменяет сам PolicyManager, это проходит начиная с Mozilla 1.4.

Вроде бы обещали реализовать регистрацию компонент в профиле пользователя то ли к версии 1.8, то ли 1.9 (хотя в bug 45701 особой активности не заметно). Тогда можно будет перестать заниматься извращениями.

  1. А что в случае File / New / Navigator Tab? Но, в принципе, открытием табов занимается функция tabbrowser.addTab(), здесь можно вклиниться (к примеру заменить её на свою, вызывающую оригинал).
  1. Сложно. Попробуйте context.ownerDocument.abstractView.parent, но я сомневаюсь, что это даст вам chrome-окно. Точка отправления у вас - элемент HTML-документа, от него в chrome не попасть. Посмотрел, как находит своего родителя само окно - через свой DocShell-объект, который для HTML-документа IMHO не достать (а если бы и достали, то там требуется преобразование в nsIScriptGlobalObject, который для скриптов не доступен).
  1. tabpanels.addEventListener("select") - но сначала, конечно, нужно найти эти tabpanels.

Да в принципе вы всё правильно делаете :)

спустя 6 часов [обр] Данил Иванов(5/30)[досье]
Владимир Палант[досье]
Злюсь на себя за то, что большая часть сказанного Вами будет доходить позже, но последнее предложение успокаивает. ;)
спустя 6 часов [обр] Данил Иванов(5/30)[досье]

Владимир Палант[досье]

  1. ок, извращаться не буду. Но всё-таки как кошерно "выключать" компоненту при отключенном extension? А то как получается: если extension пользователь отключает, imglikeopera.enabled так и остаётся равным true, компонента при загрузке смотрит на это и начинает фильтровать.
  1. Ну, если File / New / Navigator Tab и Ctrl+T перехватить нельзя — то будет равным imglikeopera.default.

По поводу вклиниванья. В контекстное меню страницы добавил свой menuitem с oncommand="openNewTab();", при вызове этого контекстного меню прячу "настоящий" пункт "Open Link in New Tab". Сама функция мне какой-то такой показалась:

function openNewTab()
{
    var URL = gContextMenu.target.href;
    var currentTab = getBrowser().selectedTab;
    var currentiloTabLoadValue = getBrowser().selectedBrowser.iloTabLoadValue;
    var newTab = getBrowser().addTab("about:blank");
    getBrowser().selectedTab = newTab;
    getBrowser().selectedBrowser.iloTabLoadValue = currentiloTabLoadValue;
    getBrowser().loadURI(URL);
    getBrowser().selectedTab = currentTab;
    return;
}

Ещё добавить в loadURI() ReferrerURI, Charset, посмотреть browser.tabs.loadInBackground... По-моему работает, но уже утро на дворе, глаза красные, туго соображаю.

  1. Да, вроде бы не получается. Придётся так оставить.
  1. Бум искать. :)
спустя 11 часов [обр] Владимир Палант(434/4445)[досье]
  1. Ваша проблема в том, как оповестить компоненту о том, что расширение включено и надо работать? Посмотрите nsIObserverService. Пример регистрации собственной реализации nsIObserver у вас уже есть, а расширению нужно будет лишь вызвать nsIObserverService.notifyObservers(), к примеру с topic "imglikeopera".
  1. Это некрасиво... В onload своего расширения пишете что-нибудь такого типа:
  var browser = getBrowser();
  browser._oldAddTab = browser.addTab;
  browser.addTab = function(aURL) {
    var currentValue = browser.selectedBrowser.iloTabLoadValue;
    var tab = browser._oldAddTab(aURL);
    browser.selectedBrowser.iloTabLoadValue = currentValue;
    return tab;
  };
  1. Однако, у tabbrowser нет tabpanels (точнее есть, но это анонимные элементы - XBL). Попробуйте addEventListener("select") на самом tabbrowser - может получится.
спустя 12 часов [обр] Данил Иванов(5/30)[досье]
  1. Понял. Попробую.
  1. Небольшое дополнение (вдруг кому пригодится). Передача значения iloTabLoadValue в новый таб после его создания не даёт нужного эффекта, поэтому склоняюсь к такому варианту:
var browser = getBrowser();
browser._oldAddTab = browser.addTab;
browser.addTab = function(aURL, aReferrerURI, aCharset) {
    var currentTab = browser.selectedTab;
    var currentValue = browser.selectedBrowser.iloTabLoadValue;
    var tab = browser._oldAddTab("about:blank");
    browser.selectedTab = tab;
    browser.selectedBrowser.iloTabLoadValue = currentValue;
    browser.loadURI(aURL, aReferrerURI, aCharset);
    if (prefService.getBoolPref("browser.tabs.loadInBackground"))
        browser.selectedTab = currentTab;
    return tab;
};
Это некрасиво

Уели. Читал и краснел. :)

  1. Да, отловить можно.

Ещё раз спасибо.

спустя 9 дней [обр] Данил Иванов(5/30)[досье]

Хочется иметь контроль над временем устаревания изображений. Сейчас по адресу about:cache?device=memory можно увидеть, например

Key: http://localhost/test.jpg
Data size: 110920 bytes
Fetch count: 1
Last modified: 09/17/04 21:44:13
Expires: 09/27/04 22:40:48

Существует ли возможность изменить значение "Expires"?

const nsICacheService =
  Components.interfaces.nsICacheService;
const cacheService =
  Components.classes["@mozilla.org/network/cache-service;1"].getService(nsICacheService);
var httpCacheSession =
  cacheService.createSession("HTTP", 0, false);
httpCacheSession.doomEntriesIfExpired = true;

var cacheEntryDescriptor =
  httpCacheSession.openCacheEntry(url, Components.interfaces.nsICache.ACCESS_WRITE, false);
if (cacheEntryDescriptor)
{
  cacheEntryDescriptor.setExpirationTime("1193800000");
  cacheEntryDescriptor.markValid();
  cacheEntryDescriptor.close();
}

Великим методом научного тыка пробовал различные варианты true/false в httpCacheSession, .doomEntriesIfExpired, cacheEntryDescriptor. Максимум, чего удаётся добиться: в кэше, наряду с иходной записью, появляется "дубль" вроде такого

Key: http://localhost/test.jpg
Data size: 0 bytes
Fetch count: 1
Last modified: 08/13/70 04:46:40
Expires: 10/31/07 07:06:40
спустя 5 дней [обр] Владимир Палант(434/4445)[досье]
Данил Иванов[досье]
  1. Тут по рассылке project_owners@mozdev.org пришло интересное решение, как можно получить tabbrowser, имея window-объект документа (то есть context.ownerDocument.abstractView):
window
 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
 .getInterface(Components.interfaces.nsIWebNavigation)
 .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
 .rootTreeItem
 .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
 .getInterface(Components.interfaces.nsIDOMWindow)
 .getBrowser()
спустя 1 час 20 минут [обр] Данил Иванов(5/30)[досье]

Владимир Палант[досье]

Да уж, решение интересное. Спасибо. Нужно будет тоже подписаться.

А по поводу принудительного кэширования изображений ничего подсказать не могли бы? Хочется сделать так, чтобы браузер не лез даже проверять изменилась ли картинка, а сразу доставал из кэша. Я по мере возможностей потихоньку начал читать литературу, чтобы хоть в терминологии немного понимать, но пока на ум ничего не приходит.

И, кстати, раз уж вы следите за рассылками и более в курсе событий, скажите, стоит ли волноваться по этому поводу? Как я подозреваю, это связано с откатом на какие-то более стабильные, но старые механизмы работы. Если мои подозрения верны, то когда можно ожидать "приката" обратно?

спустя 1 час 34 минуты [обр] Владимир Палант(434/4445)[досье]
сообщение промодерировано

Изменить значение Expire: вызывать setExpirationTime() в nsICacheEntryDescriptor вы не пробовали?

Не могу сказать, что я слежу за рассылками, просто на эту меня подписали автоматически из-за http://xpoint.mozdev.org/ (я её обычно и не читаю). Но по поводу нулевого контекста для картинок, которые загружаются из CSS: это bug 243948, в актуальных версиях Mozilla уже исправлен (там в качестве контекста передаётся document). Поскольку Firefox 0.9 и 1.0 для стабильности основаны на исходниках Mozilla 1.7, то там эта ошибка ещё присутствует. Начиная с Firefox 1.1 Mozilla и Firefox опять будут использовать общую базу исходников.
Также по теме: bug 245280 (я там спросил, не хотят ли они исправить этот баг до Firefox 1.1)

спустя 1 час 23 минуты [обр] Данил Иванов(5/30)[досье]
сообщение промодерировано

Владимир Палант[досье]

вызывать setExpirationTime() в nsICacheEntryDescriptor вы не пробовали?

Пробовал (Extension: images, cached images only, no images (264353)), но как-то не получилось изменить. Если я правильно понимаю, то логика должна быть следующей: пробуем openCacheEntry с ACCESS_READ, если открыли, смотрим Expires и, если он нас не устраивает, открываем openCacheEntry с ACCESS_WRITE, меняем. Я крутил-вертел всякие установки, но максимум, как уже писал, — появляется "дубль". Теплится слабая надежда на то, что мог что-нибудь не так сделать, т.к. занимался этим в одну из красноглазых ночей. Может быть openCacheEntry с ACCESS_READ забыл закрыть перед открытием на запись или ещё что... Попробую ещё раз в выходные.

По багам позже похожу, но на сердце уже спокойнее. :)

спустя 3 часа 59 минут [обр] Данил Иванов(5/30)[досье]
Владимир Палант[досье]
Понял, что вы говорите о другом, попробовал, работает. Пожалуй, я воздержусь от описания своего душевного состояния. :)
спустя 14 минут [обр] Владимир Палант(434/4445)[досье]
Я не знаю, о чём я говорил, поскольку просто невнимательно прочитал ваш постинг :) Что именно работает?
спустя 4 часа 32 минуты [обр] Данил Иванов(5/30)[досье]

nsICacheEntryDescriptor

const cacheService =
    Components.classes['@mozilla.org/network/cache-service;1']
    .getService(Components.interfaces.nsICacheService);
var httpCacheSession =
    cacheService.createSession("HTTP",
    Components.interfaces.nsICache.STORE_ANYWHERE, true);
httpCacheSession.doomEntriesIfExpired = false;

 ... ...

try {
    var cacheEntryDescriptor =
        httpCacheSession.openCacheEntry(contentLocation.spec,
        Components.interfaces.nsICacheEntryDescriptor, false);
    //~ "if", но для наглядности -- 1200000000
    cacheEntryDescriptor.setExpirationTime("1200000000");
    cacheEntryDescriptor.markValid();
    cacheEntryDescriptor.close();
}
catch(ex) { //~ в кэше такого объекта нет }

Кажется работает. Или это действительно несколько иное, или я в своих первых экспериментах всё-таки напортачил. Разбираться не могу уже просто физически.

Я не знаю, о чём я говорил, поскольку просто невнимательно прочитал ваш постинг :)

... и всё равно помогли. Magic people, voodoo people. :)

спустя 6 часов [обр] Сергей Чернышев(25/589)[досье]
На project_owners теперь подпишитесь раз есть что на Mozdev.org запостить ;)
спустя 7 часов [обр] Владимир Палант(434/4445)[досье]
На project_owners подписывают автоматически при открытии нового проекта на mozdev.org :)
спустя 17 часов [обр] Сергей Чернышев(25/589)[досье]
Владимир Палант[досье]
Я это и имел ввиду ;)
спустя 4 часа 48 минут [обр] Данил Иванов(5/30)[досье]
Намёк понял, но пока просто так подписался. На мой взгляд, рановато ещё, чуть позже. :)
спустя 4 месяца 1 день [обр] Данил Иванов(5/30)[досье]

Думаю, создавать новое обсуждение не стоит, пишу здесь.

Решил переделать эту болванку под FF1.0-only. Выложу на всеобщее обозрение через пару дней (для загребания жара..., тьфу, отлова багов чужими руками, своих катастрофически не хватает). Некоторые вещи пришлось переделать из-за бага, некоторые просто пересмотрел.

Завёл установку, отвечающую за политику загрузки графики из CSS. Установка глобальная, для всех окон/табов. shouldLoad() возвращает false лишь в том случае, когда этой установкой запрещена загрузка подобных изображений и запрашиваемого изображения нет в кэше. Криво, но это единственный дружелюбный к пользователю вариант, пришедший мне в голову.

Так же пришлось пока откатиться на старый поиск окна через nsIWindowMediator.

Два небольших вопроса:

Стоит ли с сегодняшним состоянием дел соваться на mozdev.org? В принципе, возиться с настройками там не очень хочется, но если это несёт в себе ощутимые бонусы, полезные для дальнейшей жизни этого расширения, то возиться буду с удовольствием.

И небольшой персональный: Владимир, какие Ваши данные [можно/нужно] указать (как одного из авторов)?

спустя 21 час [обр] Владимир Палант(434/4445)[досье]
  1. Добавление проекта на mozdev.org может длиться пару недель (делается вручную), но потом у меня все настройки много времени не заняли. Основная причина, из-за которой я это сделал — политика безопасности Firefox по умолчанию позволяет установку только с downloads.mozdev.org и mozilla.org. Кроме того, удобно держать проект в CVS. Ваш же проект на mozdev.org помимо всего еще и найдет больше заинтересованных людей.
  1. Не надо, пожалуйста. Хватит с меня AdBlock, к которому я имею весьма косвенное отношение.
спустя 2 часа 55 минут [обр] Данил Иванов(5/30)[досье]
  1. Ну, если заинтересованные люди найдутся и кто-нибудь ещё пару раз направит в нужном направлении, то это будет замечательно. Ясно, спасибо.
  1. :) ок.
спустя 11 часов [обр] Сергей Чернышев(25/589)[досье]
Данил Иванов[досье]
Вы добавьте просто так - труд не должен пропадать - а заинтересованые люди либо найдутся либо не найдутся, но с mozdev.org они найдутся быстрее.
спустя 3 месяца 14 дней [обр] Данил Иванов(5/30)[досье]

Прежде всего — ещё раз спасибо. Расширение переехало на mozdev.org, где живёт и пока что здравствует, но со здоровьем в последнее время у него всё-таки есть небольшие проблемы, потому снова обращаюсь к знающим людям.

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

Для того, чтобы проводилась отрисовка места под картинки, загрузка которых была блокирована через nsIContentPolicy::shouldLoad, в этом самом shouldLoad приходится возвращать true (а не просто false) и обнулять значение contentLocation.spec.

Если раньше прототип контекстного меню содержал строки

Windows; en-US; rv:1.7.7; Gecko/20050414 Firefox/1.0.3
Prototype for nsContextMenu (browser.js, line 3592):

// See if the user clicked on an image.
if ( this.target.nodeType == Node.ELEMENT_NODE ) {
     if ( this.target.localName.toUpperCase() == "IMG" ) {
        this.onImage = true;
        this.imageURL = this.target.src;

, то теперь там такая картина

Windows; en-US; rv:1.8b2; Gecko/20050505 Firefox/1.0+
Prototype for nsContextMenu (browser.js, line 3790):

// See if the user clicked on an image.
if ( this.target.nodeType == Node.ELEMENT_NODE ) {
     if ( this.target instanceof Components.interfaces.nsIImageLoadingContent &&
          this.target.currentURI != null ) {
        this.onImage = true;
        this.imageURL = this.target.currentURI.spec;

Эта картина изменилась ещё больше в сборке от 20050508

Firefox 1.0:
var uri = Components.classes['@mozilla.org/network/standard-url;1']
                    .createInstance(Components.interfaces.nsIURI);
uri.spec = this.imageURL;

trunk (20050508):
var uri = this.target.QueryInterface(Components.interfaces
              .nsIImageLoadingContent).currentURI;

и я уже не уверен, что на этом всё и закончится.

В данный момент я поступаю следующим образом:

eval('nsContextMenu.prototype.setTarget = ' +
      nsContextMenu.prototype.setTarget.toString()
        .replace(/this\.target\.currentURI(\.spec)?/g, 'this.target.src'));

+ ещё три eval для .initMiscItems, .toggleImageBlocking, .isImageBlocked в духе

eval('nsContextMenu.prototype.initMiscItems = ' +
      nsContextMenu.prototype.initMiscItems.toString()
        .replace(/var uri = this\.target\.QueryInterface\(Components\.interfaces\.nsIImageLoadingContent\)\.currentURI;/g, 'var uri = Components.classes[\'@mozilla.org/network/standard-url;1\'].createInstance(Components.interfaces.nsIURI); uri.spec = this.imageURL;'));

Т.е. опять возвращаю нужные мне места исходников в состояние Firefox 1.0. Такие хаки мне, естественно, жутко не нравятся.

Если бы представилась возможность в nsIContentPolicy::shouldLoad просто возвращать true для разрешённой к загрузке графики и false для блокируемой, не трогая contentLocation.spec, и при этом заставлять браузер проводить отрисовку места под блокированную графику, то это было бы замечательно. К сожалению, поиски пока что ни к чему не привели и нет ни одной мысли о каком-то простом и быстром варианте.

Я продолжу искать, но если кто подскажет направление, буду рад.

спустя 1 час 42 минуты [обр] Владимир Палант(434/4445)[досье]
Мне эти хаки тоже очень не нравятся. Я так понимаю, всё это для того, чтобы контекстное меню дальше считало заблокированную картинку картинкой? Так может не обнулять spec, а заменять на resource:///res/broken-image.gif?
спустя 7 часов [обр] Данил Иванов(5/30)[досье]

Нет, так не получится.

Первая причина: в этом случае всё равно придётся использовать хаки, т.к. FF будет брать именно currentURI.spec для пунктов контекстного меню "View Image", "Copy Image Location" и пр., т.е. resource://...

Причина вторая:

Там ещё с DOCTYPE свистопляска. Например, при xhtml11.dtd для <img alt="alt" src="img.gif" width="500" height="500"/> при таком подходе (просто замена, без танцев, код которых привёл ниже) будет отображено только содержимое alt, причём место под картинку будет именно такого размера, чтобы поместился этот alt, без учёта указанных width/height.

По поводу обнуления contentLocation.spec я не совсем точно выразился. Сейчас в коде есть такой бардак:

shouldLoad: function(contentType, contentLocation, context, obj) {
... ... ...
    //~ 1) Double request, ugly, but need for placeholders.
    //~ 2) DHTML stuff blocked here (mouseOvers on blocked images
    //~ with they "src" change; etc.)
    if (obj.iloFixP && obj.getAttribute("ilo-ph-fix")) {
        if (obj.iloFixP == "fixed")
            return iBlock(obj, contentLocation);
        
        //~ else: iloFixP == "2fix"
        obj.iloFixP = "fixed";
        contentLocation.spec = "chrome://imglikeopera/content/fakeurl";
        return true;
    }
... ... ...
}

function iBlock(obj, contentLocation)
{
    contentLocation.spec = "";
    
    var oW = parseInt(obj.width || obj.style.width);
    var oH = parseInt(obj.height || obj.style.height);
    
    //~ set placeholder size
    if (!oW && !oH) { obj.setAttribute("ilo-ph-fix", "tofix"); return false; }
    else if (oW && !oH) { obj.iloFixSH = true; obj.height = parseInt(oW / 0.668); }
    else if (!oW && oH) { obj.iloFixSW = true; obj.width = parseInt(oH * 0.668); }
    
    var srcWas = obj.getAttribute("src");
    
    if (!srcWas) {
        obj.removeAttribute("data");
        return false;
    }
    
    if (obj.tagName.toLowerCase() == "input")
        return false;
    
    if (obj.getAttribute("alt") == null)
        obj.setAttribute("alt", "Image");
    
    obj.setAttribute("ilo-ph-fix", "fixed");
    obj.iloFixP = "2fix";
    
    obj.setAttribute("src", srcWas);
    return true;
}

Т.е., если графики нет в кэше, то сначала работает iBlock, где обнуляется contentLocation.spec, рассчитывается размер и пр., а потом элемент (после obj.setAttribute("src", srcWas)) идёт на второй запрос, где contentLocation.spec присваивается значение chrome://...

Жуткая вещь, хотел пересмотреть на досуге, но... работает. :) Вот только всё это очень и очень криво. А теперь ещё эти trunk'и...

Где-то ведь в FF зашито, что при таких-то условиях нужно отрисовывать такой-то placeholder, при других — такой-то.

спустя 6 минут [обр] Данил Иванов(5/30)[досье]
Пока всё это писал, понял, что скорее всего все эти потуги с установкой размеров нужны только для XHTML, где картинки по-умолчанию идут как inline элементы. Если такие танцы с бубном нужны лишь для "Standards compliance mode", а для "Quirks mode" достаточно просто менять contentLocation.spec, после чего сразу возвращать true, то видимо нужно найти то место в коде браузера, где этот момент (зависимость отрисовки placeholder от mode) определяется и посмотреть что там происходит.
спустя 50 минут [обр] Данил Иванов(5/30)[досье]
Извиняюсь за сумбурность описаний, но за сегодня уже просто намаялся с этими .spec. Гложет чувство, что я упускаю из вида какие-то очевидные вещи.
спустя 1 день 2 часа [обр] Данил Иванов(5/30)[досье]

Вроде бы нашёл один метод, который позволит обойтись без eval'ов. Примерный красноглазый набросок пока такой:

shouldLoad: function(contentType, contentLocation, context, wnd)
{
    if ((contentType == 3) &&
        (contentLocation.scheme == "http" || contentLocation.scheme == "https"))
    {
        try {
            if(!wnd.getAttribute)
            return true;
        } catch(e){}
        
        if (wnd.getAttribute && wnd.getAttribute("xxx"))
        {
            var uri = Components.classes['@mozilla.org/network/standard-url;1']
                                .createInstance(Components.interfaces.nsIURI);
            uri.spec = wnd.src;
            wnd.currentURI = uri;
            wnd.removeAttribute("xxx");
            return true;
        }
        contentLocation.spec = "";
        wnd.setAttribute("xxx","xxx");
        wnd.src = wnd.src;
        return true; //nevermind...
    }
}

На 20050508 не будет работать пункт контекстного меню "Copy Image Location", но в 20050511 всё должно обстоять чуть иначе, во всяком случае, я буду надеяться на это. :)

Другое дело, что с 20050511 не удалось толком поиграться, там другая проблема: bug 293778 "bookmarks toolbar missing in 2nd opened window, links in second window possibly cause crash", Adblock 0.5.2+ installed. Adblock bookmarks не сразу рушит, а вот ILO на первом же окне. Эх.

спустя 13 часов [обр] Владимир Палант(434/4445)[досье]

От eval'ов можно избавиться всегда, не в этом проблема (посмотрите конструктор объекта Function).

Зашито, что и как отображать, на весьма низком уровне: nsImageFrame, метод HandleLoadError(). Там и увидел, что nsIContentPolicy::shouldLoad()/shouldProcess() больше не возвращает PRBool, теперь возвратное значение — short. Варианты определены в том же интерфейсе как константы: REJECT_REQUEST, REJECT_TYPE, REJECT_SERVER, REJECT_OTHER. В вашем случае, похоже, надо возвращать REJECT_TYPE. Для совместимости лучше всего проверять, определена ли в интерфейсе такая константа — если нет, то возвращаем false, как и раньше.

Похоже, что сейчас действительно всё работает через свойство currentURI, так что достаточно его изменить (даже copy image location). Этот файл, правда, не менялся уже давно, поэтому не знаю, чем может отличаться 20050508.

спустя 1 час 52 минуты [обр] Данил Иванов(5/30)[досье]
От eval'ов можно избавиться всегда, не в этом проблема (посмотрите конструктор объекта Function).

Да, проблема в том, что приходится хакать, а уж через eval это делается или ещё как — вопрос уже второй, согласен.

nsIContentPolicy::shouldLoad()/shouldProcess() больше не возвращает PRBool ... Для совместимости лучше всего проверять, определена ли в интерфейсе такая константа — если нет, то возвращаем false, как и раньше.

О как. Ясно.

По поводу совместимости думаю так: выйдет FF1.1 — расширение будет работать только с этой версией браузера.

Во-первых, там уже сейчас (если я не ошибся; очень бегло смотрел и уже не помню какой транк) можно определить документ, которому принадлежит графика из CSS (как помните, эту возможность поломали в FF1.0). Давно хочу поменять логику работы, выкинув глобальную для всех окон опцию "Загружать фоновые изображения".

А во-вторых,.. у меня недостаточно знаний (да и времени) для того, чтобы бегать между версиями. Тут бы хоть с одной-то справиться. Сейчас, например, хорошо задумался на фразой "определены в том же интерфейсе" и лишь после неимоверных двухминутных усилий понял что сие означает. :)

За ссылки/мысли в очередной раз спасибо, буду пережёвывать.

Powered by POEM™ Engine Copyright © 2002-2005