はしくれエンジニアもどきのメモ

情報系技術・哲学・デザインなどの勉強メモ・備忘録です。

Firefox のWebExtensions のチュートリアルを試してみた

Firefox のWebExtensions のチュートリアルを試してみた

Firefox 44 から拡張機能は、 XUL からChrome, Operaも採用している WebExtensions が推奨されるようになりました。

Firefox でのWebExtensions の拡張機能の作り方 のチュートリアルを試してみたのでメモ。

環境

試した環境は以下です。

署名なしインストール

Firefox拡張機能(アドオン)は、 署名がないとインストールできないが、 Nightly やDeveloper Edition であれば、 設定してインストールすることができる。

  1. Firefox のアドレスバーに about:config と入力して下さい。

    (「細心の注意を払って使用する」をクリック)

  2. 「xpinstall.signatures.required」 を検索して下さい。

  3. 設定をダブルクリックするか、右クリックして "切り替え" を選択して、false にセットしてください。

    「xpinstall.signatures.required」をfalseに

引用: https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6

デバッグ

拡張機能のコードに、 console.log(); しても コンソール(DOM Console)には表示されない。

「ブラウザツールボックス」のコンソールでデバッグすることができるので、 その設定をする。

  1. Developer Toolsから「開発ツールのオプション」を開く。

  2. 「ブラウザとアドオンのデバッガを有効」と 「リモートデバッガを有効」 にチェックを入れる。

    「ブラウザとアドオンのデバッガを有効」と 「リモートデバッガを有効」 にチェック

引用:https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Debugging

「Web開発ツール」から、 「ブラウザツールボックス」を開く。 このコンソールでデバッグできる。

WebExtensions の構成

Google Chrome のWebExtensions とほぼ同じ設定。
Doc: Anatomy of a WebExtension - Mozilla | MDN

manifest.json ファイルに、 backgroundcontent_scriptsbrowser_actionのファイル、 web_accessible_resourcesファイルの設定をする。

background

バックグラウンドで動くjs, html を指定できる。

background のjs は、拡張機能を有効にするとに実行される。 以下の特徴がある。

  • WebExtension API をすべて使うことができる。

  • XHRを使ってCross-originのアクセスができる。

  • popup を設定してなくてもBrowser actionsを使うことができる。

また、 なので、WebExtension API を一部しか使えないcontent_scripts と連携して 使ったりする。

content_scripts

Webページを操作するJS とCSS を設定できる。 特徴は以下の3つ。

  • Cross-domainのXHRでアクセスできる。

  • WebExtension APIの一部を使うことができる。

  • background (scripts)とmessage を交換して、WebExtensionAPIを間接的に 全部使える。

browser_action

popup を設定することで、 toolbar にアイコンを配置できる。 popup用のメニューになるようなhtml, css, js を指定できる。 jsは、アイコンをクリックした時に有効になる。

web_accessible_resources

アクセスできるリソースを設定する。画像、HTML, CSS, JS など いろいろなファイルを設定できる。 jQuery等のファイルも指定できる。

チュートリアル

browser_action(popup)を使ったチュートリアル

「beastify」というbrowser_action を使ったチュートリアルを試してみたのでメモ。

toolbar に表示されるアイコンをクリック。 メニュー用HTMLにあるリスト(Frog, Turtol, Snake) をクリックすると、 現在のWebページ(DOM)にその画像を追加する拡張機能です。

Tutorial: Walkthrough - Mozilla | MDN

プロジェクト(ディレクトリ)の作成

適当な場所に、プロジェクト用のディレクトリを作成。


mkdir project_dir
cd project_dir

manifest.jsonファイルの作成

manifest.jsonファイルの作成する。

  • manifest_version : manifest のバージョンを指定。 現時点() では「2」が最新のようです。

  • "name" : 拡張機能の名前

  • "version" : 拡張機能のバージョン

  • "applications" : 他の拡張機能と被らないようメールアドレス形式の id などを指定。

  • "permissions": パーミッションの設定。 ["http://*/*", "https://*/*"] httpページ, httpsページのみ有効となる。

  • "browser_action":

    • default_icon : アイコン(ボタン)葉の画像を指定。

    • default_title: optionでタイトルを指定。tooltipで表示される。

    • default_title : popupするときのhtmlを指定。 アイコンをクリックすると、ここで指定したhtml が表示される。

  • web_accessible_resources : リソースを指定。今回はDOMに追加する画像ファイル。


{

  "manifest_version": 2,
  "name": "Beastify",
  "version": "1.0",

  "applications": {
    "gecko": {
      "id": "beastify@sample"
    }
  },

  "permissions": [
    "http://*/*",
    "https://*/*"
  ],

  "browser_action": {
    "default_icon": "button/beasts.png",
    "default_title": "Beastify",
    "default_popup": "popup/choose_beast.html"
  },

  "web_accessible_resources": [
    "beasts/frog.jpg",
    "beasts/turtle.jpg",
    "beasts/snake.jpg"
  ]

}

popup用のファイルの作成

popup用htmlでも、DOCTYPE宣言などして 通常のHTMLと同じように書く。

  • choose_beast.html :

    
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8">
        <link rel="stylesheet" href="choose_beast.css"/>
      </head>
    
      <body>
        <header>
            <h3>Hello World, Extensions</h3>
        </header>
        <main>
          <ul class="beasts">
            <li class="beast">Frog</li>
            <li class="beast">Turtle</li>
            <li class="beast">Snake</li>
          </ul>
        </main>
    
        <script src="choose_beast.js"></script>
      </body>
    </html>
    
  • choose_beast.css :

    
    html, body {
      width: 14rem;
      margin: 0;
    }
    
    main{
      width: 100%;
      margin: 0;
    }
    
    .beasts {
      width: 100%;
      margin: 0;
      padding: 0;
    }
    
    .beast {
      display: block;
      width: 50%;
      margin: .5rem auto;
      padding: .3rem 0;
      border-radius: .3rem;
      text-align: center;
      font-size: 1.1rem;
      background-color: #E5F2F2;
      cursor: pointer;
    }
    
    .beast:hover {
      background-color: #CFF2F2;
    }
    
  • choose_beast.js :

    chrome.tabs.executeScript(null, { file: "~~.js" }); で、acitive tab で指定のスクリプト(content_scripts)を実行させる

    chrome.tabs.query() で、 active tabの取得。

    function(tabs) { chrome.tabs.sendMessage(tabs[0].id, {beast: chosenBeast}); } : active tab にmessage を送る。

    
    document.addEventListener("click", function(e) {
      if (!e.target.classList.contains("beast")) {
        return;
      }
    
      console.log('addEventListener click');
    
      var chosenBeast = e.target.textContent;
    
      /* chrome.tabs.executeScript: active tabで指定のscript を実行させる */
      chrome.tabs.executeScript(null, {
        file: "content_scripts/beastify.js"
      });
    
      /* get the active tab */
      chrome.tabs.query({active: true, currentWindow: true},
        function(tabs) {
          //send a message to content scripts running in the active tab
        chrome.tabs.sendMessage(tabs[0].id, {beast: chosenBeast});
      });
    });
    

content_scriptsの作成

popup のjs からmessage を受け取り、 content_scripts がコールバックで動作し、 webページに画像を追加する。


// Assign beastify() as a listener for messages from the extension.
chrome.runtime.onMessage.addListener(beastify);

function beastify(request, sender, sendResponse) {
  if (!request.beast){
      return;
  }
  // DOMのbodyの中にある要素を全て削除
  removeEverything();
  // img要素を作りbody に挿入
  insertBeast(beastNameToURL(request.beast));
  chrome.runtime.onMessage.removeListener(beastify);
}

function removeEverything() {
  while (document.body.firstChild) {
    document.body.firstChild.remove();
  }
}

function insertBeast(beastURL) {
  var beastImage = document.createElement("img");
  beastImage.setAttribute("src", beastURL);
  beastImage.setAttribute("style", "width: 100vw");
  beastImage.setAttribute("style", "height: 100vh");
  document.body.appendChild(beastImage);
}

function beastNameToURL(beastName) {
  switch (beastName) {
    case "Frog":
      return chrome.extension.getURL("beasts/frog.jpg");
    case "Snake":
      return chrome.extension.getURL("beasts/snake.jpg");
    case "Turtle":
      return chrome.extension.getURL("beasts/turtle.jpg");
  }
}

ファイル構成

最終的なファイル構成

  • beastify/

    • manifest.json

    • popup/

    • content_scripts/

      • beastify.js

    • beasts/

      • frog.jpg

      • snake.jpg

      • turtle.jpg

    • button/

      • beasts.jpg

xpiファイルの作成

最後に、zipコマンドでxpiファイルを作成する。


zip -r 拡張機能名.xpi *

動作確認

xpiファイルをFirefox にドラッグしてインストール。 アイコンをクリックでのpopup は以下のようになる。
popupすると「choose_beast.html」が表示される。

background とnotificationを使ったサンプル

上記のチュートリアル ではbackground を使ってないが、このサンプルではbackground を使っている。 また、chrome API のnotification を使っている。

content_scripts とbackground がmessage で通信する。

  • permissions: notifications でnotificationを使うことができる。 content_scripts で、 content_scriptsの設定をする。 machesで 有効にするWebページを指定。
    manifest.json :

    
    {
    
      "manifest_version": 2,
      "name": "notify",
      "version": "1.0",
    
      "applications": {
        "gecko": {
          "id": "notify@my"
        }
      },
    
      "permissions": [
         "notifications"
      ],
    
      "web_accessible_resources": [
        "link.jpg"
      ],
    
      "background": {
          "scripts": ["background/background-script.js"]
        },
    
        "content_scripts": [
          {
            "matches": ["http://*/", "https://*/"],
            "js": ["content-scripts/notify.js"]
          }
      ]
    
    }
    
  • chrome.runtime.sendMessage({"url": e.target.href}); で、background にhref属性をmeassage として送る。

    content_scripts/notify.js :

    
    window.addEventListener("click", notifyExtension);
    
      function notifyExtension(e) {
        //console.log('event:  ', e);
        if (!e.target) {
          return;
        }
        var target = e.target;
        //console.log('target: ', target);
        if (target.href){
          console.log("content script sending message");
          chrome.runtime.sendMessage({"url": target.href});
        } else {
          if (!target.hasChildNodes()) {
            return;
          }
    
          var childNodes = target.childNodes;
          for(var i = 0; i < childNodes.length; i++){
            var childNode = childNodes[i];
            if(childNode.href){
              console.log("content script sending message: chilednode");
              chrome.runtime.sendMessage({"url": childNode.href});
              return;
            }
          }
        }
      }
    
  • chrome.runtime.onMessage.addListener(notify); で、 content_scriptsからmessage を受け取る。

    chrome.notifications.create({ "type": , "iconUrl": , "title": , "message": }); で、notification を生成する。

    background :

    
    chrome.runtime.onMessage.addListener(notify);
    
    function notify(message) {
      if(!message.url){
        return;
      }
    
      console.log("background script received message");
      chrome.notifications.create({
        "type": "basic",
        "iconUrl": chrome.extension.getURL("link.jpg"),
        "title": "You clicked a link!",
        "message": message.url
      });
    }
    

リンクをクリックして click event が発火するWebページでは、以下のように、 notification(通知) が生成される。 (Googleの検索結果では、 リンクを左クリックしても発火しないようです。右クリックと 新しいタブで開くだと発火するようです。)

ブラウザ右下側に、notification(通知)が表示される。