2008-07-13

パスワード生成Greasemonkeyスクリプト

Firefox用の、パスワードを作成するGreasemonkeyスクリプトを書いてみました。作成されたパスワードは自動でクリップボードにコピーできます。dataスキーム、Flashファイルは使っていません。

PassMaker.user.js

認証画面のパスワード入力欄で、Altキーを押すと、自動作成されたパスワードが記入されます。認証サイトごとに、パスワードの文字数や使う記号を設定できます。外部ライブラリとの通信、パスワードの平文・暗号文・ハッシュ値のPCへの保存は行ないません。

パスワードは、

  • ドメイン名 (www.google.com, login.yahoo.co.jp, www.hatena.ne.jp など)
  • ユーザー欄に入力された文字列
  • パスワード欄に入力された文字列
などを参照して作成されます。 ユーザー名とパスワード欄に事前入力する文字列がたとえ同じでも 別ドメインのウェブサービスでは、異なるパスワードが自動生成されます。 初期設定では、8文字のパスワードが作られます。 文字数を変更するには、パスワード欄で、Escキーを押します。 表示された設定項目のlength欄に希望するパスワードの文字数を入力します。 最大文字数は43文字です。 初期設定では、数字と大文字・小文字のアルファベットからなるパスワードが作られます。 2文字まで、パスワードに使用可能な記号を指定することができます。 その設定は、marks欄に入力します。 salt欄に文字列を入力すると、異なるパスワードを作成することができます。 作成されたパスワードをダイアログで表示させるかどうかをshow項目のラジオボタンで選べます。copy項目のラジオボタンでは、パスワードをクリップボードにコピーするかどうかを設定できます。OKボタンを押すか暗くなった画面をクリックすると、設定情報が保存され画面が元に戻ります。RESETボタンを押すと、そのサイトに関する設定情報がPCから削除されます。

アドレスバーにabout:configと入力し、フィルタを使うことで設定情報を見ることも出来ます。設定名greasemonkey.scriptvals.http://juce6ox.blogspot.com//PassMaker.dataに各サイトでの設定値を保存しています。

Firefoxの初期設定では、javascriptからクリップボードへのコピーは禁止されているようです。コピーを許可するための手順は以下の通りです。

  1. NoScriptアドオンを利用している場合
  2. NoScriptアドオンの詳細設定で、「クリップボードからリッチテキストのコピーとペーストを可能にする」をチェックします。
  3. NoScriptアドオンを利用していない場合(Firefoxの初期設定)
  4. Firefoxのプロファイルフォルダにあるuser.jsを編集します。 無い場合は、新規にuser.jsというファイルをそのフォルダに作成します。 その際に文字コードは、UTF-8を指定します。
user.jsに次のコードを加えます。
user_pref("capability.policy.policynames", "allowclipboard");
user_pref("capability.policy.allowclipboard.sites", "https://www.google.com https://login.yahoo.co.jp https://www.hatena.ne.jp");
user_pref("capability.policy.allowclipboard.Clipboard.copy", "allAccess");
上記の例は、GoogleYahoo!はてなへの認証画面でパスワードコピーを許可しています。 パスワードのクリップボードへの自動コピーを許可するサイトを追加するには、上記2行目のように認証を行うURLを半角スペースで区切って追加してください。

履歴

  • 2008/7/15 : ver. 1.2
    設定値に関わらずパスワードが1文字になる場合の不具合を修正
  • 2008/7/14 : ver. 1.1
    パスワード生成後のフォーカス位置を入力欄に

参考サイト

2007-12-12

TEST : DOM Load Event

DOM ロード関連のイベントの発火順番を調べるテストを書きました。

TEST : DOM Load Event


上記テストでは、いくつかの手法を一つのファイルにまとめています。

IE で DOM ロードを検知するには、script defer を使うのが優れた方法だとおもいますが、document.write を使いたくない場合には、doScroll の動作検知を利用したり、setTimeoutを使わない cached htc を利用したりと多様なやり方があることが分かりました。

IE/DOMLoad : cached htc

document.write を使わず、外部ファイルのダウンロードをしない、IE用の DOM ロードイベントを取得する方法を考えました。

DHTMLの ondocumentready イベントを利用します。
そのためには、DOM要素に behavior 属性で htc ファイルへの参照設定が必要となります。そこで、外部ファイルのダウンロードを不要とするために、ブラウザのキャッシュ機能の利用を考えて、javascriptファイルの拡張子を htc とします。そしてブラウザ判定でIEならば、拡張子が htc である javascript ファイル自身のURLをbehavior属性に渡します。これによりブラウザのキャッシュが使用され、新たな通信が不要となることが期待されます。

HTML :

<script src="http:// ... /onready.htc" type="text/javascript" charset="utf-8"></script>
javascript :
onready(function(){
    alert('how are you?');
});
onready(function(){
    alert('fine! and you?');
});
onready(function(){
    alert('me, too!');
});

onready.htc :
/*
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<public:component xmlns="http://www.w3.org/1999/xhtml" xmlns:public="urn:HTMLComponent">
<public:attach event="ondocumentready" onevent="element.init();" />
<!--

    onready.htc : Cross-Browser DOMContentLoaded Event Listener with Cached HTC script hack for IE

    Copyright (c) 2007 latchet (http://juce6ox.blogspot.com/)
    Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)

*/
var onready = (function(){
    var id,
        ua   = navigator.userAgent.toLowerCase(),
        fs   = [],
        i    = 0,
        done = false,
        init = function(){
                   var f, j = 0;
                   if(done) return;
                   done = true;
                   /*@cc_on
                   document.documentElement.init = null;
                   if(id != undefined) document.documentElement.removeBehavior(id);
                   window.detachEvent('onload', init);
                   document.onreadystatechange = null;
                   /*@if(false)@*/
                   document.removeEventListener('DOMContentLoaded', init, false);
                   window.removeEventListener('load', init, false);
                   /*@end@*/
                   window.onload = null;
                   while(f = fs[j++]) f();
               },
    /*@cc_on
        url  = (function(e, ok){
                   while(e = e.lastChild)
                       if(e.nodeName.toLowerCase() == 'script' && ok.test(e.src))
                           return e.src;
               })(document, /^[^?#]+\.htc$/),
    @*/
        set  = function(){
                   var ok;
                   /*@cc_on
                   document.documentElement.init = init;
                   if(url) id = document.documentElement.addBehavior(url);
                   window.attachEvent('onload', init);
                   document.onreadystatechange = function(){
                       if(document.readyState == 'complete') init();
                   };
                   /*@if(false)@*/
                   if(/mozilla/.test(ua) && !/compatible|webkit/.test(ua) || /opera/.test(ua))
                       document.addEventListener('DOMContentLoaded', init, false);
                   else if(/webkit|khtml/.test(ua) && (ok = /loaded|complete/))
                       (function(){
                           return ok.test(document.readyState) ? init() : setTimeout(arguments.callee, 0);
                       })();
                   window.addEventListener('load', init, false);
                   /*@end@*/
                   var old = window.onload;
                   window.onload = (typeof old == 'function') ? function(){old();init();} : init;
               };
    return function(f){
        if(typeof f != 'function') return;
        if(done) return f();
        if(!i) set();
        fs[i++] = f;
    };
})();
//--></public:component>


参考資料

IE/DOMLoad : behavior expression

IEの独自実装による document.write を使わない DOMロード の検知方法を考えました。

CSSのbehaviorとexpressionを使います。
behaviorは画面描画のたびに頻繁に評価されます。
expressionを用いると、javascriptを記述できます。

上記を利用することで、Diego Perini氏による doScroll の動作チェックコードの頻度をあげるというアイデアです。
アイデアの中心となるコードは次のようなものです。

document.documentElement.style.setExpression('behavior', 'document.documentElement.polling()');

これを利用したDOMロードイベントのライブラリを公開します。
利用方法は、関数 onready の引数に、DOMロード後に実行したい関数を渡すだけです。

HTML :
<script src="http:// ... /onready.js" type="text/javascript" charset="utf-8"></script>
javascript :
onready(function(){
    alert('how are you?');
});
onready(function(){
    alert('fine! and you?');
});
onready(function(){
    alert('me, too!');
});

onready.js :
/*
    onready.js : Cross-Browser DOMContentLoaded Event Listener

    Copyright (c) 2007 latchet (http://juce6ox.blogspot.com/)
    Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
*/
var onready = (function(){
    var id,
        ua   = navigator.userAgent.toLowerCase(),
        fs   = [],
        i    = 0,
        done = false,
    /*@cc_on
        oldExp,
    @*/
        init = function(){
                   var f, j = 0;
                   if(done) return;
                   done = true;
                   /*@cc_on
                   if(oldExp) document.documentElement.style.setExpression('behavior', oldExp);
                   else       document.documentElement.style.removeExpression('behavior');
                   window.detachEvent('onload', init);
                   document.onreadystatechange = null;
                   /*@if(false)@*/
                   document.removeEventListener('DOMContentLoaded', init, false);
                   window.removeEventListener('load', init, false);
                   /*@end@*/
                   window.onload = null;
                   while(f = fs[j++]) f();
               },
        set  = function(){
                   var ok;
                   /*@cc_on
                   oldExp = document.documentElement.style.getExpression('behavior');
                   document.documentElement.polling = function(){
                       try{
                           document.documentElement.doScroll('left');
                       }catch(e){
                           return setTimeout(document.documentElement.polling, 0);
                       };
                       document.documentElement.polling = null;
                       init();
                   };
                   document.documentElement.style.setExpression('behavior', 'document.documentElement.polling()');
                   window.attachEvent('onload', init);
                   document.onreadystatechange = function(){
                       if(document.readyState == 'complete') init();
                   };
                   /*@if(false)@*/
                   if(/mozilla/.test(ua) && !/compatible|webkit/.test(ua) || /opera/.test(ua))
                       document.addEventListener('DOMContentLoaded', init, false);
                   else if(/webkit|khtml/.test(ua) && (ok = /loaded|complete/))
                       (function(){
                           return ok.test(document.readyState) ? init() : setTimeout(arguments.callee, 0);
                       })();
                   window.addEventListener('load', init, false);
                   /*@end@*/
                   var old = window.onload
                   window.onload = (typeof old == 'function') ? function(){old();init();} : init;
               };
    return function(f){
        if(typeof f != 'function') return;
        if(done) return f();
        if(!i) set();
        fs[i++] = f;
    };
})();

参考資料

このアイデアを思いついてコードを書き終えたころに、細かい違いはありますが、 同じようにbehaviorとexpressionを使うアイデアを、doScrollハックの考案者であるDiego Perini氏がjQueryのメーリングリストに投稿していたことを検索の結果、知りました(笑)

Nabble - Re: document.ready firing too early within iframe

その時は正直、すこしがっくりしましたが、気付かなかったからこそ取り組めた面もあるのかもしれません。コードを整えつつ、その後、新しい別の方法が無いか再び試行錯誤し、(自分にとって)新しいアプローチを見つけました。document.writeは使わず、IEのDHTMLのondocumentreadyイベントを利用しつつ、新たな通信をしなくてもすむとおもわれる方法です。

IE/DOMLoad : cached htc

window.onload Problem

javascript の window.onload イベントはページの解析、画像などのファイル読込が全て完了してから発生するようです。
そのため、ロードイベント前の文書の解析後に迅速にページのDOM要素に対してjavascriptで操作を行ないたいという要求がおこるのは、ユーザーの利便性を考慮すると当然といえそうです。

Ajaxという言葉が注目され、Googleによるjavascriptを活用したアプリケーションが登場し、 回線速度の向上、インフラ整備、ブログ作成者・閲覧者の増加、ウェブAPIの公開、 マッシュアップサイトが数多く登場しています。

そのような背景の中でjavascriptによる DOM ロードイベント検知の方法もいろいろ探究されてきたようです。今までの流れを書き出してみました。


枯れた技術が活用され、以前は想定されなかった問題が解決されていく様子に見えます。
HTML5草案では、DOMContentLoadedイベント(FirefoxとOperaは実装済)が提案されているそうです。
将来、SafariとIEにも採用されたら多くの人に喜ばれそう。