ページ

2009年10月21日水曜日

Safari用独自プラグインを作る(7) - Evernote を class-dump する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: Safari用独自プラグインを作る(6) - Safari のクラス構成

Safari を class-dump した後、Evernote のプラグインが気になったので class-dump してみた。

$ class-dump -H -o evernote ~/Library/Internet\ Plug-Ins/
  EvernoteSafariClipperPlugin.webplugin/Contents/
  MacOS/EvernoteSafariClipperPlugin
※見やすい様に改行を入れてある。


AGProcess-MachTaskEvents.h
AGProcess-Private.h
AGProcess-Signals.h
AGProcess.h
CDStructures.h
ENClipper.h
ENMIMEUtils.h
ENSafariClipper.h
ENSafariClipperPluginView.h
ENSafariContextMenuSwizzler.h
ENSafariToolbarSwizzler.h
ENSwizzlerHelper.h
NSObject-Protocol.h
NSString-NSStringAdditions.h
WebPlugInViewFactory-Protocol.h

Info.plist によると Principal class(最初にロードされるクラス)は、 ESSafariClipperPluginView となっていた。



引用すると:

ENSafariClipperPluginView.h
#import "NSView.h"

#import "WebPlugInViewFactory-Protocol.h"

@interface ENSafariClipperPluginView : NSView <WebPlugInViewFactory>
{
}

+ (void)initialize;
+ (id)plugInViewWithArguments:(id)arg1;
+ (id)mimePListPath;
- (void)deletePListFile;
- (void)delayedPListFileDelete;

@end

よく見ると WebPlugInViewFactoryを実装し、NSViewのサブクラスとなっている。一方、他のヘッダファイルを見たが WebPlugIn(非形式プロトコル)のメソッドは実装していない。一部のみ Webkit-Plugin の作法に準拠させているようだ。それと初期化は +[load] ではなく +[initialize] で行っている。

眺めていると他に Swizzler と名のつくファイルがいくつかあった。
ENSafariContextMenuSwizzler.h
ENSafariToolbarSwizzler.h
ENSwizzlerHelper.h 
このあたりのクラスがメソッド置換を受け持っているようだ。前2つのクラスは名前からしてコンテキストメニューとツールバーに関連していることがわかる。

定義を覗いてみよう。

ENSafariContextMenuSwizzler.h
#import "ENSwizzlerHelper.h"

@interface ENSafariContextMenuSwizzler : ENSwizzlerHelper
{
}

+ (void)createSharedInstance;
- (id)init;
- (id)webViewFromElementInfo:(id)arg1;
- (void)contextMenuAddItemChosen:(id)arg1;
- (void)contextMenuAddPage:(id)arg1;
- (void)contextMenuAddPageAsPDF:(id)arg1;
- (id)webView:(id)arg1 contextMenuItemsForElement:(id)arg2 defaultMenuItems:(id)arg3;

@end

定義では Safariで定義されているクラス BrowserWebView のメソッド -[BrowserWebView webView:contextMenuItemsForElement:defaultMenuItems:] がここで定義されている。なるほどコンテキストメニューへの干渉はこのメソッドを置換すれば良いのか。

同様に ENSafariToolbarSwizzler.h を見ると、Safariの ToolbarController.h で定義されたメソッドが存在する(toolbar*で始まるメソッドなど)。

ENSafariToolbarSwizzler.h
#import "ENSwizzlerHelper.h"

@interface ENSafariToolbarSwizzler : ENSwizzlerHelper
{
}

+ (BOOL)muckWithThisToolbar:(id)arg1;
+ (void)createSharedInstance;
- (BOOL)toolbarContainsElephant:(id)arg1;
- (void)insertElephantIntoToolbar:(id)arg1;
- (void)setupToolbars;
- (id)init;
- (id)toolbarDefaultItemIdentifiers:(id)arg1;
- (id)toolbarAllowedItemIdentifiers:(id)arg1;
- (id)toolbar:(id)arg1 itemForItemIdentifier:(id)arg2 willBeInsertedIntoToolbar:(BOOL)arg3;
- (void)elephantToolbarButtonAction:(id)arg1;

@end

なるほど。

なお、メソッド名に"elephant" と名付けているところがちょっと面白い(Evernoteのアイコンは象の顔)。

上記2つのクラスの親クラスにあたる ENSwizzlerHelper はメソッド置換のメソッドを提供しているようだ。

ENSwizzlerHelper.h
#import "NSObject.h"

@interface ENSwizzlerHelper : NSObject
{
}

- (void *)swizzleInstanceMethodWithSelector:(SEL)arg1 fromClass:(Class)arg2;

@end

セレクタを一つしか渡さないところを見ると、サブクラスでターゲットと同じメソッドを定義しているので暗黙的にそれを置換先のセレクタとしているのかもしれない。今まで見てきたメソッド置換コードの多くは適当なメソッド名を割り当てていたので、それに比べてスマートな感じがして面白い。後で(実装を想像して)自分で書いてみよう。

その他、Evernote本体と連携する目的と思われる AGProcess* などが用意されている。

- - - -
ヘッダファイルだけでもずいぶんと参考になった。
こういった解析はちょっと楽しい。
これらを参考にして Safariへの侵入にとりかかることにしよう。