GreaseMonkey Driven Development / Shibuya.js Technical Talk #2

GreaseMonkey Driven Development

cho45(さとう) / lowreal.net


はなしの内容:

GM インスコしたけど...

とかそんな人向け。

最初は自分も毎回 Install Script してました。なんだこれめんどくせーよとか思ってました。

バリバリ書きまくってる人は知りません。

とりあえずエディタの設定はしよう

  1. about:config
  2. greasemonkey.editor

例: C:\apps\xyzzycli.exe

これで設定画面の Edit から開けるように

いや何も設定されてないと選択ダイアログが開くはずだけど...

GM では微妙なタイミングででてくるエディタ選択画面。なんか挙動が謎いので自分で設定しよう。

これで設定の Edit から編集できるようになるで、とりあえず既存スクリプト修正は楽に

いいテキストエディタを使おう

言うまでもない。

ともあれ開発を容易に1

どこでも適用できる user.js を作っとく。

あとてきとーに関数の雛形書いて保存しておく

てんぷれ例

// ==UserScript==
// @name        Test
// @description Test
// @namespace   http://lowreal.net/
// @include     *
// ==/UserScript==

(function () {


    /*

    GM_xmlhttpRequest({
        method : "GET",
        url : url,
        headers : {
            "User-Agent":"monkeyagent",
            "Accept":"text/monkey,text/xml",
        },
        onload : function (req) {
            var doc = new XML(req.responseText.replace(/^<\?xml.+?\?>/, ''));
        },

        onerror : function (req) {
            alert(req.responseText);
        }
    });
     */

    function $N (name, attr, childs) {
        var ret = document.createElement(name);
        for (k in attr) {
            if (!attr.hasOwnProperty(k)) continue;
            v = attr[k];
            if (k == "class") {
                ret.className = v;
            } else {
                ret.setAttribute(k, v);
            }
        }
        switch (typeof childs) {
            case "string": {
                ret.appendChild(document.createTextNode(childs));
                break;
            }
            case "object": {
                for (var i = 0, len = childs.length; i < len; i++) {
                    var child = childs[i];
                    if (typeof child == "string") {
                        ret.appendChild(document.createTextNode(child));
                    } else {
                        ret.appendChild(child);
                    }
                }
                break;
            }
        }
        return ret;
    }

    // Using XPath Expression Object, snapshot is buggy (crash@Fx1.5.01)
    // exp = "string(/)";
    // var resolver = document.createNSResolver(document.documentElement);
    // var exp      = document.createExpression(exp, resolver);
    // var result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

    function $X (exp, context) {
        if (!context) context = document;
        var resolver = function (prefix) {
            var o = document.createNSResolver(context)(prefix);
            return o ? o : (document.contentType == "text/html") ? "" : "http://www.w3.org/1999/xhtml";
        }
        var exp = document.createExpression(exp, resolver);

        var result = exp.evaluate(context, XPathResult.ANY_TYPE, null);
        switch (result.resultType) {
            case XPathResult.STRING_TYPE : return result.stringValue;
            case XPathResult.NUMBER_TYPE : return result.numberValue;
            case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
            case XPathResult.UNORDERED_NODE_ITERATOR_TYPE: {
                result = exp.evaluate(context, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
                var ret = [];
                for (var i = 0, len = result.snapshotLength; i < len ; i++) {
                    ret.push(result.snapshotItem(i));
                }
                return ret;
            }
        }
        return null;
    }

    function d(args) {
        var out = [];
        for (var i = 0; i < arguments.length; i++) out.push(arguments[i]);
        if (navigator.userAgent.match(/Firefox/)) {
            dump(out.join(" ") + "\n");
        } else {
            // alert(out.join(" "));
        }
    }


})();

ともあれ開発を容易に2

そんな感じで test.user.js をインストール

インストール済みのを Edit を押してエディタでひらきっぱ

ともあれ開発を容易に3

他人のスクリプトをパクる

他人のスクリプト読むと「あ~アレね~」とか思って世界が広がるます。

そういうことで

  1. ブラウズしてて何か思いつく
  2. 開きっぱなしの test.user.js を編集してゴリゴリ書く。
  3. よくね? と思ったら分離する。
  4. 「こりゃだめだ」と思ったら諦めてコメントアウトして、ウェブブラウズ再開

ポイントまとめ

なんというか

リロードだとキャッシュが最新か確かめたりするので、ページ移動のほうがいいときもある。画像が多いページとか

GDD

ヒントだけ。

ページ内の window オブジェクトは GM では unsafeWindow としてアクセスできる。

だからそのサイトのスクリプトを置き換えたりすることもできる。

LDR 拡張系 user.js はコレ

unsafeWindow 使用時の注意

unsafeWindow は unsafeWindow という名の通り、安全ではないオブジェクトです。サイト内スクリプトが触れる window オブジェクトをいぢくるわけですから、unsafeWindow 以下のオブジェクトに GM_xmlhttprequest などを渡してしまうと (単に引数に GM_* を渡すだけでも、コールスタックから見える状態になってしまう場合がある) 、脆弱性の引き金になります。unsafeWindow を使って書いたものを公開する場合は慎重になるべきです。

このため、unsafeWindow オブジェクトは常に「unsafeWindow」としてアクセスするように心がけ、可能な限り他の名前をつけない (代入しない) ようにし、関数の引数に GM_* は絶対に渡さないようにすべきです。

詳しくは GreaseMonkey のドキュメント を読んでください。

おまけ

Opera 向け注意

XMLHttpRequest は難しいことしないとドメインを超えられない。

Opera では GreaseMonkey ほぼ互換のファイル形式が使えます。

最近 Opera9 がリリースされ、XPath などがサポートされました。

GreaseMonkey では DOM オブジェクトが構築できた時点でスクリプトが実行されます。