[Урок 8] . Корректное переключение табов в iMacros. Исправляем ошибки разработчиков при помощи JS.

Поводом для этой статьи послужила неприятная ситуация, которая периодически возникает при переключении табов при помощи команды iimPlayCode("TAB T=1");. Если вы не используете JS в своих iMacros-скриптах, то вероятность столкнуться с проблемой мала. Но к нам, продвинутым пользователям iMacros — это не относится.

Баги с табами в iMacros

Откройте 3 вкладки в браузере и создайте макрос test.js:

iimPlayCode("TAB T=1");

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

Чтобы лучше понять, как оно работает, измените код на следующий:

iimPlayCode("TAB T=2");

Теперь снова попробуйте запустить из каждой вкладки.
Из первой вкладки перекидывает на вторую — замечательно!
Из второй вкладки перекидывает на третью, как-то это не правильно, мне бы хотелось остаться на второй вкладке после выполнения кода.
Из третьей вкладки он включает таймер и в итоге выдает ошибку RuntimeError: Tab number 2 does not exist, line 1 (Error code: -971)

Это никуда не годится, зато теперь ясна логика работы этой функции — при запуске макроса, под номером один — будет текущий таб, под номером два — идущий за ним таб. Вы можете попробовать поэкспериментировать с iimPlayCode("TAB T=0"); или с iimPlayCode("TAB T=-1");

Такой вариант меня абсолютно не устраивает, потому что я хочу полностью и точно контролировать браузер и подобное поведение неприемлемо.

Решение проблемы при помощи XPCOM и функций расширения Firefox

XPCOM (англ. Cross Platform Component Object Model) — кросплатформенная компонентно-ориентированная модель разработки ПО от Mozilla. XPCOM позволяет создавать компоненты на JavaScript и других языках. Осуществляется это через слой абстракции XPConnect, а интерфейсы классов берутся из бинарных библиотек типов.

Если говорить простым языком — нам доступен огромный набор объектов, классов, компонентов, которые позволяют получить доступ к практически любым функциям браузера и даже позволяют взаимодействовать с операционной системой, например, чтение\запись файлов.

Вы можете открыть консоль в фаербаге и запустить макрос со следующим кодом:

window.console.log(Components);
window.console.log(Components.classes);
window.console.log(Components.interfaces);

И исследовать содержимое этих объектов, кликая по ним в консоли и разворачивая древовидную структуру.

iMacros components classes

Мы будем использовать компонент и сервис Window Mediator.Поскольку каждый раз для переключения табов писать простыню кода неудобно, мы обернем весь функционал в небольшой JS-объект и сможем переключаться между вкладками одной маленькой, короткой функцией.

Давайте попробуем:

var Tabs = {

	_browser: function () {			
		var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] 
			 .getService(Components.interfaces.nsIWindowMediator);		
		return wm.getMostRecentWindow("navigator:browser").gBrowser;
	}(),

	go: function (tabIndex) {
		this._browser.selectedTab = this._browser.tabContainer.childNodes[tabIndex - 1];	
	}

};

 Мы создали небольшой Javascript-объект, с которым теперь очень удобно работать. Нижнее подчеркивание для функции _browser говорит нам, что она приватная и используется только внутри объекта, можно этого не делать, но это хорошая практика написания красивого и понятного кода. Хочу обратить внимание, что эта функция вызывается только один раз, при первом вызове Tabs.go(), после чего она перезаписывается и в ней оказывается объект gBrowser со всеми его свойствами и методами.

Использовать очень просто:

Tabs.go(1);
iimPlayCode("WAIT SECONDS=3");
Tabs.go(2);
iimPlayCode("WAIT SECONDS=3");
Tabs.go(3);

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

Пишите ваши вопросы, пожелания и дополнения в комментариях.

8 комментариев

  1. Спасибо за ваш блог, очень много полезного.
    Помогите доработать пожалуйста:
    var wm = Components.classes[«@mozilla.org/appshell/window-mediator;1»]
    .getService(Components.interfaces.nsIWindowMediator);
    var mainWindow = wm.getMostRecentWindow(«navigator:browser»);
    kolvo = mainWindow.gBrowser.tabContainer.childNodes.length
    if(kolvo > 1){
    mainWindow.gBrowser.selectedTab = mainWindow.gBrowser.tabContainer.childNodes[2];
    window.close();
    }
    В условии хочу сделать переключение на 1-ую и закрытие других вкладок:
    mainWindow.gBrowser.selectedTab = mainWindow.gBrowser.tabContainer.childNodes[1];
    mainWindow.gBrowser.removeTabsToTheEndFrom(gBrowser.mCurrentTab);
    Подскажите пожалуйста

    Ответить
  2. Здрасте, спасибо за ваши труды! iimPlayCode(«WAIT SECONDS=3»); При проходе данной строки просит закрыть все вкладки. Как избавиться от этого сообщения?

    Ответить
    • Для перебора вкладок и поиска ранее открытого сайта я дописал себе определение общего количества вкладок:
      count: function () {
      return (this._browser.tabContainer.childNodes.length);
      }

      Ответить
    • Буду потихоньку статью в комментариях набирать ) Очень хороший материал, полезный, здесь: https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Tabbed_browser
      openAndReuseOneTabPerURL — функция позволяет находить вкладку по URL и открывать ее. Если вкладки с нужным URL нет — открывается новая вкладка.

      Ответить
        • Хотя, нашел сам. Может пригодится кому…
          Дополняем JS-объект из статьи. Возвращаем индекс открытой вкладки:
          index: function () {
          return (this._browser.tabContainer.selectedIndex + 1);
          }
          Применяем:
          Tabs.index()

          Ответить

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