Document.activeElement недоступен в содержимом аддонов firefox script только для Gmail

Я разрабатываю аддон Firefox, используя addon-sdk. Этот аддон добавляет пункт меню в контекстное меню, и пользователь может щелкнуть правой кнопкой мыши по любому элементу управления, чтобы активировать этот пункт меню. После активации он показывает небольшое всплывающее окно с предложениями, когда пользователь вводит что-то.

Все работает хорошо, кроме Gmail.

В Gmail код ниже не работает.

self.port.on('showPopup', function(data) {
    var active = document.activeElement;
    console.log(active.type);
    if (active && getWordUnderCaret(active).word == data.input) {
        populateSuggestions(data);
        positionPopup(active);
        stylePopup();
    }
});

Причиной отказа является document.activeElement указывает на document.body, а getWordUnderCaret терпит неудачу, поскольку он ожидает ввода /textarea. Это хорошо работает во всех других местах. Я не уверен, почему он указывает на document.body, поскольку я вижу, что основное внимание уделяется управлению вводом. Ввод document.activeElement в консоли Firebug дает мне правильный объект.

В качестве альтернативы я попытался отслеживать активный элемент самостоятельно, а не использовать document.activeElement. Но я столкнулся с такими проблемами, как упорство в этом. Я не могу использовать window, чтобы сохранить это, поскольку window является прокси-сервером. Я попытался с unsafeWindow, но не смог заставить его работать.

Мне интересно, почему это не удается в Gmail? Любая помощь, чтобы исправить это было бы здорово!

Мой код доступен в Github

Edit

Похоже, это проблема с addon-sdk. Я создал Gist, который можно использовать для воспроизведения проблемы. Он доступен здесь

Ответ 1

Я предполагаю, что вы имеете в виду большую ячейку в Gmail, где вы вводите тело сообщения, когда вы составляете электронное письмо.

"Если это случай;

Причина:

Это редактирование элемента управления в Gmail не является текстовым элементом управления (input или textarea). Это редактор WYSIWYG, реализованный с <iframe>, который сделан editable, установив document.designMode = "on" (или document.body.contentEditable = true в зависимости от поддержки браузера).

Итак, при нажатии; вы не всегда получаете нужный элемент с тем, как вы запрашиваете document.activeElement, в этом случае. Вы получаете оберточный (основной) документ body (даже не iframe body).

Например; Я полагаю, в этой строке вашего кода; вы добавляете прослушиватель событий click в основной документ. Но вы также должны добавить его в редактируемые iframes на странице; потому что страница и iframe имеют разные объекты document (и iframe не будет устанавливать себя как активные). Итак, вы получите неправильный document.activeElement.

Обход проблемы:

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

// Add mouseup event listener to the main document
document.addEventListener('mouseup', logActiveElement, false);
// Get all the iframes on the main document.
var iframeElems = document.getElementsByTagName('iframe');
// Add mouseup event listener to the document of the iframe elements
for (var i = 0; i < iframeElems.length; ++i) {
    addIframeEvent(iframeElems[i], true, 'mouseup', logActiveElement);
}

Вот вспомогательные функции:
(Обратите внимание, как мы добавляем прослушиватели событий к iframe (документу) из основного документа и как мы проверяем, может ли iframe в настоящее время редактироваться.)

// Adds the specified event listener to the iframe element. 
function addIframeEvent(iframeElem, editableOnly, eventName, callback) {
    // Get the document of the iframe element.
    var iframeDocument = iframeElem.contentDocument || 
                            iframeElem.contentWindow.document;
    // Watch for editableOnly argument. 
    if ( (editableOnly && isDocEditable(iframeDocument)) || !editableOnly) {
        // Add the event listener to the document of the iframe element.
        iframeDocument.addEventListener(eventName, callback, false);
    }
}

// Checks whether the specified document is content-editable or in design mode.
function isDocEditable(doc) {
    return ( ('contentEditable' in doc.body) && (doc.body.contentEditable === true) ) || 
        ('designMode' in doc) && (doc.designMode == "on") );
}

// Handler function
function logActiveElement() {
    console.log("Active Element:", document.activeElement);
}

Теперь обработчик события зарегистрирует правильный activeElement на консоли.

Осложнения:

В результате; такие вещи, как выбор текста и проверки в вашем существующем коде, не будут работать так же, как с обычными элементами управления редактированием (ввод, текстовое поле и т.д.).

Например; ваша функция getWordUnderCaret(editor) получает insertionPoint от editor. selectionStart, которая не существует в document.body (цели iframe). Для такого рода отбора/инспекции; вы должны переключаться между DOM Selections, Диапазоны DOM; вместо выбора текста и диапазонов в текстовом редактировании.

Примечание. Библиотека jQuery выбора текста/диапазона (Rangy), которую вы используете, promises для обработки такого вида редактируемого контента в браузере (iframes, divs и т.д.). Вы пробовали это для iframes (например, в Gmail)?

Дополнительная информация:

  • См. документация ActiveElement в Mozilla Developer Сеть. В Aditinonally это означает: "Не путайте фокус с помощью выбор по документу. Когда выбор отсутствует, activeElement - это страница <body>. "

UPDATE:

Проверить элементы управления текстом в Gmail; Я также играл с вашим примером кода;

  • Добавлены опции attachTo: ["existing", "top", "frame"] в PageMod.
  • Изменено значение contentScriptWhen на 'end' (вместо 'ready') в параметрах PageMod; чтобы убедиться, что все, включая DOM, CSS, JS, загружено. Некоторое содержимое может быть изменено через JS на странице ready/load, после чего "end" будет выполняться после него.
  • Применяет контекст селектора к пунктам меню; чтобы убедитесь, что элемент отображается только во время контекста селектора.

Протестировано в Gmail (поле поиска, окно чата и т.д.) и другие сайты; это, похоже, работает.
См./Проверьте рабочий пример здесь в конструкторе аддонов.

Gmail Search-box text input

Gmail Chat-box textarea

Ответ 2

Вы пытались использовать последнюю версию git аддона sdk? Контекстное меню было переписано с нуля, и я слышал, что он исправляет довольно много ошибок.