© Bob Duran – “Red Door” – Creative Commons: Attribution

SVGには<use>という要素が存在する。これはプレースホルダのように働き、SVG文書の指定されたノードのコピーを保持するという優れもの。つまり、use要素を使うと、あるSVGの図形をコピペすることなく使いまわせるようになる。

<use> 要素は SVG ドキュメントの中からノード取り出して、別の場所に複製します。効果は、あたかもノードが見えない DOM に完全にクローンされ、HTML5 の template要素 によく似ている、use 要素がある場所に貼り付けられたかのように同じになります。

これはMDNでの紹介。コピー元のSVGはDOM上にある物以外にも、外部URIから読み込むこともできる(IEはサポート外)。で、この外部URIから読み込む時のブラウザの振る舞いが少し謎だと気付いた。

クロスオリジンは無理な模様

<svg>
	<use xlink:href="foo.svg"/>
	<use xlink:href="bar.svg#id"/>
	<use xlink:href="http://localhost:8000/baz.svg#id"/>
	<use xlink:href="https://example.com/foobar.svg#id"/>
</svg>

こんなSVGをhttp://localhost:8000/が含んでいる時、実際にリクエストされるSVGはどれだと思うだろうか。最初、私は全部読み込めるだろうと思っていたのだが、違った。

実際にFirefoxのネットワークタブで調べてみると、bar.svgとbaz.svgのみがリクエストされていることが分かる。つまり、この事から、参照するURIはハッシュで対象のノードを明示する必要があり、しかも複製先文書の同一生成元(オリジン)にマッチしなればならないようだ。これらに反する場合、そもそもリクエストされない

CORS?

クロスオリジンなリクエストがされないということは、Cross-Origin Resource Sharingcrossorigin属性が関係ないということになる。つまり、どう足掻いても<use>は同一生成元のみに制限されるわけだ。これは画像など静的なコンテンツを専用のオリジンから配信している場合(例えばcdnやamzaon s3などのオンラインストレージ)に問題になりそうだ。また、www.example.comとblog.example.comなど、サブドメインごとにサイトが分かれている場合には、それぞれにSVGのコピーが必要になる。

意義?

さて、このような制限はセキュリティの理由から存在するのだと思われる。

セキュリティ上の理由でいくつかのブラウザーは、同一生成元ポリシーを use 要素に適用して、xlink:href 属性の異なる生成元の URI を読み込むことを拒否することがあります。

MDNにもこのように注意書きがある。しかし、XHRを利用すればクロスオリジンなSVG文書の中身は読めるわけで、なんというか、時代遅れな感もある。恐らく仕様的なアレで駄目なんだろうけど、素直にAccess-Control-Allow-Originヘッダで判断すればいいじゃんと思う。

解決策?

HTMLからオリジンを超えて<use>でSVGを表示するには、XHRで複製元のSVGをDOMに挿入して、<use>にはidをハッシュで指定すればなんとか表示は出来そうだ。しかし、本末転倒でしかない。外部からのスタイリングなどは無理になるが、use要素を諦めて、img要素のsrc属性にクロスオリジンなSVGのuriをセットして表示してまうのが手っ取り早い。これが解決策と言えるかは怪しいけど。また、SVGファイル内でuse要素を利用するなら、外部uriを諦めてインラインにするしかないかも。

はー。なんというか、SVGはあまりに複雑であまり調べる気がしない。大昔からあるわりにイマイチ普及しないのも、頷けるね。