Emacs LispとRubyを使ってGoogle Chromeを操作する

私は以前までブラウザはFirefoxを使っていましたが、Firefox4MacFlashが見れなくなったり、Firefox5でタブを切り替えても表示が切り替わらない(この現象は検索してもヒットしないので自分の環境だけの可能性有り)といった不具合があってから、メインのブラウザをGoogle Chromeに乗り換えています。

FirefoxからGoogle Chromeに乗り換えるのをためらった理由の1つが、自作したEmacsとの連携機能Chromeに移行すると使えなくなるというものでしたが、google-chrome-clientという別プログラムからChromeにアクセスするためのRubyライブラリの存在を知ったので、Emacs連携機能のGoogle Chrome版を作成しました。

この機能を使うためには、Google Chromeを次のオプションで起動します。

--remote-shell-port=9222

Macの場合、Google Chromeの実行形式へのパスは、/Applications/Google Chrome.app/Contents/MacOS/Google Chromeです。
このオプション付きで起動すると、「ChromeDevTools Protocol」というプロトコルが使えるようになります。

Ruby側のプログラムは、google-chrome-clientというライブラリを使用しているので、まずそちらをインストールします。

gem install google-chrome-client

続いて、以下のスクリプトを適当なファイル名で保存します。

require 'rubygems'
require 'google/chrome/client'

module Google
  module Chrome

    class Client
      def ping
        header, resp = self.request({ 'Tool' => 'DevToolsService' },
                                    { 'command' => 'ping' })
        resp['data']
      end
      def list_tabs
        header, resp = self.request({ 'Tool' => 'DevToolsService' },
                                    { 'command' => 'list_tabs' })
        resp['data']
      end
    end

    class Tab
      def evaluate_javascript(script)
        body = {'command' => 'evaluate_javascript', 
                'data' => script}
        number = @number
        @client.instance_eval do
          write_request({ 'Tool' => 'V8Debugger', 'Destination' => number },
                        body)
        end
      end
    end

  end
end

if $0 == __FILE__
  url = ARGV[0]
  client = Google::Chrome::Client.new('localhost', 9222)
  client.tabs[0].evaluate_javascript("window.open('#{url}');")
end

Emacs側のプログラムは先ほどのRubyプログラムのラッパーになっています。コードそのものはFirefox版のコピペです。chromerepl-open-uri-pathに先ほどのRubyプログラムのパスを指定します。

(defun uri-encode (str)
  (mapconcat
   (lambda (s)
     (mapconcat
      (lambda (x) (format "%%%x" x))
      (vconcat (encode-coding-string s 'utf-8))
      ""))
   (split-string str)
   "+"
   ))

(defvar chromerepl-open-uri-path "~/.emacs.d/bin/chromerepl-open-uri.rb")

(defun chromerepl-open-uri (uri)
  (interactive "suri: ")
  (let ((cmd (format "ruby %s '%s'" chromerepl-open-uri-path uri)))
  (shell-command cmd)))

(defun chromerepl-open-uri-region (begin end)
  (interactive "r")
  (let (str)
    (setq str (buffer-substring-no-properties begin end))
    (chromerepl-open-uri str)))

(defun chromerepl-alc-search (word)
  (interactive "sword: ")
  (chromerepl-open-uri (format "http://eow.alc.co.jp/%s/UTF-8/" (uri-encode word))))

(defun chromerepl-alc-search-region (begin end)
  (interactive "r")
  (let (str)
    (setq str (buffer-substring-no-properties begin end))
    (chromerepl-alc-search str)))

(defun chromerepl-google-search (keywords)
  (interactive "skeywords: ")
  (chromerepl-open-uri (format "http://www.google.co.jp/search?hl=ja&q=%s" (uri-encode keywords))))

(defun chromerepl-google-feeling-lucky (keywords)
  (interactive "skeywords: ")
  (chromerepl-open-uri (format "http://www.google.co.jp/search?hl=ja&btnI=&q=%s" (uri-encode keywords))))

(defun chromerepl-google-search-region (begin end)
  (interactive "r")
  (let (str)
    (setq str (buffer-substring-no-properties begin end))
    (chromerepl-google-search str)))

(defun chromerepl-google-feeling-lucky-region (begin end)
  (interactive "r")
  (let (str)
    (setq str (buffer-substring-no-properties begin end))
    (chromerepl-google-feeling-lucky str)))

(defun chromerepl-google-translate-region (begin end)
  (interactive "r")
  (let (str uri)
    (setq str (buffer-substring-no-properties begin end))
    (setq uri (format "http://translate.google.co.jp/translate_t?hl=ja&sl=en&tl=ja#%s%s"
		      (if (equal (find-charset-region begin end) '(ascii)) "en|ja|" "ja|en|")
		      (uri-encode str)))
    (chromerepl-open-uri uri)
    ))

(defmacro define-chromerepl-x-search (name site)
  (let ((sym-i (intern (concat "chromerepl-" (symbol-name name) "-search")))
	(sym-g (intern (concat "chromerepl-" (symbol-name name) "-search-result")))
	(url-i (format "http://www.google.co.jp/search?hl=ja&btnI=&as_sitesearch=%s&q=%%s" site))
	(url-g (format "http://www.google.co.jp/search?hl=ja&as_sitesearch=%s&q=%%s" site)))
    `(progn
       (defun ,sym-i (keywords)
	 (interactive "skeywords: ")
	 (chromerepl-open-uri (format ,url-i (uri-encode keywords))))
       (defun ,sym-g (keywords)
	 (interactive "skeywords: ")
	 (chromerepl-open-uri (format ,url-g (uri-encode keywords))))
       )
    ))
(defmacro chromerepl-x-search-expand ()
  (let ((chromerepl-x-search-list
	 '((ruby        . "doc.ruby-lang.org")
	   (python      . "docs.python.org"  )
	   (lisp        . "www.lispworks.com")
	   (gauche      . "practical-scheme.net/gauche/man")
	   (django      . "docs.djangoproject.com/en/1.2")
	   )))
    `(progn
       ,@(loop for elt in chromerepl-x-search-list
	       collect `(define-chromerepl-x-search ,(car elt) ,(cdr elt))))    
    ))
(chromerepl-x-search-expand)