Skip to content

Instantly share code, notes, and snippets.

@hetima
Created July 27, 2011 10:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hetima/1109104 to your computer and use it in GitHub Desktop.
Save hetima/1109104 to your computer and use it in GitHub Desktop.
WebKit2 context menu hack
// based on http://d.hatena.ne.jp/uasi/20110722/1311275712
IMP orig_setMenuProxy;
// オリジナルの setMenuProxy: と差し替えるメソッド
void ST_setMenuProxy(id self, SEL _cmd, void *menuProxy)
{
// オリジナルのメソッドを呼んでやる
orig_setMenuProxy(self, _cmd, menuProxy);
// menuProxy は WebContextMenuProxyMac クラスのポインタ
// 16バイト目に NSPopUpButtonCell * へのポインタが格納されている
NSPopUpButtonCell *cell = *(NSPopUpButtonCell **)((void **)menuProxy + 2);
// cell から menu を取得
// プラグインや機能拡張によって追加されたメニュー項目も含む、画面に表示する直前の状態のメニューが取り出せる
NSMenu *menu = [cell menu];
// 選択文字列を調べる
// WKView を取得。menuProxy の 24バイト目
id wkview = *(id *)((void **)menuProxy + 3);
// 適当な名前で NSPasteboard 作成
NSPasteboard* pb=[NSPasteboard pasteboardWithName:@"com.hetima.safaristand"];
[pb clearContents];
// 書き込ませる
[wkview writeSelectionToPasteboard:pb types:[NSArray arrayWithObject:NSStringPboardType]];
if([[pb stringForType:NSStringPboardType]length]>0){
//NSLog(@"%@",[pb stringForType:NSStringPboardType]);
}
// 「コピー」項目(tag==8)の有無でもいいんだけどリンクをクリックしていると出てこない
// のでこっちの方が確実。NSPasteboard 経由で楽に選択文字列取得できるし。
// リンクをクリックしているかどうかは「リンクをコピー」項目があるかどうかで調べられる
NSMenuItem* copyLinkItem=[menu itemWithTag:3]; //3 == copy link
if(copyLinkItem){
// リンクの URL などは representedObject に入っている
// これを自分のメニュー項目に setRepresentedObject: して取っておけば良い。
// 具体的な取り出し方は別項で
id webUserDataWrapper=[copyLinkItem representedObject];
}
}
/*
コンテクストメニューの representedObject からリンク要素を得るサンプル
WebKit2.framework をリンクする必要がある。
うまくリンクできない場合は WebKit2.framework を /Developer/SDKs/xx.sdk/System/Library/PrivateFrameworks
にコピーすればよさげ
正直 WKRelease() は正しく使えているか自信がない
*/
enum Type { //from WebKit2/Shared/APIObject.h
TypeNull = 0,
TypeArray = 1,
TypeData = 7,
TypeDictionary = 8,
TypeString = 15,
TypeURL = 16
}
extern void* WKRetain(void* type);
extern void WKRelease(void* type);
extern uint32_t WKGetTypeID(void* typeRef);
extern void* WKDictionaryGetItemForKey(void* dictionary, void* key);
extern void* WKStringCreateWithUTF8CString(const char* string);
extern size_t WKStringGetMaximumUTF8CStringSize(void* string);
extern size_t WKStringGetUTF8CString(void* string, char* buffer, size_t bufferSize);
extern void* WKURLCopyString(void* url);
NSString* htWKNSStringFromWKString(void* wkStr){
if(!wkStr || WKGetTypeID(wkStr)!=TypeString)return nil;
size_t len=WKStringGetMaximumUTF8CStringSize(wkStr);
void* buf;
buf=malloc(len);
//終端のNULL付きです
len=WKStringGetUTF8CString(wkStr, buf, len);
NSString* keyStr=[[NSString alloc]initWithBytes:buf length:len-1 encoding:NSUTF8StringEncoding];
free(buf);
return [keyStr autorelease];
}
NSString* htWKNSStringFromWKURL(void* wkURL){
if(!wkURL || WKGetTypeID(wkURL)!=TypeURL)return nil;
void* wkStr=WKURLCopyString(wkURL);
NSString* result=htWKNSStringFromWKString(wkStr);
WKRelease(wkStr);
return result;
}
//TypeString と TypeURL に対応。TypeURL の場合も NSString を返す
NSString* htWKDictionaryStringForKey(void*dic, NSString* key){
void* wkStr=WKStringCreateWithUTF8CString([key cStringUsingEncoding:NSUTF8StringEncoding]);
void* val=WKDictionaryGetItemForKey(dic, wkStr);
WKRelease(wkStr);
if(val){
uint32_t type=WKGetTypeID(val);
if(type==TypeString) return htWKNSStringFromWKString(val);
if(type==TypeURL) return htWKNSStringFromWKURL(val);
}
return nil;
}
// ここまでライブラリ的なやつ
// action の例
/*
メニュー項目の action から呼ばれる
representedObject には「リンクをコピー」から横取りした WebUserDataWrapper を入れておく
WKDictionary には以下のキーが含まれている
"ImageURL", "IsLinkDataLocal", "LinkLabel", "CanHandleLinkURL", "ElementIsSelected",
"LinkURL", "AllowsFollowingLinkURL", "Frame", "LinkTitle", "SelectionString", "MediaURL"
*/
-(void)copyLinkAndTitleMenuAction:(id)sender{
id webUserDataWrapper=[sender representedObject];
void* apiObject=[webUserDataWrapper userData]; //struct APIObject
uint32_t type=WKGetTypeID(apiObject);
if(type==TypeDictionary){ //8==TypeDictionary
NSString* linkStr=htWKDictionaryStringForKey(apiObject, @"LinkURL");
NSString* titleStr=htWKDictionaryStringForKey(apiObject, @"LinkLabel");
if(!linkStr)linkStr=@"";
if(!titleStr)titleStr=@"";
NSString* result=[NSString stringWithFormat:@"%@\n%@", titleStr, linkStr];
NSPasteboard*pb=[NSPasteboard generalPasteboard];
[pb clearContents];
[pb setString:result forType:NSStringPboardType];
}
}
@hetima
Copy link
Author

hetima commented Aug 5, 2011

WKStringGetUTF8CString() の返り値はヌル文字付き。NSStringを作るなら initWithBytes:length:encoding: で1バイト少なく指定するか initWithUTF8String: にそのまま渡すか

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment