2019年10月26日土曜日

Chrome のプラグインを作る! その3 ローカルのJSONファイルを解析する

「キリヌーク」というカッターナイフを買った。
刃にばねが仕込んであり、刃先の圧力を一定に保ち紙一枚をきれいに切り取れる。
「切る」よりも、「ひっかく」という感覚だ。
これは使いやすい。
アイデアは生活を豊かにする。

***

さて、プラグイン開発の続きを書こうと思う。
このブログのフィードを投稿日時順・JSON形式のテキストとして取得するには、以下のURLにアクセスするだけでよい[1]。

https://mamorunoblog.blogspot.com/feeds/posts/default?alt=json&orderby=published
拡張機能を実装するJavaScriptからこのフィードを取得し解析することで、最後にブログを更新した日時を取得できる。
いきなりURLにアクセスするのは難しそうなので、まずはローカルのJSONファイルを読み込む練習をしたいと思う。

ポイント

  • JavaScriptからローカルのファイルにアクセスするためには、ブラウザの起動オプションにより許可を出す必要がある
  • サーバと対話しデータを受け取るにはXMLHttpRequestクラスを用い、JSONのパースにはJSONクラスを利用する
  • アイコンの読み込みはマニフェストで指定できる

開発・実行環境

ブラウザ Google Chrome
バージョン: 77.0.3865.120(Official Build) (64 ビット)
起動時オプション:
--allow-file-access-from-files --renderer-process-limit=10 --flag-switches-begin --flag-switches-end

PC
プロセッサ: Intel(R) Core(TM) i5-5200U CPU @2.20GHz
メモリ: 8.00 GB
OS: Windows 8.1 (64bit)

注意すべきは、デフォルトではJavaScriptからローカルのファイルを読み込めないこと。
確かに、JavaScriptからローカルファイルを無制限に読み取れてしまっては、悪意のあるサイトがPCの大切なデータを物色できてしまい、大変に困る。
しかし、簡単にJavaScriptの動作確認をしたい場合に、この制限は都合が悪い。
--allow-file-access-from-files フラグを立てることでローカルのファイルを読み込むことができる[2]。
フラグが有効になっているかどうかは、chrome://version から確認できる。

フォルダ構成

├── data1.json
├── icon
│   └── icon.png
├── load.html
├── load.js
└── manifest.json
前回まではアイコンをHTMLファイルとJavaScriptファイルと同じレベルに置いていたが、階層を分けて整理した。

コード

load.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charser="utf-8" />
  5. <title>Odonata Bug Hunt Blog</title>
  6. <link rel="manifest" href="/manifest.json">
  7. <script type="text/javascript" src="load.js"></script>
  8. </head>
  9. <body>
  10. <input type="button" value="Click me!" onclick="load();">
  11. <div>Odonata Information</div>
  12. Scientific Name: <span id="id_scientific_name"></span><br />
  13. Japanese Name: <span id="id_ja_name"></span><br />
  14. Size: <span id="id_size"></span><br />
  15. Habitat: <span id="id_habitat"></span><br />
  16. </body>
  17. </html>
参考サイト様のソースを参考に実装した[3]。
ボタンを押すとJavaScriptのイベントハンドラが呼び出され、spanタグの部分がJSONから読み取られたデータに置き換えられる。
具体的には、<span id="id_scientific_name"></span>、<span id="id_ja_name"></span>、<span id="id_size"></span>、<span id="id_habitat">の部分がJSONに記されたデータに置き換えられる。

data1.json

  1. {
  2. "scientific_name":"Sympetrum infuscatum",
  3. "ja_name":"Noshime Tombo",
  4. "size":28,
  5. "habitat":"From Hokkaido to Kyuushuu"
  6. }
読み取られるJSONファイルである。

load.js

  1. var load = function loadFunc() {
  2. var xhr = createXMLHttpRequest();
  3. var url = "./data1.json"
  4. xhr.onreadystatechange = function () {
  5. switch (xhr.readyState) {
  6. case 4: // DONE
  7. // if (xhr.status != 200) {
  8. // return;
  9. // }
  10. console.log("xhr.readyState: " + xhr.readyState);
  11. console.log("xhr.status: " + xhr.status);
  12. console.log("xhr.responseText:\n" + xhr.responseText);
  13.  
  14. // Parsing JSON.
  15. var data = JSON.parse(xhr.responseText);
  16. var elem = document.getElementById("id_scientific_name");
  17. elem.innerText = data.scientific_name;
  18. var elem = document.getElementById("id_ja_name");
  19. elem.innerText = data.ja_name;
  20. var elem = document.getElementById("id_size");
  21. elem.innerText = data.size;
  22. var elem = document.getElementById("id_habitat");
  23. elem.innerText = data.habitat;
  24. break;
  25. default:
  26. // none.
  27. break;
  28. }
  29. }
  30. xhr.open("GET", url);
  31. xhr.send();
  32. }
  33.  
  34. function createXMLHttpRequest() {
  35. if (window.XMLHttpRequest) {
  36. return new XMLHttpRequest();
  37. }
  38. if (window.ActiveXObject) {
  39. try {
  40. return new ActiveXObject("Msxml2.XMLHTTP.6.0");
  41. } catch (e) {
  42. // none.
  43. }
  44. try {
  45. return new ActiveXObject("Msxml2.XMLHTTP.3.0");
  46. } catch (e) {
  47. // none.
  48. }
  49. try {
  50. return new ActiveXObject("Microsoft.XMLHTTP");
  51. } catch (e) {
  52. // none.
  53. }
  54. }
  55. return false;
  56. }
サーバと対話し、データを受け取るにはXMLHttpRequestオブジェクトを用いる[4]。
ウェブページの一部のみを更新することができ、ユーザの作業に支障をきたさない。
その名によらず、HTTP以外にもFILEやFTPといった通信方式、XML以外にもあらゆる種類のファイル形式に対応している(なぜこの名称にしたのだろうか?)。

InternetExplorerの古いバージョンではXMLHttpRequestのサポートがないため分岐処理が必要とのこと[3]。
今のところ、Chrome拡張機能の開発を見据えているため、IEをサポートする気はない。
したがって、以下のように書いてしまってもよい。
  1. var xhr = new XMLHttpRequest();
ただ、使いまわしのできるコードとするため、例に従っておく。

manifest.json

  1. {
  2. "icons": [
  3. {
  4. "src": "icon/icon.png",
  5. "sizes": "16x16","32x32","48x48","62x62"
  6. }
  7. }
アイコンはマニフェストに含めることができる。
headタグ内に直接指定するよりも、こちらのやり方のほうがリソースの記述を分離できて、私は好きだ。

実行結果

ブラウザに表示されたテキストを以下に示す。
Odonata Information
Scientific Name: Sympetrum infuscatum
Japanese Name: Noshime Tombo
Size: 28
Habitat: From Hokkaido to Kyuushuu

コンソール出力を以下に示す。
xhr.readyState: 4
load.js:18 xhr.status: 0
load.js:19 xhr.responseText:
{
  "scientific_name":"Sympetrum infuscatum",
  "ja_name":"Noshime Tombo",
  "size":28,
  "habitat":"From Hokkaido to Kyuushuu"
}

ちゃんとJSONから読み取ったデータがHTMLのspanタグ内に入った。

問題点

最初はJavaScriptで以下のエラーガードをコメントアウトせずに有効にしていた。
  1. if (xhr.status != 200) {
  2. return;
  3. }
読み込みに成功するとxhr.statusは200になるらしく[5]、それ以外は不適としてはじいてしまおうという考えのもとである。
しかし、読み込みができているにも関わらず、なぜか0となった。
おかげで10分近く頭を抱えてしまった。

対処療法的に、このエラーガードを外した。
どこか間違っているんじゃないかと思うと恐いが、まぁ動いたので、いったんは良しとする。
原因が分かったら加筆修正するかも。

感想

ローカルファイルを読み込むための方法を調べるのは、意外とめんどくさかった。
セキュリティの観点から見れば、簡単にJavaScriptからローカルファイルにアクセスできてしまうのは、確かにまずい。
今回はローカルファイルを用いてJSONファイル読み込みのテストを行ったが、本格的なプロの開発ではどのような環境で開発が行われているのか、興味が湧いた。

付録

特になし。

参考文献

  1. Blogger の フィード URL 構成 と パラメタ― まとめ
  2. Windows版chromeでローカルファイルに直接アクセスする方法[オレ得JavaScriptメモ]
  3. JavaScriptでのJSONのパーシング - JSONの読み込みと値の取得 (JavaScript プログラミング)
  4. XMLHttpRequest
  5. HTTP レスポンスステータスコード

0 件のコメント:

コメントを投稿

コメント表示は承認制に設定しています