Добавление HTML в XUL (расширение Firefox)

#firefox-addon #xul

#firefox-дополнение #xul

Вопрос:

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

 ajax: function(url){
        var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                          .createInstance(Components.interfaces.nsIXMLHttpRequest);
        request.onreadystatechange = function() {
            if (request.readyState == 4) {
                // only if "OK"
                if (request.status == 200) {
                    var popup = document.getElementById("ajax"); // a <menupopup> element
                    var txt = request.responseText;
                    alert(txt);
                        popup.appendChild(txt);
                    } else {
                        alert("There was a problem retrieving the XML data:n"  
                            request.statusText);
                }
            }
        };
        request.open("GET", url, true);
        request.send(null);
}
  

xul / html, возвращенный с сервера:

      <menuitem>
        <html:h2><html:a href="http://google.com">Google</html:a></html:h2>
        <html:p><html:table><html:tr><html:td>xxxx</html:td></html:tr></html:table></html:p>
     </menuitem>
     <menuitem>
        <html:h2><html:a href="http://yahoo.com">Yahoo</html:a></html:h2>
        <html:p><html:table><html:tr><html:td>yyyy</html:td></html:tr></html:table></html:p>
     </menuitem>
  

Моя страница xul:

… еще немного кода

 <menu class="menu" label="test">
   <menupopup id="ajax" width="450" height="700" onpopupshowing="myextension.ajax('http://www.myserver.com/phpscript.php'>
  </menupopup>
</menu>
  

Теперь, если я прикреплю html непосредственно к menupopup с идентификатором ajax, он будет работать так, как ожидалось. Когда я прикрепляю его с помощью appendChild, это не так. Я знаю, что не могу использовать appendChild, но, похоже, в XUL нет эквивалента innerHTML. У меня нет контроля над xul / html, возвращаемым с сервера, поэтому я не могу использовать методы DOM для добавления содержимого и обхода html.

Я пытался использовать HTMLParser https://developer.mozilla.org/en/Code_snippets/HTML_to_DOM чтобы преобразовать его в объект DOM, но это, похоже, не работает, я полагаю, потому что это сокращает <menuitem> тег.

Есть идеи, как прикрепить HTML к menupopup, чтобы я мог отображать их как menuitems.

Хорошо, я попробовал подход iframe:

         ajax: function(url){
            var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
                              .createInstance(Components.interfaces.nsIXMLHttpRequest);
            request.onreadystatechange = function() {
                if (request.readyState == 4) {
                    // only if "OK"
                    if (request.status == 200) {
                       var frame = document.getElementById("frame");
                       frame.setAttribute("src", "data:text/html,"   encodeURIComponent(request.responseText));
                        } else {
                            alert("There was a problem retrieving the XML data:n"   request.statusText);
                    }
                }
            };
            request.open("GET", url, true);
            request.send(null);
    }
  

с

 <iframe id="frame" type="content" src="" />
  

Похоже, это не работает. Однако, если я выполняю оповещение о encodeURIComponent (request.responseText), оно правильно закодировано. Еще больше, если я напрямую добавлю его в iframe следующим образом:

 <iframe type="content" src="data:text/html,

Hello everyone

"/>

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

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

1. Если вы управляете сервером, возможно, было бы лучше, если бы вызов AJAX возвращал некоторый JSON с данными, которые вы хотите отобразить, а затем создавал структуры XUL / HTML на стороне клиента, используя методы DOM или XBL.

2. Я не могу этого сделать. На самом деле я получаю RSS-канал, который содержит некоторый HTML в описании. Я уже анализирую RSS-канал, чтобы извлечь из него часть HMTL. Но сложно разобрать html, а также я не знаю, что в нем будет. Поэтому я предпочитаю использовать html как есть.

3. Извините, я не заметил, что вы уже сказали, что не можете управлять сервером.

Ответ №1:

Сначала правильный ответ, который на самом деле не то, что вы ищете, но который важно опустить:

Не добавляйте / внедряйте / какой бы то ни было удаленно полученный HTML или javascript-код в области кода XUL или chrome!

Подобные вещи всегда являются уязвимостью в системе безопасности. JS является исполняемым, и HTML также может содержать исполняемые биты (например, javascript: протокол). Любой введенный код будет выполняться с полными правами браузера, что соответствует привилегиям пользователя операционной системы (которые в Windows XP соответствуют привилегиям администратора).

Вам нужно либо экранировать html, либо разобрать его и оставить только защищенные биты.

Вы не можете доверять удаленному коду, даже если он исходит с ваших собственных серверов:

  • Человек посередине атакует (http)
  • Взломанный сервер
  • Грубый администратор сервера (надеюсь, не вы ;))

Кстати: добавление / внедрение / любого удаленно извлеченного кода или HTML в chrome space приведет к отклонению затронутых версий вашего дополнения на addons.mozilla.org по причине, указанной выше.

Теперь технически правильный ответ, но не используйте с удаленно извлеченным и очищенным HTML:

  1. Вам нужно убедиться, что будет использоваться правильное пространство имен HTML ( xmlns:html="http://www.w3.org/1999/xhtml" )
  2. На самом деле вы не можете appendElement() вводить текст, а только реальные элементы DOM. Следовательно, вы должны предварительно проанализировать любой текст в DOM. Проще всего с допустимым XML (DOMParser); возможно для содержимого tag soup через скрытый iframe.
  3. Вы должны adoptNode использовать любые элементы из разных доменов
  4. Затем добавляйте элементы один за другим (каждый элемент меню и поддерево).

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

1. Проблема в том, что rss-канал содержит html-часть, которую трудно разобрать на отдельные элементы с помощью javascript (это PITA). Итак, я ищу более простой способ его добавления.

Ответ №2:

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

 <menu class="menu" label="test">
    <menupopup id="ajax" onpopupshowing="myextension.ajax('http://www.myserver.com/phpscript.php'>
        <menuitem>
            <iframe id="frame" type="content" width="450" height="700"></iframe>
        </menuitem>
    </menupopup>
</menu>
  

Затем вы можете загрузить свои данные в этот фрейм, используя data: URL. Важной частью здесь является type=»content», это создает границу безопасности между вашим кодом (chrome) и загруженным вами кодом (content). Вот почему важно, чтобы ваш документ XUL не находился в области содержимого браузера — тогда вы уже находитесь на стороне «содержимого» границы безопасности, вы не можете установить другую.

На самом деле помещение данных во фрейм работает следующим образом:

 var frame = document.getElementById("frame"); // <iframe> element
var txt = request.responseText;
frame.setAttribute("src", "data:text/html;charset=utf-8,"   encodeURIComponent(txt));
  

Для получения дополнительной информации см. https://developer.mozilla.org/En/Displaying_web_content_in_an_extension_without_security_issues (эта статья была написана специально для чтения RSS).

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

1. Я пытался использовать очень простую htmlстраницу: <html> <body> <p> Всем привет</p> </body> </html>, но она не отображается в iframe. Когда я добавляю это напрямую, вот так <iframe id=»frame» type = «content» src=»data: text / html,% 3Cp>hello% 3C / p>% 3C / html>»/> это действительно работает, но когда я использую frame.setAttribute, происходит сбой.

2. Похоже, что тег menuitem необходим для отображения html с помощью menupopup