Как вы пишете HTML и JS на страницу с расширением Firefox? - PullRequest
0 голосов
/ 07 октября 2019

Я не могу понять, как внедрить HTML и javascript на веб-страницу с расширениями Firefox.

Это работает с расширением Chrome, но НЕ работает с Firefox. Обратите внимание, что я использую chrome.extension.getURL, который использует HTML и Javascript.

Вот мой манифест - обратите внимание, что я даже не использую фоновый материал

{
  "background": {
    "scripts": [ "js/background.js", "js/jquery.js"]
  },
  "content_scripts": [ {
      "js": [ "js/jquery.js", "js/chart-min.js", "js/chart-colors.js", "js/jquery-ui.min.js", "js/profiles/my_request.js", "js/options.js", "js/profiles/projects_builder.js", "js/profiles/board_builder.js", "js/profiles/my_api.js", "js/profiles/user_board_builder.js", "js/profiles/user_board.js","js/profiles/default_labels.js", "js/profiles/default_lists.js", "js/profiles/master_board.js", "js/profiles/projects.js",  "js/profiles/estimates.js", "js/profiles/new_todo_label.js","js/profiles/reports_dashboard.js",  "js/profiles/mutation_observer.js", "js/profiles/completion_chart.js", "js/profiles/cumulative_flow_chart.js"  ],
       "matches": [ "https://asana.com/*" ],
       "all_frames": true
    }],
  "permissions":[ "identity", "cookies", "storage", "activeTab", "https://asana.com/*"],
  "name": "Boards here",
  "short_name" : "boards",
  "version": "3.1.9.5",
  "manifest_version": 2,
  "icons"   : { "48":  "images/logo_thicker.png"},
  "description": "html for website",
  "browser_action":{
    "default_icon":"images/logo_thicker.png",
    "default_popup":"html/popup.html"
  },
  "web_accessible_resources": [ "images/*",  "html/*" ]
}

Пример для списков по умолчанию - default_lists.js использует my_request, который является просто jquery ajax-оболочкой

DefaultLists = (function() {
  function DefaultLists() {

     if (window.location.href.includes('#default_lists')) {
        this.show_form()
    }

   DefaultLists.prototype.show_form = function {
        my_request.ajax({
            url: chrome.extension.getURL("html/manage_default_lists.html"),
            type: 'get',
            success: function (data) {
              $('.panel.panel--project').remove()
              $('.panel.panel--perma').html(data)
            }
         });
    };
  }

  return DefaultLists;
})();
window.default_lists = new DefaultLists();

Так что теперь manage_default_lists.html выглядит примерно так:

<section style="position:relative;top:-50px;" class="message-board__content">
<bc-infinite-page page="1" reached-infinity="" direction="down" trigger="bottom">
  <table class="my_labels" data-infinite-page-container="">
      <tbody>
        <tr id="loading_lists" >
          <td>
            <span style="margin-left:280px;color:grey">Loading...</span>
            </td>
          </tr>
        <tr id="create_row" style="display:none">
          <td>
            <span class="">
              <input id="new_label_name" placeholder="List name" type="text" style="width:180px;font-size:15px;margin-left:42px;padding:5px;border-radius: 0.4rem;border: 1px solid #bfbfbf;" value="">
              <a style="margin-left:10px;float:right;margin-right:80px" class="cancel_new btn--small btn small" href="#">cancel</a>
              <input id="create_label" style="float:right;" type="submit" value="save" class="btn btn--small btn--primary primary small" data-behavior="loading_on_submit primary_submit" data-loading-text="saving…">
            </span>
          </td>
        </tr>
      </tbody>
    </table>
  </bc-infinite-page>     
</section>
</article>

  <script>
  $('#cancel_delete_label_button').on('click', function(event){
    $('#delete_label_modal').hide()
  });

  $('#cancel_force_lists_button').on('click', function(event){
    $('#force_lists_modal').hide()
  });

  $(document).off( "click", '.edit_label').on('click', '.edit_label', function(event) {
    td = $(this).parents('td')
    td.find('.show_row').hide()
    td.find('.edit_row').show()
    event.preventDefault()
  });

  $(document).off( "click", '.cancel_edit').on('click', '.cancel_edit', function(event) {
    td = $(this).parents('td')
    td.find('.show_row').show()
    td.find('.edit_row').hide()
    event.preventDefault()
  });

  $(document).off( "click", '.cancel_new').on('click', '.cancel_new', function(event) {
    // console.log('cancel')
    $('#create_row').hide()
    event.preventDefault()
  });

  $(document).off( "click", '#new_label_button').on('click', '#new_label_button', function(event) {
    $('#create_row').show()
    $('#new_label_name').val('')
    event.preventDefault()
  });

  $(document).off( "click", '#labels_article').on('click', '#labels_article', function(event) {
    // console.log(event.target.className)
    if (event.target.className != 'color-editor-bg'){
      $('.label-colors').hide();
    }
  });


</script>

1 Ответ

0 голосов
/ 08 октября 2019

Вы добавляете этот HTML-код на веб-страницу, используя свой скрипт содержимого и web_accessible_resources. Это не связано с CSP расширения, который запрещает встроенные скрипты на страницах расширений, таких как всплывающее окно browser_action или страница параметров. В вашем случае CSP страницы относится к материалам, которые вы добавляете в нее. На странице могут быть легко запрещены встроенные скрипты, сайты часто делают это.

Вы можете либо переписать HTTP-заголовок Content-Security-Policy страницы, используя API webRequest, либо немного переработать код , последний является лучшим решением не только потому, что он более сфокусирован, но и потому, чторезультат перезаписи HTTP-заголовка является случайным, когда несколько расширений выполняют его в одном и том же HTTP-запросе.

Итак, давайте переделаем код:

  • сохраните сценарии отдельно
  • извлекать и вставлять через browser.tabs.executeScript
  • запускать внедренный код, когда мы этого хотим

Важное изменение поведения заключается в том, что код будет выполняться в скрипте содержимого. контекст, используя jQuery и переменные сценария содержимого. Ранее ваш код выполнялся в контексте страницы, поэтому он использовал jQuery и другие переменные страницы.

Я использую Mozilla browser WebExtension, пространство имен, полифилл и асинхронный / ожидающий синтаксис.

manifest.json:

"background": {
  "scripts": [
    "browser-polyfill.min.js",
    "background.js"
  ]
},
"content_scripts": [{
  "js": [
    "browser-polyfill.min.js",
    "content.js"
  ],
  "matches": ["........."]
}],

структура каталогов:

  • html / manage_default_lists.html
  • html / manage_default_lists.js

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

content.js:

async function getResource(name) {
  const htmlUrl = browser.runtime.getURL(`html/${name}.html`);
  const [html, scriptId] = await Promise.all([
    fetch(htmlUrl).then(r => r.text()),
    browser.runtime.sendMessage({
      cmd: 'executeScript',
      path: `html/${name}.js`,
    }),
  ]);
  const js = window[scriptId];
  delete window[scriptId];
  return {html, js};
}

DefaultLists.prototype.show_form = async () => {
  const {html, js} = await getResource('manage_default_lists');
  $('.panel.panel--project').remove()
  $('.panel.panel--perma').html(html);
  js();
};

background.js:

browser.runtime.onMessage.addListener(async (message, sender) => {
  if (!message) return;
  switch (message.cmd) {
    case 'executeScript': {
      const js = await (await fetch(browser.runtime.getURL(message.path))).text();
      const id = `js.${performance.now()}${Math.random()}`;
      await browser.tabs.executeScript(sender.tab.id, {
        // 0 at the end is to prevent executeScript from additionally returning
        // the function's code as a string, which we don't need 
        code: `window["${id}"] = () => { ${js} };0`,
        frameId: sender.frameId,
        matchAboutBlank: true,
        runAt: 'document_start',
      });
      return id;
    }
  }
});
...