Help us understand the problem. What is going on with this article?

ChromeAppsでの外部リソースの扱い方

More than 5 years have passed since last update.

ChromeAppsではwindow.alertなどは使えないと書いた。
他にもということで、利用不可能な物に関するネタである。

普通のウェブサイトやウェブアプリケーションで、たまに外部サイトの画像をそのまま貼り付けたい場合があると思う。そういう場合にはこうするだろう。

<img src="http://exsample.com/a00.png" alt="hoge">

上記の利用シーンの場合、これはリンク切れでもしてない限り問題なく表示される。
しかし、ChromeApps(およびExtension?)の場合、上記やり方は通用しないのだ。

Refused to load the image 'http://exsample.com/791079161fc21e71_normal.png' because it violates the following Content Security Policy directive: "img-src 'self' data: chrome-extension-resource:".

とコンソールにエラーが出て怒られてしまう。
つまり、ChromeApps実行環境向けに決められた Content Security Policy に反するので使わせないよ!ということなのだ。
こうなってしまうと、例えばTwitterのタイムラインを表示する、いわゆるTwitterクライアントを作ろうとした場合に問題が起きると思われる。

通常はAPIを叩いて戻ってきたツイートの情報・ユーザーアカウントの情報のJSONを解析し、HTMLに当てはめて
サクッと出力するぜ! Stream APIを使った場合でも逐一HTMLに当てはめて表示してバンバン見せるよ!
とやりたいところであろう。

が! ChromeAppsの場合だと、上記の問題によって、少なくとも画像はimgタグでスマートに使えなくなってしまう。ではどうするのか。
一応、公式のドキュメントに解決方法が書かれている。それを試したので紹介したい。

1 XMLHttpRequestで取得する

公式ドキュメントではこのようにするとよい、と書かれている。そのまま引用する。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://supersweetdomainbutnotcspfriendly.com/image.png', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
  var img = document.createElement('img');
  img.src = window.URL.createObjectURL(this.response);
  document.body.appendChild(img);
};

xhr.send();

ちなみにここを楽しようと思って、jQueryの$.ajaxなどを使って取得しても、あっちは確かdatatypeには blob を指定しても意味なかったはず。
試しにblobを指定して取得、imgにセットしてみたがまあ、当然読み込まれなかった。
$.ajaxの場合、用いるのはコールバック関数で指定したdataか、jqXHR.responseTextが大抵だろう。
win8_devcap003.PNG
画像の生データなので、Canvasに書き込もうと思ったが、これも上手くいかなかった。
単にCanvasの適切な使い方を知らないだけかもしれないので、それは後々また試したい。

というわけでChromeAppsで外部サイトの画像を取得して表示するなら、素直に生のXMLHttpRequestを使うことになる。
上記onload内で参照しているthis.reponseは中身はこうなっている。(赤線部分)
win8_devcap004.PNG
きちんとblobで取れているのがわかる。あとはこのblobデータをオブジェクトのURLに変換する。

img.src = window.URL.createObjectURL(this.response);

この部分だ。これについてはここあたりが詳しい。

こうして生成されたURLをimg.srcに指定した結果、無事に表示される。
win8_devcap005.PNG
(ちなみにこれは私のTwitterのアイコンです)

懸念される事象

このやり方で無事に外部サイトの画像を表示できたが、タイムラインなどのような画像が比較的たくさん出てくるデータ、それもStream APIでバシバシ流れてくるようなデータにこのやり方で挑んだ場合、いちいち一つの画像ごとにXMLHttpRequestで読み込んで・・・などとしていたら、諸々のロスが発生して動作が遅くなったりしないか。それが心配である。

2 webviewを使う

これは 取得する ではなくて、もはやimgそのものと言っても過言ではないかもしれない。
webviewは、ChromeAppsで使える独自のタグだ。iframeの代わりに外部サイトを埋め込んだりするのにバリバリ活躍する。こうしてみた。
(webviewを使う際はmanifest.jsonのpermissionに webview を追加するのを忘れずに。)

<webview src="https://supersweetdomainbutnotcspfriendly.com/image.png" style="width:64px;height:64px;"></webview>

すると、何の苦労もなく表示できる。
win8_devcap006.PNG
上のa=がwebviewで表示した画像である。一見すると違いはなさそうだが、右クリックすると違いがわかる。
webviewを使った場合は普通の画像のときのようなコンテキストメニューが、XMLHttpRequestで取得した方は
ChromeApps開発時のコンテキストメニューしか出ない。

3 終わりに

コードの量を考えると、webviewを使ったほうが楽でスマートに思える。一方でセキュリティポリシーとやらに適切に従う公式で紹介されているやり方である、XMLHttpRequestを使った方のほうがなんとなく気持ち的に安心する。
使い方に関してはwebviewのほうがimgタグを使った場合とほぼおなじ感覚で使えるので、どうしてもこっちで楽をしたい。

両方のやり方の真価が分かるのは、きっとStream APIを使うような大量に流れてくるデータをさばくようなシーンだろう。
負荷などをその時に確かめてみたい。

lumis
昔COBOL、途中HSP、VB、C++、 今はHTML/Javascript、GoogleAppsScript、Pythonなど。 環境周りはUbuntu, Win7~8.1~10、macOS。 ドハマリしてるのはGoogleApps Script(GAS)です。GAS大好き!
http://lumitaglibro.blogspot.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away