Hatena::ブログ(Diary)

Web系がおもしろい。

2010-12-23

GoogleChromeやOperaでもGM_*Value(s)系関数を使えるようにする

| 15:49 | GoogleChromeやOperaでもGM_*Value(s)系関数を使えるようにするのブックマークコメント

Firefox向けのuserJS書いていたんですが、GoogleChrome, Operaにも対応させたくなったので、以下のサイト様を参考にGM_*Value(s)系の関数が実装されていないときにoverrideする処理書きました。


参考元サイトさま。

GM_setValue系をChromeでも使えるようにする - Firefoxアドオンとか


ソース

/**
 * 各関数が実装されていない場合、localStorageで代用する
 */
function GM_XBrowse() {
	var gmFuncs = {};
	
	// functions ----
	gmFuncs.GM_setValue = function (key,value) {
		return localStorage.setItem(key, value);
	};
	gmFuncs.GM_getValue = function (key) {
		return localStorage.getItem(key);
	};
	gmFuncs.GM_deleteValue = function (key) {
		return localStorage.removeItem(key);
	};
	gmFuncs.GM_addStyle = function (doc, css) {
		var head, style;
		head = document.getElementsByTagName("head")[0];
		if (!head) { return; }
		style = document.createElement("style");
		style.type = "text/css";
		style.innerHTML = css;
		head.appendChild(style);
	};
	gmFuncs.GM_listValues = function () {
		var list = [];
		for(var i=0, len=localStorage.length; i<len; i++) {
			list.push(localStorage.key(i));
		}
		return list;
	};

	// set ----
	if(typeof GM_setValue == 'function') {
		try {
			if(GM_setValue.toString().indexOf("not supported") > -1) {
				GM_setValue = gmFuncs.GM_setValue;
			}
		} catch(e) {}
	} else {
		GM_setValue = gmFuncs.GM_setValue;
	} 

	if(typeof GM_getValue == 'function') {
		try {
			if(GM_getValue.toString().indexOf("not supported") > -1) {
				GM_getValue = gmFuncs.GM_getValue;
			}
		} catch(e) {}
	} else {
		GM_getValue = gmFuncs.GM_getValue;
	} 

	if(typeof GM_deleteValue == 'undefined') {
			GM_deleteValue = gmFuncs.GM_deleteValue;
	}
	if(typeof GM_listValues == 'undefined') {
			GM_listValues = gmFuncs.GM_listValues;
	}
	if(typeof GM_addStyle == 'undefined') {
			GM_addStyle = gmFuncs.GM_addStyle;
	}
}

実際に動かしてみる場合はこちら:https://gist.github.com/1027730


※僕が確認した時は、Chromeでも GM_addStyle() サポートされてたんですけど、もしかしてChrome9.0bだからですかね。。一旦ダウングレードしてみます。

Opera11にGM_addStyle()がなかったので追記(記事の主題からははずれますが)。引用元:GM_addStyleの実装と最適化 - os0x.blog


注意事項

Web Storage(localStorageオブジェクト)は、GM_*Value(s)関数と似た働きをしてくれるんですが、以下の2つには注意しましょう。。

  • 同じorigin上でしかデータが共有できない
  • 他のスクリプトから書きかえられる可能性がある

GM_*Value(s)系関数は、複数のドメイン間でも関係なしに値を取得・設定できます。

例えば、GoogleのTOPページでGM_setValue("hoge", "fuga");としたあと、YahooのTOPページでGM_getValue("hoge");とすると、"fuga"が出力されます。


originに関する参考資料:no title


ところで

毎回if文で「GM_hogehoge == 'undefined'」書いてるのうざい

関数オブジェクト渡して処理、なんてことを真っ先に考えたんですが、

undefinedなオブジェクト関数に渡す前で例外吐いちゃうんですね。。

var isNotSupported = function(gmObj) {
	return (typeof gmObj == 'undefined'
		|| gmObj.toString().indexOf("not supported") > -1);
};
if(isNotSupported(GM_getValue)) { // 例外発生
	GM_getValue = function (key) {
		return localStorage.getItem(key);
	};
}

これを避けるために、"GM_getValue"みたいに文字列を関数に渡して処理する方法も考えたんですが、Firefoxでもtypeof Object == 'undefined' の判定をするとtrueになってしまいました。

var isNotSupported = function(gmStr) {
	return (typeof unsafeWindow[gmStr] == 'undefined' // Firefoxでもtrue
		|| unsafeWindow[gmStr].toString().indexOf("not supported") > -1);
};
if(isNotSupported("GM_getValue")) {
	GM_getValue = function (key) {
		return localStorage.getItem(key);
	};
}

どうやら、GM_*の関数ビルドイン関数のような扱いになっていて、windowオブジェクトやunsafeWindowオブジェクトに属していないようです。。

alert(typeof window["GM_getValue"]); // undefined
alert(typeof unsafeWindow["GM_getValue"]); // undefined
alert(typeof GM_getValue); // function

クロスブラウザ対策を行ったクラスを作った方がスマートかも

すでにスクリプト作っちゃってて対策するのめんどくさいんだけど!っていう人以外は、関数をオーバーライドするのではなく、自分でクラスを作ってそれを使った方が柔軟に対応できそうです。


あとここまで書いておいてなんですけど、FirefoxWeb Storageサポート済みです。事足りるなら一本化するのもありかも。


追記:Firefox4に対応

Firefox4から、GM_setValue#toString() を行うと例外エラーが発生するようになりました。そのため、try-catchで対処しています。

try-catch嫌いなので他の方法取りたかったんですが、ぱっと思い浮かびませんでした…。

eval使うともう少しすっきりするんですが、キモいので使ってません。


Firefox4RC版で、以下のスクリプトがエラーを吐いてしまうことを確認しました。調査中です。。

Firefox4に対応させました。

トラックバック - http://d.hatena.ne.jp/esperia/20101223/1293086983