Пример использования telnet-сервера в firefox

Каждый раз, когда мне говорят «о! новый хромиум стал еще быстре, а новая опера — еще мелодичнее», в ответ я задаю простой вопрос: «А в вашем браузере есть telnet-сервер? А вот в firefox — есть», — после чего адепты других религий понимают, что пропаганда бесполезна.

В этой заметке речь пойдет о том, как можно расширять и управлять огненной лисицей из других приложений через вышеупомянутый telnet-сервер, реализуемый плагином mozrepl. В качестве примера я покажу, как реализовать функцию создания скриншота сайта с минимальными усилиями.

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

В числе прочих были рассмотрены варианты использования khtml2png и пары из xwd+imagemagic, но рендерялка khtml3 безбожно глючила, а второй вариант был не так тривиален, как хотелось бы.

Если все крутится вокруг бразуера — значит надо либо дописывать код к браузеру, либо воздействовать на браузер из внешней программы. В процессе поиска решения был найден чудесный плагин Screen grab!, который снимал нужные варианты скриншотов и оставалось только научиться управлять этим плагином. Вспоминая, что в случае с OpenOffice подобную функциональность можно получить используя возможность тестирования пользовательского интерфейса, я отправился на поиски тестовых плагинов. Поиск увенчался успехом и после просмотра вдохновляющего скринкаста о управлении firefox-ом из emacs-а был достаточно быстро написан черновик кода.

Идея проста до невозможности — на лету патчится функция открытия файла screengrab-а после чего вызывается одна из функций плагина и, соответственно, скриншот сохранятеся прямо в файл без каких-либо вопросов к пользователю.

Впоследствие код превратился вот в это:
var ScreenshotSaver = {
    lambda: null, // EventListener
    mixin:  null, // our file opener
    pure:   null, // screengrab's file opener
    answer: 42,   // to check `this' validity
};

ScreenshotSaver.assert = function(bool) {
    if (!bool) {
        repl.print('assertion failed');
        repl.print(arguments.callee.caller);
    }
};

ScreenshotSaver.save = function(url, fpath) {
    this._install_onload(fpath);
    window.content.location.href = url;
};

ScreenshotSaver._install_onload = function(fpath) {
    this.assert(this.lambda === null);

    // Looks like hack, but I need valid `this' in _dump_screenshot
    // I need this.lambda to be precise.
    this.lambda = function(ev) {
        repl.print('ScreenshotSaver: some `load\' event catched...');
        if (!ev.target.mIsBusy)
            ScreenshotSaver._dump_screenshot(fpath);
    };
    window.getBrowser().addEventListener("load", this.lambda, false);
};

ScreenshotSaver._uninstall_onload = function() {
    this.assert(this.lambda !== null);
    window.getBrowser().removeEventListener("load", this.lambda, false);
    this.lambda = null;
};

ScreenshotSaver._dump_screenshot = function(fpath) {
    this.assert(this.answer === 42);
    this.assert(this.lambda !== null);

    repl.print('ScreenshotSaver._dump_screenshot starts');
    /* Wanna deregister handler from itself? Here are some useless links:
     * http://code.activestate.com/recipes/576366/
     * http://en.wikipedia.org/wiki/Fixed_point_combinator
     * eval:"repl.print(arguments.callee)" */
    this._uninstall_onload();

    this._install_screengrab_mixin(fpath);
    Screengrab.grabCompleteDocument();
    this._uninstall_screengrab_mixin();

    repl.print('ScreenshotSaver._dump_screenshot exits');
};

ScreenshotSaver._install_screengrab_mixin = function(fpath) {
    this.assert(this.mixin === null && this.pure === null);

    this.mixin = function(defaultName) {
        var file = Components.classes["@mozilla.org/file/local;1"].
                        createInstance(Components.interfaces.nsILocalFile);
        file.initWithPath(fpath);
        return file;
    }

    this.pure = SGNsUtils.askUserForFile;
    SGNsUtils.askUserForFile = this.mixin;
};

ScreenshotSaver._uninstall_screengrab_mixin = function() {
    this.assert(SGNsUtils.askUserForFile === this.mixin);
    this.assert(this.mixin !== null && this.pure !== null);

    SGNsUtils.askUserForFile = this.pure;
    this.pure = null;
    this.mixin = null;
};

Скачать файл можно тут.

Загружается такое «расширение» на лету не менее тривиально:
$ firefox &
$ telnet localhost 4242 # 4242 — порт, на котором слушает mozrepl
repl> repl.load('file:///home/luser/ScreenshotSaver.js');
repl> ScreenshotSaver.save('http://habrahabr.ru', '/tmp/habr.png');
repl> ScreenshotSaver.save('http://linux.org.ru', '/tmp/lor.png');

После чего в /tmp появляется два соответствующих скриншота.

Как еще можно использовать mozrepl? Да как угодно — от автоматизированного тестирования веб-сайтов и «интерактивной консоли» при разработке плагинов для firefox до www-ботов, которые будут практически не отличимы от людей без проведения теста Тьюринга.

UPD: спасибо за карму, перенёс топик в «огненного лиса».

Из открытых вопросов остаются, например, обработка ошибок и скрывание вертикального scrollbar-а. При разрешении 1024x768 сделанный таким образом скриншот будет иметь ширину 1009px, т.е. будет уже на ширину полосы прокрутки — 15px.
+48
19 января 2009, 21:53
51
darkk 44,6

комментарии (34)

+16
drunk #
Вот именно за то, что к лисе можно прикрутить хоть черта с рогами я ее и люблю.
+6
darkk #
А саму лису — к emacs-у :-)
Жаль, что не смог найти тот чудесный скринкаст.
+6
almaz #
вроде почти ко всем браузерам можно прикрутить почти всё, но вроде только один из двух сделан для этого бесконечного процесса
+5
merlin_rterm #
ладно, telnet-сервер

там есть pow, Plain Old Webserver. По умолчанию — виснет на 6670 порту, можно писать скрипты на встроенном server javascript, или прикрутить cgi — perl, php уже прикручены, tcl я прикрутил сам.
Есть sqlite.

И, получается, никакой apache или там mysql для разработки сайтов не нужен.
+3
drunk #
А вы использовали уже лису для разработки вместо апача и скуля? Может статейку напишете на хабр? Очень любопытно.
0
merlin_rterm #
Ну я вообще-то не разработчик, скорее админ. Ничего в таком окружении, короче, не разрабатывал. Но когда надо было срочно вебкамеру потестить, и куда-то флеш-плейер, или джава-класс положить — очень пригодилось встроенное в Fx.
+3
drunk #
Какая разница. Тем более, по статистике, многие админы со временем переквалифицируются в разработчиков. Я кстати сам сейчас встал на эти рельсы. Рассказывайте, тем более у вас уже опыт c примерами какой никакой есть. ;)
–1
darkk #
sqlite все-таки заметно отличается от того же mysql или postgresql, хотя for fun — почему бы и нет.
0
mayhem #
чет у меня консоль кривая. и телнет попробовал и путти, а оно криво выводит… :(
0
darkk #
У меня в Gentoo telnet сносно работает.
Правда, истории ввода нет и клавиши управления курсором не работают, но плагин их и не реализует скорее всего.
0
zupernintendo #
«А в вашем браузере есть telnet-сервер? А вот в firefox — есть»
«По умолчанию — виснет на 6670 порту, можно писать скрипты на встроенном server javascript, или прикрутить cgi — perl, php уже прикручены, tcl я прикрутил сам. Есть sqlite.»

Ага, «ждем с нетерпением новой платформы разработки исполнения веб-приложений на десктопе».
+9
alexxxst #
Зачем?
0
unhappy #
тоже считаю, что кофе должна варить кофеварка, а приспособленная для этого стиральная машина :)
разве что исключительно для удовлетворения здорового любопытства :)
+1
darkk #
Но когда кофеварку можно подключить к сети и управлять ей через HTCPCP — это здорово :-)
0
unhappy #
верно! :)
просто важно к этому относиться без фанатизма и не делать из кофеварки домашний мультимедийный центр, мотивируя тем, что это в принципе осуществимо :D
0
darkk #
Да, из кофеварки сложно сделать медиацентр, но вот попробовать сделать электрошуруповёрт из кофемолки в час ночи — почему бы и нет :-)
0
grin #
что значит зачем? есть задача, делать скрины сайта.
решение отличное, топикастер молодец
+1
AxisPod #
Я конечно все понимаю, но тот-же IE тоже позволит скрины без проблем делать, если ему что-нить дописать, да и уже есть реализованные вещи.

Да и например нужен ли telnet в повседневной работе каждому? А вот быстрый JS не помешает. Всегда будет спор, одним нравятся понты, но при этом почти всегда бесполезные в хозяйстве и будут рядовые вещи, которые нужны всем. И решающий голос всегда будет за тем, что требуется большинству.
+3
darkk #
В моём случае IE плохо запускался на сервере под FreeBSD… Конечно, в повседневной работе каждому такой плагин не нужен. Также в повседневной работе каждому не нужны python, ruby и javascript, а нужен лишь софт, написанный с их использованием.
А вот, например, разработчику mozrepl может быть весьма полезен при том не только как средство отладки.

Кстати, если вспомнить про 80/20 — большинству нужно всего 20% функций, но это не значит, что 80% функций стоит выкинуть. Пример? Поддержка работы на нескольких мониторах, которая не нужна 80% пользователей (я думаю, цифра даже больше).
0
allan_sundry #
Приятно относить себя к 20% пользователей предпочитающих два монитора одному!

P.S. Скорее всего 80% работающих за одним монитором пока еще не пробовали работать за двумя :-)
0
kartoha #
А я использую плагин Fireshot для лисы, очень удобно и можно вносить комментарии по желанию и много чего другого.
0
darkk #
Screengrab был выбран, т.к. он FOSS, в отличие от шароварного Fireshot-а.
0
kartoha #
Ну он шароварный только для проф. версии, так я его юзаю бесплатно и мне пока достаточно функционала. Но подумываю о проф. версии, все таки 1200 рублей за удобную хорошую программу не жалко.
0
darkk #
Мне возможностей screengrab-а хватает за глаза, а вырезать/обрезать картинку и навешать комментариев мне проще в gimp-е — я не думаю, что превращать браузер в графический редактор — такая уж хорошая идея.

Кстати, в screengrab есть возможность работать с буфером обмена в отличие от бесплатной версии fireshot-а.
0
kartoha #
Есть возможность работать с буфером.
0
darkk #
Странно, на сайте возможность работы с буфером обмена указана как возможность Pro-версии.
0
kartoha #
Если речь идет о том, чтобы получить скрин экрана броузера и скопировать его в буфер — есть такая возможность у меня.
0
darkk #
Опять таки лицензионная чистота была важна для меня, т.к. этот код является частью системы, которая теоретически может приносить прибыль.
0
kartoha #
Так она у меня не крякнутая… Просто бесплатная версия ограничена, но все то, о чем мы с вами говорим доступно.
0
darkk #
Да, но я почти уверен, что лицензионное соглашение запрещает модификацию кода плагина (а именно это я и делаю в примере с screengrab, правда, делается это на лету) — т.е. я бы не смог автоматизировать генерацию скриншотов.
0
kartoha #
Я понимаю, все зависит от специфичности задачи. Согласен с вами.
0
bolnikh #
А что еще нормально работает из командной строки под юникс? khtml?
0
darkk #
Качество рендеринга khtml из Qt3 мне не понравилось — были проблемы со шрифтами и определением размера страницы, я использовал khtml2png. Для Qt4 и, соответственно, webkit я подобных утилит с разбегу не нашел.

Был еще вариант просто скриншотить любой браузер, посылать ему событие KeyPress(Next), кропать контент и склеивать — но моя идея сделать это легко и просто рухнула как только я понял, что страница может содержать анимированный gif в месте склейки, а следовательно тривиальным алгоритмом обойтись не получится.
0
andrewsh #
this.assert(this.answer === 42);

Лолшто? :)

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.