はじめての自作Chrome Extension

社長:それではブラウザに何か機能を追加してみましょう。

開発:いわゆるプラグインですか。

基盤:Google Chrome的には「 extension 」て呼ぶようです。

社長:我が社正式ブラウザのVivaldiではどうなんでしょう?

基盤:VivaldiChromium がベースなので、共通のようです。おっと、日本語版ガイドがあるじゃないですか。

開発:「多いです」ってのがくだけけていい感じですね。

社長:Vivaldiのコミュニティっていい感じなんですかね。

基盤:いや、ブログとかボロクソ書いてる人が多いです。確かにある意味、いい感じ(笑)

開発:これ読むと、Vivaldiはユーザが拡張する必要なんて無い完璧なブラウザですからっていう気持ちが見えますね(笑)。基本的な機能についてはそうであって欲しいし、実際そうだなと感じますが。

Chromiumとextension

基盤:Chromiumベースなのは、Chrome, Edge, Opera それに Vivaldiですね。 あと Android も。EdgeがChromiumになったのは2020年1月だそうです。そういう時代なんですね。まあでも、世の中をあれだけ特殊なブラウザで苦しめた事は謝らないんでしょうけど。

開発:インターネットの普及を散々邪魔したあげく、いざ普及したら自分こそがインターネットですみたいにしれっと出てきたりする会社もいましたよね。

基盤:5大ブラウザでは Firefox と Safari が Chromeでは無いということですね。

社長:まあ、祖先をたどれば結局は Mozilla、Netscape、NCSA Mosaic に行き着くんでしょうけどね。 わたしなんてデビュー時の Mozaic から使ってますよ。英語ではモゼークって発音するらしいとか。

基盤:それで、extension てどう作るんだろう、と調べると、https://developer.chrome.com/extensions が根っこですね。

Google Chrome extensions の手引き入り口

開発:They are build on web technology such as HTML, JavaScript, and CSS。とありますから、うちの現有能力でいけますね。というか、それなら確かにポータブルだとは思われます。

社長:Apacheとかもそういうノリで作られてるといいですね。

基盤:古くはCGIで今はWordPressのプラグインでPHPとか。いずれのNode.jsとかともお付き合いするのでは。

開発:でも仕様・機能を満たしてればいいので、フロントエンド側のブラウザみたいにエンドユーザの好みで多様化っていう世界では無いですよね。

誰だって最初は Hello world

基盤:それじゃさっそく、Hello world ! 的なのを。

開発:では、このページにいきなりある Hello Extensions から。あー、この手の手引きにありがちなのですが、いきなり具体的な手順を頭から書いてますね。最初に簡単にサマリーをつけて欲しいものです。つまり、こういうことですね。

  • Chromium の extension はメタ情報と実装で構成される
  • メタ情報はマニフェストと呼ばれ、manifest.json という名前のファイルに格納する
  • manifest の中で拡張機能の外形を定義して、それを実装に対応させる
  • 実装は HTML や アイコン画像などで、そのファイル名を manifest から参照する
  • extension を組み込みには、このmanifest(を含むディレクトリ?)をブラウザに登録する
  • extension を呼び出す方法は manifest に記述する

基盤:あとはここに書いてある流れですね。

社長:これってつまり、自身に実装を持たない extension もアリエルということですよね。というか、この参照がURL一般で良いなら、data URI scheme でも行けると。

基盤:とりあえずやってみましょう。まずディレクトリを作って、このちっちゃなサンプルの json と html と png を格納します。

Last login: Mon Jun 15 01:56:30 on ttys011
MacMini% cd ~/Desktop
MacMini% mkdir ExtHello
MacMini% cd ExtHello
MacMini% cat > manifest.json
{
    "name": "Hello Extensions",
    "description" : "Base Level Extension",
    "version": "1.0",
    "manifest_version": 2,
    "browser_action": {
      "default_popup": "hello.html",
      "default_icon": "hello_extensions.png"
    },
    "commands": {
      "_execute_browser_action": {
        "suggested_key": {
          "default": "Ctrl+Shift+F",
          "mac": "MacCtrl+Shift+F"
        },
        "description": "Opens hello.html"
      }
    }
  
  }
MacMini% cat > hello.html
  <html>
    <body>
      <h1>Hello Extensions</h1>
    </body>
  </html>
MacMini% cp -p ~/Downloads/hello_extensions.png .
MacMini% ls -l
total 24
-rw-r--r--  1 xxxx  staff   74 Jun 15 13:39 hello.html
-rw-r--r--@ 1 xxxx  staff  319 Jun 15 13:41 hello_extensions.png
-rw-r--r--  1 xxxx  staff  466 Jun 15 13:39 manifest.json
MacMini% 

基盤:でブラウザに登録。登記所は Chrome では chrome://extensions だから、Vivaldi では vivaldi://extensions ですかね。

社長:まだ誰も居ませんね。

基盤:で、Load unpacked で Desktop/ExtHello を開くと・・・

基盤:追加されましたね。で、Details を見ると・・・

開発:わたしはその、「Allow access to file URLs」という設定がとても気になります。

基盤:で、manifest には「_execute_browser_action」が「"mac": "MacCtrl+Shift+F"」と定義されていましたから、その通りに押す・・・あれ、何も起こらないですね。

開発:一旦 Extension から抜けないといけないとか。

基盤:「+」で新しいタブを開いてみます・・・

基盤:あ、extension で登録たアイコンが出ましたね。あれ?でもCommand+Shift+Fで何も起こらない・・・ じゃこの「Hi」ってアイコンをクリックすると・・・

開発:出ましたね。右クリックすると?

基盤:拡張機能を直接管理できますね。でもHide Buttonて・・・ ああ、拡張機能全般のボタンの可視性制御ですかね。Vivaldi はまだよくわからないです。

はじめての自作 extension

社長:で、そこに一般のURLを直接書けるのか気になるのですが。

基盤:では、こんなふうに改変して、Update ・・・

基盤:なんか、Updating ... で固まりますね。version を変えるとアップデートできないとか?

開発:一旦 Remove して Load してみましょうか。しかしこれ、manifest に作成日付が無いっていかがなもんでしょう?

基盤:ぽち。ぽちっと。

開発:登録されましたね。で「Hi」をクリックすると・・・

基盤:出ました。窓がちっちゃいけど。

開発:これ、普通にブラウズできるのかな?

基盤:メニューをポチッと。

基盤:普通にブラウザのようです。

社長:なるほど。。。てことは、manifest だけ配って、実装は手元に置いて更新するってことができるわけですかね。

基盤:たぶん。

開発:といいますか、いっそ manifest の URL だけ配るって事はできないんですかね?

社長:それだとちょっと、ブラウザ側で何も extensions を管理できなくないですかね。まあそれはともかく、大変わかりやすいフレームワークであるという事はわかりました。一旦休憩しましょう。

* * *

jQueenアイコン作成

広報:さっそくですが、jQueen のアイコンを作成してみます。

社長:ちょっとその前に。上の画像をみて気づいたんですが、Vivaldiが右下に時刻表示をしているので、時間経過がわかってとても良いなと思います。

基盤:その時間より前には存在してなかたっという証明ができたらすごいですけどね(笑)

社長:これいっそ、タイムスタンプ付きで電子署名すると良いかなと。PNGのコメント的なチャンクに入れられますよね。

開発:できると思います。ただ、圧縮されたりした時に捨てられるかもですが。

社長:署名データ自体を画像として透かしみたいにできないですかね…

経理:証明書のモトはきっちり取らないと。まだ500回くらいしか署名してないかと。目指せ1署名1円です。

基盤:そういえば今回のAzureの課金は、ちょうど1日100円でしたね。

広報:それで、OneDrive上のロゴ集のパワポファイルを開こうとしたのですが、いつまでたってもクルクルしてて開かないのです。というか、いくつかのサイトにアクセスができなくなってます。

基盤:うーん。psするとプロセスが550ありますね。CPU負荷的には大した事ないですが、仮想メモリ4000GBを超えてます。昨日から動かしはじめたタイムマシーンが疑わしいですが・・・今は休憩中ですね。なんにしろOperaの残骸のタブとVivalidiのと100以上タブ開いてますしね・・・ うーん、topで見ると毎秒1万回くらいスワップインしてますね。ていうか、それでも割と普通に使えてるんですけどね。こないだ入れた自家製のDNSサーバ怪しくないですか?

開発:うーん、DNSのログをみると、異常は無いようなあるような。ただ、これだけヘビーにリカーシブなサーバとして動かしたことは過去に無いかもしれない。デフォの並列2プロセスというのも問題かも。そもそも内部的なRRのキャッシュがいっぱいになったとか?とりあえず delegated -r ... 。おっと、回復しましたね。どうも、しかるべき使い方をしていなかったようです(^-^;

基盤:それじゃOperaもちょっと死んでもらいます。どうせ完璧に蘇るでしょうしね。terminate。。。おおっ、固まった!すごい、load avarage 170達成です。それでも動くtopはエライなあ。。Operaプロセス全滅しました。5分くらいかかりましたね。総仮想メモリ3000GBに低下。ロードアベレージ10前後になりました。普通に使えますね。

広報:パワポ、さくっと立ち上がるようになりました。ですがさっきやりかけた編集で大事なスライドが一枚飛んでしまったようです。

基盤:そういうときはOneDriveに感謝しつつ昔のを回復ですね。

基盤:削除したわりには、いきなりサイズが大きくなってますが。しかもなんだか不自然なッジャスト800KB。。で、壊れてないらしき6月2日のを Restore 。。。復活しました。ていうか、Office 365で出来なて不便だなって思ってた、古い版を回復するんじゃなくて取り出すというのが、OneDrive ではできるんですね。これは朗報です。

開発:で中身は?

広報:ほっ。元のが回復しました。それでは、ちょっと時間がなくなってしまいましたので、適当にサクッとこんな感じで。

社長:ああ、とりあえずこれでいいんじゃないですかね。jQueern のアイコンとして表示してみましょう。

基盤:では、manifest.json のアイコン名を変えて、Update。。 やっぱり固まりますね。RemoveしてLoad。出来ました。

社長:おっと、ちゃんと jQ って読めますね。

開発:経費でハグキルーペへの野望が遠ざかりました(笑)

経理:ちょっとあれ、あり得ないです。原価数百円じゃ無いんですかね。調達請求出てたの保留中です。

基盤:それはそうと、今気づいたんですが、Vivaldi ってコンテンツの表示のズームと別にVivaldiのアイコンとかテキストとか(ユーザインタフェイスの)ズームができるですね。例えばこれ、UI 300%。

開発:うーむ。わたしらが欲しいと思ってるものの倍返ししてくるブラウザですね。そもそも離散的でなく連続的です。全部のアプリがこうなってたら、マジはゔきルーペ、いらねーな。

社長:これは本来ならMacOSで統一管理するべきところだとは思いますけどね。

基盤:お腹が空きました。

社長:飲みに行きますか。

* * *

コンテクストメニュー

社長:ひさしぶりでしたが夜に飲むのも良いものですね。

開発:事始めの〆はやはりコンテクストメニューで。

社長:context-menu.click は我が社のものですからね。

基盤:ところでこの extension のチュートリアルですが、Creative Commons (BY) だってあります。

社長:これ、なんかクールだね。うちも入れようか。

基盤:具体的には利用する時どうすればいいんですかね。引用元明示という意味ではURLを書いているからそれでいいのかなと。

開発:うちは Techno Comical Creative Commons とか標榜したいかと。

基盤:context menu のサンプルはこれですね。

基盤:これをさっきのにマージして、Remove + Load ・・・ なんかエラーが出ますね。

基盤:sample.js の最後の部分ですね。なんかテスト用にわざとやってる感じ。

基盤:じゃ、これを外して再ロード。

基盤:OK。

開発:では早速クリックをば。ワクワク。

基盤:右クリック!

社長:おー、出た出た。

開発:なるほど。でもこのサンプル、最初の一歩向けにしてはちょっと複雑ですね。あと、manifest.json にも sample.js にも「右」クリックだということは書いてない。コンテクストメニューを右クリックで出すというのはブラウザが決めることなんでしょうね。

社長:他と同居するとメニューが複雑になるから、Shift+右クリックとかにして景色をさっぱりできると良いかもですね。

開発:ともかく、とりあえず気になるコンテクストは現在のページのURLですから、これをメニューに出すには。。こんな感じですかね?

var jqueen = chrome.contextMenus.create(
  {"title": "Go upper layer"});
var uee = chrome.contextMenus.create(
  {"title": document.URL, "parentId":jqueen});

開発:で、extension reload して右クリック...

基盤:コンテクストが違いましたね、残念 (^-^)/

開発:うーん、とりあえず console log で追いたいですね… でもなんだか DevTools の console に出てこないような …

* * *

開発:んー?何だコリア。inspect views ? ・・・ おお!コンソール出た。こいつは便利。

開発:それでは早速、ページ移動して右クリック。

基盤:出ましたね。あとはこれを昨日の関数で分解してどっかに表示すると。

開発:ふー。ログさえ見れれば千人力。extension もちょといじって Updateで速反映できることがわかったし。素晴らしいです。ちょっとコーヒーブレイク。

* * *

経理:そういえば年金機構とけんぽ協会から封書がきてました。年金機構からは「報酬月額算定基礎届」を提出しろと。7月10日締め切りです。健保協会からは健診補助のお知らせ。

社長:リアル社員約1名、昇給、賞与無し。業績悪化により6月分は役員報酬ずばっとカット。そういう感じ(笑)

経理:最近設備投資で支出が増えましたしね。

* * *

開発:で、昨日の版の URL 分解はいきなり document.write してたから、今度は文字列を作って返す関数にしましょう。こんな感じ。

/*-- UrlEx HTML --*/
function surl2ahtml(surl,action){
  var pathv = surl.split('/');
  var aurl = "";
  var purl = "";
  var pi;
  var ahtml = ""; 
  for( pi = 0; pi < pathv.length; pi++ ){
    aurl += pathv[pi] + "/";
    purl += pathv[pi] + "/";
    if( pi < 2 ) continue; /* skip scheme:// part */
    ahtml += 
      "<a "+"href=" +aurl+ " " +action+ ">" +purl+ "</a>";
    purl = "";
  }
  return ahtml;
}

開発:でもってこれを jQueen にいれて。右クリック。

基盤:OKですね。

開発:さて、これをどうやってユーザに見せるかなのですが。今回は客人なのでユーザのHTMLの中に埋め込むわけにはいかない。

基礎:基本中の基本を知らないというこの面白さ(笑)

開発:もとのページにポップアップを定義しておいてその可視性を制御するという方法が使えないですからね。ウィンドを定義して開くのかな。ああ、多分この window.open() てやつだ。

function jpop(ahtml){
  window.open('https://its-more.jp', "_blank", 
    "menubar=0,width=300,height=200,top=100,lef=100");
}

開発:OK。とりあえずポップしました。もう刹那的に、 data URI で開いちゃってはどうかな?

基盤:怒られましたね(笑)

開発:うーむ。テンポラリなコンテンツを作ってその仮想的な?URLで開くか、開いているウィンドウにコンテンツを流し込むか…

基盤:blank っていうページがありましたよね。about:blank 。あれとかそのためにあったりして?

開発:about:blank 開きますね。で、これの document をどう参照するかですが…

開発:おっと、JavaScript って関数の定義は後で書いても良いのか!便利じゃのお。

開発:ん?いつも使ってるdocument て、window.document の略じゃ無いんだろうか?て事は、新しく開いた window のドキュメントに普通に write すれば良いような気がする。自分で作った window なんだから人に咎められるようなことでは無いよね。つまり、これでイケるのではなかろうか?

function jpop(ahtml){
  jwin = window.open("about:blank", "jpop","menubar=0,width=200,height=100,top=100,lef=100");
  jwin.document.write(ahtml);
}
function uee(info, tab){
  jpop("Jump to ancestors in " + surl2ahtml(info.pageUrl,""));
} 

開発:これでいかがでしょうか?Go!

基盤:大当たり。

開発:うーんしかし、開くのが遅いね。1秒くらいかかってる?

基盤:まあ、テスト用の突破口だと思えば。それに、これをずっとjQueen用のミニブラウザだと思えば。

開発:そうですね。原理的にはできた。では、残りのぶぶんもくっつけてと。

* * *

開発:生成したウィンドウに送り込んだHTMLにはマウスイベントに対する反応が含まれているのですが、残念ながら、おそらくセキュリティ上の制約で、何かのオプションを付けないと inline event は動かないよ、ということを言っていますね。この方法は無理な感じ。やはり、何か存在するURLを指定して開くのが良さそうな気はします。今日はこれにて撤収。

基盤:まあ、1日目にしてはだいぶ進みましたよね。

--
2020-0615 SatoxITS

2020-0616 追記:この件は、extension のディレクトリの下にあるリソースを可視化する設定("web_accessible_resources")があり、このディレクトリにHTMLやJavaScriptを置いて参照できることがわかったため、解決しました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です