пятница, 8 апреля 2016 г.

CustomButtons часть 3-я. Взаимодействие с интерфейсом Firefox.

  1. Откуда брать информацию о структуре интерфейса
  2. Контекстное меню
Ознакомившись с работой с файловой системой, буфером обмена, реестром и тому подобными вещами, можно уже делать много чего. Однако код с chrome-привилегиями может гораздо больше. И одной из таких возможностей является возможность взаимодействия с интерфейсом самого Firefox. Мы видели, что для создания меню кнопки требуются базовые знания DOM, но и все остальные элементы интерфейса браузера работают по тому же принципу, таким образом, используя те же приемы можно легко добавлять, удалять или изменять элементы интерфейса браузера по своему усмотрению. Вот этим мы сейчас и займемся.

Откуда брать информацию о структуре интерфейса



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

Для того, чтобы сохранить документ нам доступна простая функция saveDocument, которая принимает объект документа, показывает пользователю окно сохранения файла, и сохраняет документ. Обычно она используется для сохранения HTML-документа активной страницы, но можно передать этой функции ссылку на XUL-документ и сохранить его.
Код javascript Выделить
saveDocument(document);
Другой способ основан на том, что в Firefox для отображения исходного кода страницы используется специальный протокол view-source, с помощью которого можно отображать не только веб-страницу. Конечно, XUL-документ не будет красиво подсвечен, как это бывает с HTML, но отображается в отформатированном виде, так что тоже вполне читабелен.
Код javascript Выделить

loadURI("view-source:" + document.location.href);

Кстати этот же подход можно использовать и для просмотра других типов документов: dtd, css, properties и т. д.

Но такой подход не всегда дает нужный результат. Например, когда мы видим какой-то текст - те же надписи пунктов меню - разумно предположить, что в документе мы найдем menuitem у которого атрибут label будет иметь значение именно такое каким мы его видим в интерфейсе программы. Но в полученном вышеописанным способом документе текста на русском языке нет вообще. Лейблы просто имеют ссылку на ресурс, файл ресурса выбирается в зависимости от локализации. Но когда мы видим работающую программу, все эти надписи уже загружены и хотелось бы просмотреть XML-код именно того окна, которое мы видим. Для решения этой проблемы можно воспользоваться такой штукой, как XMLSerializer.
Код javascript Выделить

var serializer = new XMLSerializer();

var code = serializer.serializeToString(document);

gClipboard.write(code);

Контекстное меню



Если мы немного пройдемся по коду страницы, то можно найти там элемент popupset. Внутри этого элемента есть уже, знакомые нам по меню кнопки, элементы menupopup, наполненные элементами меню. О назначении каждого такого элемента можно судить по значению атрибута id, обычно оно достаточно информативно. Например первый элемент имеет id="tabContextMenu". Нетрудно догадаться, что это контекстное меню, появляющееся в области вкладок. Нас сейчас больше интересует меню с id="contentAreaContextMenu". Как нетрудно догадаться, это контекстное меню области содержимого. В принципе, его код можно получить вышеописанным способом, если поискать menuitem с каким-нибудь значением label, которое мы видим в этом контекстном меню
Код javascript Выделить

var serializer = new XMLSerializer();

var menuel = document.querySelector("menuitem[label='Исходный код страницы']").parentElement

var code = serializer.serializeToString(menuel);

gClipboard.write(code);

Данный код в моем случае действительно скопировал в буфер обмена код контекстного меню, но так можно и ошибиться, поскольку пункт меню с таким текстом есть и в других меню. Для того, чтобы найти именно контекстное меню, лучше всего искать его именно по id.

Сейчас мы будем добавлять новый элемент в контекстное меню области содержимого. Понадобится это может в тех случаях, когда некоторые действия надо выполнить над конкретным элементом страницы и лучшим способом указать такой элемент является как раз контекстное меню. Например: некоторые сайты, предоставляют ссылки, оставленные пользователями, в виде ссылок на свою специальную страницу, где адрес конечной ссылки передается как параметр. Таким образом сайты фильтруют ссылки, предупреждая пользователя о переходе на "ненадежный сайт". Иногда предупреждения бывают ну уж очень навязчивыми, то есть возможности перейти по этой ссылке сайт не дает. Например подобным образом ведет себя вконтактик. Ссылки там даются в таком виде
Цитата:
https://vk.com/away.php?to=Здесь реальный адрес в URL-кодировке&post=id поста
Причем не нравятся вконтактику зачастую совершенно безобидные, например новостные, сайты и он туда не пускает. Вот мы и создадим меню, исправляющее ссылку.
Код javascript Выделить

function addMenu()

{

    var menu = document.getElementById("contentAreaContextMenu");

    var item = document.createElement("menuitem");

    item.setAttribute("label", "Исправить ссылку vk");

    item.onclick = function (evt)

    {

        var ctx = menu.triggerNode;

        ctx = ctx.tagName == "A" ? ctx : ctx.parentElement

        var href = ctx.href;

        var startHref = "https://vk.com/away.php?to=";

 

        if (ctx.tagName == "A" && ctx.href.startsWith(startHref))

        {

            var startLen = startHref.length;

            var url = ctx.href.substring(startLen, ctx.href.indexOf("&"));

            ctx.href = decodeURIComponent(url);

        }

    }

    item.id = "correct_vk_outer_link";

    menu.appendChild(item);

}

 

 

function removeMenu()

{

    var menu = document.getElementById("contentAreaContextMenu");

    menu.removeChild(document.getElementById("correct_vk_outer_link"))

}

 

var myitem = document.getElementById("correct_vk_outer_link");

if (myitem)

{

    removeMenu();

}

addMenu();

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

Полагаю, большая часть кода здесь понятна без пояснений, пояснить надо только два момента
Код javascript Выделить

var ctx = menu.triggerNode;

Данное свойство(triggerNode) menupopup возвращает элемент, для которого было вызвано контекстное меню. В нашем случае ожидается, что это должна быть ссылка, но некоторые внешние ссылки вконтактика содержат элементы span и в этих случаях будет возвращаться ссылка именно на этот элемент. Поэтому вставлена строка
Код javascript Выделить

ctx = ctx.tagName == "A" ? ctx : ctx.parentElement

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

Можно обработать событие menupopup, которое называется popupshowing, выполнить все необходимые проверки там и, если элемент не подходит для нашего меню, то сделать меню недоступным или даже скрытым (атрибуты disabled и hidden соответственно). Примерно так
Код javascript Выделить

/*Initialization Code*/

 

function addMenu()

{

    var menu = document.getElementById("contentAreaContextMenu");

    var item = document.createElement("menuitem");

    item.setAttribute("label", "Исправить ссылку vk");

    item.onclick = function (evt)

    {

        var ctx = menu.triggerNode;

        ctx = ctx.tagName == "A" ? ctx : ctx.parentElement

        var startHref = "https://vk.com/away.php?to=";

 

        if (ctx.tagName == "A" && ctx.href.startsWith(startHref))

        {

            var startLen = startHref.length;

            var url = ctx.href.substring(startLen, ctx.href.indexOf("&"));

            ctx.href = decodeURIComponent(url);

        }

    }

    item.id = "correct_vk_outer_link";

    var onPopupShowing = function (evt)

    {

        var corItem = document.getElementById("correct_vk_outer_link");

        if (corItem)

        {

            var menu = document.getElementById("contentAreaContextMenu");

            var ctx = menu.triggerNode;

            ctx = ctx.tagName == "A" ? ctx : ctx.parentElement;

            var startHref = "https://vk.com/away.php?to=";

 

            if (ctx.tagName == "A" && ctx.href.startsWith(startHref))

            {

                corItem.setAttribute("hidden", "false");

            }

            else

            {

                corItem.setAttribute("hidden", "true");

            }

 

        }

 

    }

    menu.addEventListener("popupshowing", onPopupShowing, false);

    menu.appendChild(item);

    menu.correctVkOuterLink_OnPopupShowing = onPopupShowing;

}

 

 

function removeMenu()

{

    var menu = document.getElementById("contentAreaContextMenu");

    menu.removeChild(document.getElementById("correct_vk_outer_link"))

    menu.removeEventListener("popupshowing", menu.correctVkOuterLink_OnPopupShowing);

}

 

var myitem = document.getElementById("correct_vk_outer_link");

if (myitem)

{

    removeMenu();

}

addMenu();

 

Этот пример достаточно хорошо демонстрирует доступные нам возможности управления элементами интерфейса браузера.

Комментариев нет :

Отправить комментарий