レベル: 中級 James R. Fuller (jim.fuller@webcomposite.com), Technical Director, FlameDigital Limited & Webcomposite s.r.o.
2009年 6月 02日 Web が登場してから 20 年が過ぎましたが、Web は進化し続けています。インターネットはハイパーテキスト文書システムを本格的なオペレーティング・システムと似たものに変えつつあります。この記事では、新興のクラウド・ベースのオペレーティング・システムに欠けている重要な機能、つまり標準ベースの Web クリップボードに焦点を当てます。AtomPub、そして AtomClip という Firefox の XUL 拡張機能を使用して、Web クリップボードがどのようなものかを学びましょう。
今日の新しいオペレーティング・システムは、よく知られたいくつかの要素で構成されていますが、そうした要素の 1 つが GUI です。GUI によって、ウィンドウ操作、ドラッグ・アンド・ドロップ機能、メニュー、そしてマウス操作を実現することができます。こうした GUI 機能の大部分は 1980年代に開発されたものですが、それ以来、画期的な新機能はほとんど追加されていません。
 |
頻繁に使用する頭字語
- CTO: Chief Technology Officer
- DOM: Document Object Model
- GUI: Graphical user interface
- HTML: Hypertext Markup Language
- HTTP: Hypertext Transfer Protocol
- UI: User interface
- XHTML: Extensible Hypertext Markup Language
- XML: Extensible Markup Language
- XUL: XML User Interface Language
|
|
今や GUI は変貌を遂げつつあります。確かに、(スマートフォンなどの機器の登場を一因として) コンピューターとのやり取りに新しい方法が使われるようになり、それによって非常に画期的なさまざまな可能性が生まれています。しかしおそらく最も大きい広範にわたる変更は、今日のオペレーティング・システムの根幹部分にあります。つまりオペレーティング・システムが提供して当然と思われるサービスの多くは、今や Web サービスによって提供されています。実際、アプリケーションやオペレーティング・システムなどが Web サービスを直接使うようになっていますが、こうした使い方は Web ベースのオペレーティング・システムの始まりと言えるかもしれません。
欠けている機能: Web クリップボード
Web オペレーティング・システム全体としてのツールに欠けていると思える機能の 1 つが、Web ベースのクリップボードの概念です。そうしたツールのために、これまでに努力がなされたこともあります。
例えば 2006年3月、Microsoft® の CTO の Ray Ozzie は Live Clipboard のコンセプトのデモンストレーションを行い、大きな話題となりました (この詳細についてのリンクは「参考文献」を参照してください)。この構想は、Microsoft のすべてのソフトウェアを Live Clipboard に接続できるようにし、Web アプリケーション間でオブジェクトやデータ型をカット・アンド・ペーストできるようにするというものです。Live Clipboard は Web クリップボードの 1 つの姿を示すものでしたが、この構想は消えてしまったようです。
現在では、それぞれ本質的に異なるものの、いくつかの商用オンライン・サービスで Web クリップボードがエミュレートされています。その中で比較的よく使われるサービスとして、Clipmarks.com、Friendspaste.com、Pastebin などがあります。しかし標準化された手法は何もないため、システムは各社によって異なり、ベンダー・ロックインというシナリオに結びつきがちです。広く採用されやすいものを作り出すためには、既存のツールや技術を駆使して作業を行えるようにする必要があります。この記事ではそうした実験の 1 つとして、私が AtomClip と呼ぶシステムについて説明します。
AtomClip をインストールする
この Web クリップボード・アプリケーションの作成方法を説明する前に、まずソースファイルをダウンロードすることをお勧めします (「ダウンロード」を参照)。.zip ファイルを解凍し、その中に含まれている README ファイルの指示に従ってください。(Apache Ant をお持ちでない場合には、Mozilla Firefox 用にビルド済みの AtomClip.xpi 拡張機能を使うことができます)。
すべてをインストールできると、任意の Web ページ上の要素を Firefox ブラウザーで右クリックすれば新しい選択肢が表示されるはずです。表示されるコンテキスト・メニューには、Image to AtomClip、Copy text to AtomClip、Copy link to AtomClip という 3 つの選択肢が含まれています。これらの各選択肢によって、リソースは eXist という XML データベースにコピーされます。4 番目の選択肢 (Copy from AtomClip) はそれまでにクリップされたアイテムを列挙します。列挙されたアイテムの 1 つを選択すると、そのアイテムをローカルのクリップボードから貼り付けられるようになります。
AtomClip
AtomClip に対する私の目標は控え目なものです。つまりオープンな標準と既存の技術を使用して単純なコンテンツをクリップするための Web クリップボードを実装し、より高度なデータ型やオブジェクトに対応するための基礎を提供することです。ここではクライアント・アプリケーションとして Firefox の拡張機能を使用し、また Web ストレージとして使用する eXist という XML データベース (「参考文献」) からの Atom XML フィードを使います。図 1 は AtomClip Web クリップボード・アプリケーションの全体を示しています。
図 1. AtomClip のサーバーおよびクライアント・コンポーネントのアーキテクチャー
これは多くのブラウザーがクリップ動作を行うマルチユーザーのシナリオです。このシナリオでは、ユーザーは Web クリップボード間でカット・アンド・ペーストすることも可能です。コピー動作は拡張機能をとおして Firefox の中で行われるのみであり、データは Atom XML フィードに保存されます。従って以前にクリップされたアイテムをペーストできるようにするためには、単にその Atom フィードを取得して、そのアイテムをローカルのクリップボードに置けばよいだけです。
ここでは Firefox の中で作成される XUL 拡張機能 (「参考文献」を参照) として AtomClip クライアントを作成しました。この XUL 拡張機能がユーザー用のすべての機能を実装します。サーバー・コンポーネントは eXist という XML データベースの形で提供されます。eXist を選んだ理由は、eXist には AtomPub (Atom 出版プロトコル) の実装がバンドルされているためです。Firefox ブラウザーでコピー操作を行うと、AtomClip は Atom のエントリーを作成して eXist データベースに送信します。AtomClip は基本的な HTTP メッセージを使って標準的な URL を作成し、AtomPub の定義に従って Atom フィードにエントリーを追加します。
Atom の entry 要素のペイロードに含まれるコンテンツ・タイプは、画像、リンク、テキストの 3 つのうちの 1 つです。各コンテンツ・タイプによって、そのタイプに対応する HTML 要素が <content/> 要素の中に挿入されます。
image は画像タグを含みます。例えば <img src="" alt="" height="" width=""/> など。
link はアンカー・リンク・タグを含みます。例えば <a href=""></a> など。
text はXHTML のテキストをそのまま含みます。例えば <div/> など。
また Atom の基本的な各要素を再利用し、いくつかの基本的なメタデータを提供しています。
<title/> は image、text、link のいずれかを含み、そのタイプのクリップを識別するために使われます。
<link/> はクリップされたデータが含まれていたソース Web ページの URL を含みます。
<content/> はクリップされたデータを含みます。
Atom では外部の名前空間要素を挿入することができます。そこで、以下のようにクリップ動作を表すための専用の XML フォーマットを定義し、これを <content/> 要素の中に埋め込むことにします。
<clip type="image|text|link" srcURL="http://www.example.com">
<content>
<img src="" alt="" height="" width=""/>
</content>
</clip>
|
サーバー: eXist データベースを使用した Atom ストア
サーバー・サイドのコンポーネントは AtomPub を使用して通信を行える必要があり、さらに Atom フィードを生成できる必要もあります。また、フィードの選択や集約を行えるように XQuery 機能を備えた XML データベースがあると便利です。ここでは AtomPub をサポートする eXist を使います。
AtomPub は Web リソースの作成や更新のための HTTP ベースのプロトコルです。AtomPub を使うためには、適切な URL に対して適切な HTTP 操作を呼び出す必要があります。ここでは、それぞれ独自の URL を持つ次の 3 つの AtomPub サービスを使用します。
- http://localhost:8080/exist/atom/introspect/clipboard: この URL に対して HTTP の
GET を実行すると、Atom サービス文書が返されます。このサービス文書には、すべての Atom フィードに対する URL によるリンクが含まれています。
- http://localhost:8080/exist/atom/content/clipboard: この URL に対して HTTP の
GET を実行すると、Atom フィード文書が返されます。このフィード文書には、Atom の集合に現在保存されている各クリップのエントリーが含まれています。
- http://localhost:8080/exist/atom/edit/clipboard: 対象とするクリップのメタデータとコンテンツを含む XML 文書を作成し、この URL に対して HTTP の
POST を実行すると、レスポンス・コードとして HTTP のステータス・コード 201 (Created) が返され、レスポンス文書として新しい Atom エントリー (そのクリップ) を表す文書が返されます。
Atom XML フィードは eXist の中で、ある集合に追加されます (ここでは clipboard という最上位レベルの集合を使っています)。eXist から Atom XML フィードを取得するためには、適切な URL に対して HTTP の GET 操作を行えばよいだけです。この Atom フィードには、それまでに行われた各クリップ動作へのエントリーが含まれています。eXist の動作として必要なことは、たったこれだけです。
クライアント: Firefox の拡張機能としての AtomClip
このソリューションのクライアント・サイドは Firefox の XUL 拡張機能です。この拡張機能によって、Atom フィードとの間でのコピー操作のための AtomClip 用のコンテキスト・メニューの選択肢を追加します。Web ページ上のアイテムを選択して Atom フィードにコピーできるようにしたいので、(Web ページを右クリックすると表示される) コンテキスト・メニューを補強する必要があります。
Firefox 用に初めて何かを開発しようとする場合、どこから手を付ければよいかわからないかもしれません。そこで、Firefox の拡張機能をスムーズに作成するためのヒントを以下に挙げます。
- Firefox では複数のプロファイルを使うことができます。そのため、好みのブラウザー設定を保持することができる他、開発に適したプロファイルを作成することができます。新しいプロファイルを作成するためには、コマンドラインから以下のように Firefox を呼び出します。
./firefox -ProfileManager
|
そして dev というプロファイルを作成します。この状態で Firefox を実行すると、どちらのプロファイルを使用するのかを尋ねられます。これを避ける容易な方法は、このプロファイルを使用して Firefox を起動するスクリプトを作成することです。
./firefox-bin -no-remote -P dev &
|
- Firefox の Extension Developer Extension を使う。Firefox 用の開発で非常に面倒な点は、コードを編集するたびにブラウザーを再起動する必要があることです。この問題に対応するために、Extension Developer Extension という適切な名前の拡張機能が開発されています (この拡張機能のダウンロードに関しては「参考文献」を参照してください)。
- 拡張機能のソース・ディレクトリーにリンクする。毎回 .xpi ファイルを再インストールしなくてもすむように、開発用のソース・ディレクトリーを開発用プロファイルの拡張機能のディレクトリーにリンクします。そのためには、(install.rdf で定義される) その拡張機能の ID の名前を付けたファイルを Firefox の applications/extension ディレクトリーの下に作成します。
/Applications/Firefox/Content/MacOS/extensions/atomclip@webcomposite.com
|
このファイルは、このソース・ディレクトリーが存在する場所への完全なディレクトリー・パスを含む必要があります (スラッシュはそのままにし、空白はすべて削除する必要があります)。すると Firefox は開発中の拡張機能の最新バージョンをロードします。
これで Firefox の拡張機能を開発するための基本を理解できたので、AtomClip 拡張機能の構成要素を調べてみましょう。リスト 1 は、どんなクロム (つまり UI 要素) を表示するのかを Firefox に指示する XUL を示しています。
リスト 1. コンテキスト・メニューに関する XUL によるクロム
<popup id="contentAreaContextMenu">
<menuitem id="clipboard-text"
label="copy text to atomclip" accesskey="H"
insertafter="context-sep-stop"
class="menuitem-iconic"
image="chrome://atomclip/content/atom.gif"
oncommand="CopyToClipboard(document.popupNode);
postAtomRequest(atomurl,TextAtomEntry(document.popupNode),
user,password)"/>
<menuitem id="clipboard-image"
label="copy image to atomclip" accesskey="H"
insertafter="context-sep-stop"
class="menuitem-iconic"
image="chrome://atomclip/content/atom.gif"
oncommand="CopyToClipboard(document.popupNode);
postAtomRequest(atomurl,ImageAtomEntry(document.popupNode),
user,password)"/>
<menuitem id="clipboard-link"
label="copy link to atomclip" accesskey="H"
insertafter="context-sep-stop"
class="menuitem-iconic"
image="chrome://atomclip/content/atom.gif"
oncommand="CopyToClipboard(document.popupNode);
postAtomRequest(atomurl,LinkAtomEntry(document.popupNode),
user,password)"/>
<menu id="atomclip-context-menu"
class="menu-iconic"
image="chrome://atomclip/content/atom.gif"
label="copy from atomclip">
<menupopup id="atomclip-context-paste"
onpopupshowing="addSubMenu();">
<menuitem label="empty" />
</menupopup>
</menu>
</popup>
|
この 3 つの <menuitem/> 要素は AtomClip へのコピー操作を定義し、oncommand 属性によってそれらの操作の JavaScript 動作にリンクします。最後の <menu/> 要素は AtomClip からのコピー操作のための要素であり、これまでにクリップされたものを一覧表示する、ネストされたメニューです。
最も基本的な形での AtomClip 拡張機能は単なる AtomPub クライアントです。そのため、AtomClip 拡張機能は AtomPub フォーマットのリクエストを AtomPub サーバーに送信できる必要があります。XUL は JavaScript コードを使います。リスト 2 は Atom フィードの取得と新しい Atom エントリーの送信を行う関数がどのように実装されているかを示しています。
リスト 2. Atom XML フィードを取得する
function getAtomRequest(url,user,passwd) {
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
if (httpRequest.overrideMimeType) {
httpRequest.overrideMimeType('text/xml');
}
}
else if (window.ActiveXObject) { // IE
try {
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
if (!httpRequest) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
httpRequest.onreadystatechange = function() { alertGetContents(httpRequest); };
httpRequest.open('GET', url, true);
httpRequest.setRequestHeader('Authorization', 'Basic '+btoa(user+':'+passwd));
httpRequest.send(null);
}//end getAtomRequest
|
一部の Mozilla ブラウザーのバージョンによっては、サーバーからのレスポンスに MIME タイプを含む XML ヘッダーが含まれていないと適切に動作しません。その場合には、サーバーに送信されるヘッダーを httpRequest.overrideMimeType('text/xml'); という命令で変更し、MIME タイプを強制的に text/xml にします。
レスポンスを処理するためにコールバック関数 alertGetContents(httpRequest) を使用していることに注目してください。これについては後ほど説明しますが、この関数は Copy from AtomClip というメニュー項目の動作を処理します。もうひとつの作成した関数 postAtomRequest は HTTP の POST を使って新しい Atom エントリーを作成し、クリップを表す Atom XML 文書を提供します (リスト 3)。
リスト 3. 新しいエントリーを作成する
function postAtomRequest(url,payload,user,passwd) {
var httpRequest;
if (window.XMLHttpRequest) { // Mozilla, Safari, ...
httpRequest = new XMLHttpRequest();
if (httpRequest.overrideMimeType) {
httpRequest.overrideMimeType('text/atom');
// See note below about this line
}
}
else if (window.ActiveXObject) { // IE
try {
httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
if (!httpRequest) {
alert('Giving up :( Cannot create an XMLHTTP instance');
return false;
}
httpRequest.onreadystatechange = function() { alertPostContents(httpRequest); };
httpRequest.open('POST', url, true);
httpRequest.setRequestHeader('Authorization', 'Basic '+btoa(user+':'+passwd));
httpRequest.setRequestHeader('Content-Type', 'application/atom+xml')
httpRequest.send(payload);
}//end postAtomRequest
|
eXist エンジンでは Content-Type が application/atom+xml に設定されている必要があります。そうでないと eXist エンジンは XML 文書に対して何もしません。この 2 つの JavaScript 関数でもう 1 つ注意すべき点として、Basic 認証を使うためには HTTP の Authorization ヘッダーを作成する必要があります。
リスト 4 は HTTP の GET を使ったバージョンの関数用のコールバック関数を示しています。この関数は、それまでにクリップされたものをすべて、ネストされたメニューとして Firefox のコンテキスト・メニュー上に表示します。
リスト 4. AtomClip からコピーする
function alertGetContents(httpRequest) {
if (httpRequest.readyState == 4) {
if (httpRequest.status == 201 || httpRequest.status == 200) {
//alert(httpRequest.responseText);
var clipboardsmenupopup = document.getElementById('atomclip-context-paste');
//remove all menuitems
while(clipboardsmenupopup.childNodes.length > 0)
{
clipboardsmenupopup.removeChild(clipboardsmenupopup.childNodes[0]);
}
var entries = httpRequest.responseXML.getElementsByTagName('entry');
for (var i = 0; i < entries.length ; i++)
{
var item = document.createElement('menuitem');
var children = entries[i].childNodes;
var title = children[4].textContent;
var sourcepage = children[5].textContent;
var data = children[6].firstChild;
if(title=='link')
{
item.setAttribute('oncommand',
"copyFromAtomClip('"+data.firstChild.href+"');");
item.setAttribute('label',
title+":"+data.firstChild.href);
}else if(title =='image')
{
item.setAttribute('oncommand',
"copyFromAtomClip('"+data.firstChild.src+"');");
item.setAttribute('label',
title+":"+data.firstChild.src);
item.setAttribute('class','menuitem-iconic');
item.setAttribute('image',
data.firstChild.src);
}else
{
item.setAttribute('oncommand',
"copyFromAtomClip('"+escape(data.firstChild.textContent)+"');");
item.setAttribute('label',
title+":"+data.firstChild.textContent);
}
clipboardsmenupopup.appendChild(item);
}
} else {
alert('There was a problem with the request.');
alert('status:'+httpRequest.status);
}
}
}//end alertGetContents
|
この関数はまず、それまでに作成されたすべてのメニュー項目を削除し、データセットが必ず最新になるようにします。次に DOM の機能を利用して Atom XML 文書に含まれるコンテンツを列挙していき、コンテンツ (画像、リンク、テキストのいずれか) に応じて特定の選択肢を持ったメニューを生成します。例えば画像の場合には、コンテキスト・メニュー自体の中に表示する小さな画像を生成し、そのメニュー項目が何であるかのヒントを与えます。
また各コンテンツ・タイプには独自の関数があり、その関数によって Atom エントリーを作成します。リスト 5 はコンテンツがテキストの場合を示しています。
リスト 5. Atom エントリーを作成する
function TextAtomEntry(element){
var value = document.commandDispatcher.focusedWindow.getSelection().toString();
var xmlstring = '<entry xmlns="http://www.w3.org/2005/Atom">'
xmlstring += '<title>text</title>'
xmlstring += '<link>'+document.commandDispatcher.focusedWindow.location.href+'</link>'
xmlstring += '<content type="xhtml">'
xmlstring += '<div xmlns="http://www.w3.org/1999/xhtml">'
xmlstring += value
xmlstring += '</div>'
xmlstring += '</content>'
xmlstring += '</entry>';
return xmlstring;
}//end TextAtomEntry
|
title 要素はコンテンツ・タイプ (画像、リンク、テキスト) を定義し、link 要素はクリップされたアイテムが含まれていたページを含んでおり、そのアイテムを document.commandDispatcher.focusedWindow.location.href によって特定します。ここで指定されている値はペイロードの内容そのものです。
リスト 6 に示す 2 つの関数は、システムのクリップボードへコピーする操作と、システムのクリップボードからコピーする操作を実行します。つまり CopyTo 操作を実行する場合にはシステムのクリップボードからコンテンツを取得します。Atom フィードからコピーする場合には、フィードから収集したデータを取得し、貼り付けに使えるようにシステムのクリップボード上に置きます。
リスト 6. ユーティリティー関数
function copyFromAtomClip(data){}
function CopyToClipboard(element){}
|
注: AtomClip 拡張機能を作成するために、この記事に含まれている Ant ビルド・ファイルを使用することができます。
AtomClip 拡張機能の欠点
この拡張機能には、いくつかの制約があります。この制約の大部分は、Firefox の拡張機能の実装について私の知識が限られていることによるものです。
- 複雑な JavaScript コードがある場合:大量の JavaScript コードがある場合、思ったとおりにクリップできない可能性があります。例えば JavaScript を多用するアプリケーション (Google の Gmail など) の中でリンクをコピーしようとする場合、この拡張機能はそのリンクを判断するためのロジックを持っていません。こうした場合には、適切なコンテンツを取得しようとしても一貫した動作を実現できない可能性があります。
- 画像の場合: AtomClip はリンクを持つ画像に対して適切なコンテンツを選択するロジックを持っていません。また画像自体は保存されず、
<src> 属性を含む HTML の <img> タグしか保存されません。
- 基本的なコンテキストの問題: コンテンツを右クリックした場合、該当する選択肢のみが表示されるようにした方が適切です。例えば、ある画像をクリックした場合に AtomClip は Copy image to AtomClip という選択肢のみを表示するようにした方が適切です。
AtomClip の実際
AtomClip を使うためには、eXist を起動し、README にあるインストールの説明どおりに eXist が構成されていることを確認します。Firefox に AtomClip.xpi をインストールし、任意の Web ページまでナビゲートします。
次に、何らかのテキストを選択して右クリックします。すると、新しい AtomClip アクションが追加されたコンテキスト・メニューが表示されるはずです。Copy text to AtomClip を選択し、そのテキストを Atom フィードに送信します。このクリップしたテキストを貼り付けるためには、(任意の要素上で) 再度右クリックし、Copy from AtomClip を選択します。すると、それまでに Atom XML フィードにクリップされたものがすべて表示されるはずです。テキスト・エントリーを選択すると、AtomClip にクリップされたテキストが貼り付けられます。
まとめ
この記事ではオープンな標準と技術を使用した Web クリップボードをデプロイしました。この Web クリップボードは広く採用されるのに適した特徴を備えています。Web クリップボードで高度な機能を提供するためには、まず単純なシナリオに対応する必要があります。XUL、Atom XML フィード、そして AtomPub を組み合わせることにより、現在 Web でよく使用されている技術をベースにした一連の強力な技術を手にしたことになります。
AtomClip の作成作業は楽しいものでした。この記事がきっかけで、皆さん自身で実験をしてみる気になったようであれば幸いです。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| AtomClip source code | atomclip.zip | 12KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
- AtomServer の資料を読み、また AtomServer をダウンロードしてください。
- eXist データベースをダウンロードしてください。eXist はオープンソースのデータベース管理システムであり、XML のデータ・モデルに従って XML データを保管し、また効率的で索引ベースの XQuery 処理を行うことができます。
- IBM 製品の試用版をダウンロードするか、あるいはオンラインで IBM SOA Sandbox を試し、DB2®、Lotus®、Rational®、Tivoli®、WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。
議論するために
著者について
記事の評価
|