これまで見た例や他のソフトを見ると、SIMBL を使えば WebView などにアクセスできそうだ。
ただ Evernote は SIMBL を使っている気配がないがスクリーンショットが撮れている。
できれば SIMBL を使わず通常のプラグインだけで対応したいので、とりあえず WebKit Plug-in で、何が、どこまで、できるのか調べてみる事にする。
WebKit Plug-In
WebKit Plug-In Programming Topics
参考)Apple Mailing Lists
developing toolbar plugin for Safari
※標準APIでは Safariのカスタムツールバーは作れないとのこと。そういえば Evernote の場合、問題がネットで上がっていたし、挙動を見るとSafari標準のツールバーが揃ってしばらくした後、アイコンが突然現れていた。
2009年10月4日日曜日
WebKit Plug-in
2009年10月2日金曜日
Safariでキャプチャ調査(1) 〜 Evernoteの WebKit Plug-in を見てみた
Safari でWebページをキャプチャできるツールを探してみたところ、Evernote が Safari 用のプラグインを提供していた。
Welcome to your notable world | Evernote Corporation
ちなみに通常のプラグインファイルは ~/Library/Internet Plug-Ins/ 配下にある。自分のホームディレクトリだからインストール時に root権限が要求される(ダイアログが表示される)こともない。

ツールバーの他、コンテキストメニューにも Evernote 用のメニューが追加される。
2009年9月22日火曜日
WPSU(16) - WebKitで新規ウィンドウを開く(Documentベースアプリへ移行)
WPSU はシングルウィンドウのアプリとして作成しているため、新規ウィンドウを開くようなリンクを押しても反応しない。
Safari など普通のブラウザと同様にリンクが開くようにする。このあたりは自分で実装するよりは標準のフレームワークを使った方が楽にできる。今までのプロジェクトを一旦捨てて新しいプロジェクトへソースコードを載せ替える。
最初のテンプレートで document-based application を選択する。
後は旧プロジェクトから必要なソースコードをコピーしてプロジェクトへ加える。従来 WebController で行っていた処理は MyDocument へ載せ変えた。
その上で、リンクが押されたときの処理を追加する。
まず Interface Builder を使い、WebViewの UIDelegate を File's Owner(すなわち MyDocument)へ設定する。
次に -[WebUIDelegate webView:createWebViewWithRequest:] を MyDocumentに実装する。
MyDocument.m
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
{
MyDocument* document = [[NSDocumentController sharedDocumentController] openUntitledDocumentOfType:@"DocumentType" display:YES];
[[[document webView] mainFrame] loadRequest:request];
return [document webView];
}
新規にドキュメントを作成し、その上に配置された WebView でリクエストを処理させる。
実行してみよう。

出た。
- - - -
JavaScriptを ON にするとエラーが出て強制終了してしまった。
どうも JSを使うブログパーツが原因で何か問題が起きているようだ。
2009年8月17日月曜日
WPSU(14) - WebKitでクッキーを自前でハンドリングする #4 NSHTTPCookieを調べる
#タイトルが長くなってきた
さてクッキーを自前でハンドリングするにしても用意されているクラスが使えればそれにこしたことが無い。Foudation.framework には幸いなことに NSHTTPCookie クラスが用意されている。
NSHTTPCookie Class Reference
これが使えそうかどうか調査してみよう。
リファレンスを眺めると3つのインスタンス作成メソッドが用意されていた。
このうち、+[NSHTTPCookie cookiesWithResponseHeaderFields:forURL:] を試してみる。
cookiesWithResponseHeaderFields:forURL:
デリゲートメソッドに仕掛ける。
サーバから受け取ったレスポンス内のヘッダ情報を渡してみた。
AppController.m
- (void)webView:(WebView *)sender
resource:(id)identifier
didReceiveResponse:(NSURLResponse *)response
fromDataSource:(WebDataSource *)dataSource
{
NSDictionary* headers = [(NSHTTPURLResponse*)response allHeaderFields];
NSURL* url = [response URL];
NSArray* cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:headers forURL:url];
NSLog(@"---------------");
NSLog(@"%@", url);
NSLog(@"%@", cookies);
}
実行してみよう。Googleへ行き、非ログイン状態で「ログイン」リンクを押してみた。

するとサーバからクッキーが送られてきた。先ほどのメソッドでうまく取り込めているようだ。
2009-08-17 21:14:58.569 WebPageScreenshotUtility[2160:10b] ---------------
2009-08-17 21:14:58.621 WebPageScreenshotUtility[2160:10b] https://www.google.com/accounts/Login?hl=ja&continue=http://www.google.co.jp/
2009-08-17 21:14:58.640 WebPageScreenshotUtility[2160:10b] (
<NSHTTPCookie
version:0
name:@"GoogleAccountsLocale_session"
value:@"ja"
expiresDate:@"(null)"
created:@"272175298.569016"
sessionOnly:TRUE
domain:@"www.google.com"
path:@"/accounts"
secure:FALSE
comment:@"(null)"
commentURL:@"(null)"
portList:(null)>,
<NSHTTPCookie
version:0
name:@"GALX"
value:@"fo7Y_pW13-M"
expiresDate:@"(null)"
created:@"272175298.569091"
sessionOnly:TRUE
domain:@"www.google.com"
path:@"/accounts"
secure:TRUE
comment:@"(null)"
commentURL:@"(null)"
portList:(null)>
)
※読みやすい様に改行を入れてある
- - - - -
おお、これは便利。ヘッダ内のCookieの解析はこれが使えそうだ。
2009年8月15日土曜日
WPSU(13) - WebKitでクッキーを自前でハンドリングする #2 レスポンスを見る
こんどはレスポンスを見てみる。
-[WebResourceLoadDelegate webView:resource:didReceiveResponse:fromDataSource:]を実装し、レスポンスヘッダを書き出してみる。
WeController.m
- (void)webView:(WebView *)sender
resource:(id)identifier
didReceiveResponse:(NSURLResponse *)response
fromDataSource:(WebDataSource *)dataSource
{
NSLog(@"%@", [(NSHTTPURLResponse*)response allHeaderFields]);
}
Googleへアクセスしログアウトしたところ下記のヘッダが得られた。
2009-08-15 12:36:15.842 WebPageScreenshotUtility[10622:10b] {
"Cache-Control" = "private, max-age=0";
"Content-Encoding" = gzip;
"Content-Length" = 282;
"Content-Type" = "text/html; charset=UTF-8";
Date = "Fri, 15 Aug 2009 03:36:15 GMT";
Expires = "Fri, 15 Aug 2009 03:36:15 GMT";
Server = "GFE/2.0";
"Set-Cookie" = "GoogleAccountsLocale_session=ja, SID=EXPIRED;Domain=.google.co.jp;Path=/;Expires=Mon, 01-Jan-1990 00:00:00 GMT, HSID=EXPIRED;Domain=.google.co.jp;Path=/;Expires=Mon, 01-Jan-1990 00:00:00 GMT, SSID=EXPIRED;Domain=.google.co.jp;Path=/;Expires=Mon, 01-Jan-1990 00:00:00 GMT";
"X-Content-Type-Options" = nosniff;
}"Set-Cookie" が得られた。
- - - - - -
WebView を使った場合、2つのデリゲートでそれぞれクッキーの送出と保存を行えば良さそうだ。
2009年8月14日金曜日
WPSU(12) - WebKitでクッキーを自前でハンドリングする #1 リクエストを調べる
クッキーをWebKitアプリ間で共有しないようにするには、クッキーの処理を自前でやる必要があることがわかった。WSBUでは共有したくないのでクッキー処理を実装することにする。
普通なら「まずクッキーの仕様を調べて、、」といくところだが大雑把な内容は理解しているつもりなので実装を進めながら確認を取っていこう。
まずは WebKit でクッキーを自前処理する方法を調べる。
WebView の WebResourceLoadDelegate に先日作成した WebController を指定する。ここにデリゲートメソッド webView:resource:willSendRequest:redirectResponse:fromDataSource: を実装してみた。
WebController.m
- (NSURLRequest *)webView:(WebView *)sender
resource:(id)identifier
willSendRequest:(NSURLRequest *)request
redirectResponse:(NSURLResponse *)redirectResponse
fromDataSource:(WebDataSource *)dataSource
{
NSLog(@"%@", [request allHTTPHeaderFields]);
return request;
}
調査目的でリクエストヘッダの内容をデバッガコンソールへ出力させている。
google.com へアクセスしてみよう。
デバッガコンソール(抜粋)
[Session started at 2009-08-14 12:14:01 +0900.]
2009-08-14 12:14:05.494 WebPageScreenshotUtility[10382:10b] {
Accept = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
"User-Agent" = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3";
}
2009-08-14 12:14:07.097 WebPageScreenshotUtility[10382:10b] {
Accept = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
"Accept-Encoding" = "gzip, deflate";
"Accept-Language" = "ja-jp";
"User-Agent" = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3";
}
2009-08-14 12:14:07.505 WebPageScreenshotUtility[10382:10b] {
Referer = "http://www.google.co.jp/";
"User-Agent" = "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_8; ja-jp) AppleWebKit/530.19.2 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3";
}
見たところクッキーヘッダが無い。
クッキーが送出されていないと思いきや、google.comを見るとログイン状態になっている。

どうもこのメソッドが呼ばれるタイミングではまだクッキー情報が付加されていないようだ。
試しに setHTTPShouldHandleCookies: を使い、クッキー処理を停止させてみる。
[(NSMutableURLRequest*)request setHTTPShouldHandleCookies:NO];
すると今度は google.com へアクセスしてもログイン状態にはなっていない。なるほど。
2009年8月12日水曜日
WebKit を使ったアプリケーションはクッキーを共有する
WPSU を作っていて気になったことがある。
WPSU を使って Googleを開くとログインしたはずが無いのに、ログイン状態で表示される。
(右上にメールアドレスが記載)
普段 Safari でこのアカウントを使っているのだが、
もしやクッキーは WebKitを使ったアプリケーションで共有されている?
Safariを開きログイン状態であることを確認した後、試しに WPSUでログアウトしてみると Safariの方でもログアウト状態になっていた。おおなんてこった。これってまずくないのか?
WebKitにはクッキーを扱うクラスは見つからなかった。Foundation.framework を調べると NSHTTPCookieStorage というクラスが見つかった。キャッシュの時と同様 Foundation.framework で管理されているようだ。
ADC - NSHTTPCookieStorage Class Reference
Overview には、はっきりと "a singleton object (shared instance)" と書かれている。
サンプルを作って動作を見てみる。
サンプル:CookieStudy.zip
-[NSHTTPCookieStorage cookies]の結果をデバッガコンソールへ吐き出してみよう。
コードはこんな感じ。
@implementation AppController
- (void)awakeFromNib
{
NSHTTPCookieStorage* cookie_storage =
[NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* cookies = [cookie_storage cookies];
NSLog(@"%@", cookies);
}
@end
実行するとでるわでるわ、すべてのクッキーが表示された。

表示内容はこんな感じ。
<NSHTTPCookie version:0 name:@"__utmz" value:@"1981754.1243.1.1.utmcsr=google.co.jp/"
expiresDate:@"2010-01-13 00:40:03 +0900"
created:@"2692303.4789"
sessionOnly:FALSE
domain:@".deep.csi.com"
path:@"/"
secure:FALSE
comment:@"(null)"
commentURL:@"(null)"
portList:(null)>
※一部値を改変
- - - -
悪意のある WebKitアプリケーションがクッキー情報を好きにできるってことか。
と思ったりもしたが、
この件に限らず悪意のあるプログラムを走らせたら何でもできるという点で特別問題視する事でもないか。
ただ異なるブラウザ間でログイン状態が共有されるのはあまり気持ちのよい状況ではないな。
2009年8月7日金曜日
WPSU(11) - UserAgent を変える(Safariを名乗る?)
WebKitで作った簡易ブラウザで Gmail へアクセスすると「Gmail には完全にサポートされたブラウザのご利用をおすすめします。」と表示され、ユーザインターフェイスが Safariで見るそれとは違った少ししょぼい?ものなる。
UserAgentで判断しているのだろうとあたりをつけて、まずは Safariと簡易ブラウザの UserAgentを調べてみた。
Safari 3.2.3
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_7; ja-jp)
AppleWebKit/525.28.3 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3"
WebKit簡易ブラウザ
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_7; ja-jp)
AppleWebKit/525.28.3 (KHTML, like Gecko)"
前半は同じだが Safari の場合は最後にバージョン番号とアプリ名/ビルド番号が追加されている。
WebKit のドキュメントには Spoofing という項目が設けられていて UserAgent についての話題が取り上げられている。ここで紹介されていたメソッドを使い試してみよう。
WebKit Objective-C Programming Guide - Spoofing
まずは - [WebView setCustomUserAgent:] から。
試しに適当な文字列を設定してみる。
[_web_view setCustomUserAgent:@"CustomUserAgent"];
するとサーバ側では UserAgentに指定文字列がそのまま出てきた。
"CustomUserAgent"
次に - [WebView setApplicationNameForUserAgent:] を試す。
[_web_view setApplicationNameForUserAgent:@"Sample"];
すると WebKit標準のUserAgentの最後に指定文字列をものが出てきた。
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_7; ja-jp)
AppleWebKit/525.28.3 (KHTML, like Gecko) Sample"
それでは setApplicationNameForUserAgent: を使い Safariを偽装してみよう。
コード:
[_web_view setApplicationNameForUserAgent:@"Version/3.2.3 Safari/525.28.3"];
サーバ側確認:
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10_5_7; ja-jp)
AppleWebKit/525.28.3 (KHTML, like Gecko) Version/3.2.3 Safari/525.28.3"
Gmail へアクセスしてみる。

Safariと同じ画面が出た。Gmailでは「標準HTML画面」と呼ばれているもののようだ。
画面読み込み中に右下に下記のメッセージが表示される。

試しに Safariで「簡易HTML形式」を選んだところ、簡易ブラウザで見たのと同じ画面が表示された。

Gmailでは、UserAgentからブラウザ情報を得てそれを元に HTML5/CSS 3 の対応状況を判断し、画面の表示を変えているようだ。
なお簡易ブラウザでは Gmailの表示が完了した数秒後にクラッシュしてしまった。以下、ログ
Debugger() was called!
The Debugger has exited due to signal 2 (SIGINT).The Debugger has exited due to signal 2 (SIGINT).
これはなんだ?
2009年8月6日木曜日
WPSU(10) - キャプチャ画像にスクロールバーを含めない
- [WebFrameView setAllowsScrolling:] での制御はどうやってもうまく行かなかった。
仕方が無いので、スクロールバーが出ないようにビューの大きさを調整してみる。色々試行錯誤したところ 幅10px, 縦15px 大きくするとスクロールバーが出なくなった。
rect.size.width += 10.0;
rect.size.height += 15.0;
その結果、横はちょうど良い大きさだが、縦は若干(数px)余白ができるようになった。
今回の結果:

調整しない場合の結果:

スクロールバーの幅や高さによってこの値が決まると思われるので、スクロールバーの大きさが変わるとこの数値は変わる可能性がある。
が、機能実現にはこれで十分なので安易だがこれでいこう。
(一番良いのはスクロールバーの制御だが、これは地道に調べていく)。
2009年8月5日水曜日
WPSU(9) - WebDocumentViewにスクロールバーを表示しない
キャプチャした画像をよく見るとスクロールバーがついている。
これをなくすのに -[WebFrameView setAllowsScrolling:]を使う。
WebFrameView Class Reference
setAllowsScrolling:
するとキャプチャ画像からスクロールバーが消えた。
ただ何故かもとに戻らない。一旦スクロールバーを消すとその後に表示設定(YES)を行っても、スクロールバーが2度と表示されなくなってしまう。
むう。どうすればいいのか。
2009年8月3日月曜日
WPSU(8) - Webページのスクリーンショットを取る
Webページのスクリーンショットを取る。
- [WebController capturePage:] を用意し、InterfaceBuilderでキャプチャボタンのターゲットに指定する。5月ぐらいに検証したときのコードをそのまま貼付けて使った。
WebController.m
- (IBAction)capturePage:(id)sender
{
// (0) ready for capture
[_web_window orderOut:nil];
// (1) filename
NSArray *paths = NSSearchPathForDirectoriesInDomains(
NSDesktopDirectory, NSUserDomainMask, YES);
NSString* path = [NSString stringWithFormat:@"%@/test.png",
[paths objectAtIndex:0], nil];
// (2) size
NSRect rect = [[[[_web_view mainFrame] frameView] documentView] bounds];
[_web_window setContentSize:rect.size];
// (3) capture
[[[[_web_view mainFrame] frameView] documentView] lockFocus];
NSBitmapImageRep* bitmap_rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect];
[[[[_web_view mainFrame] frameView] documentView] unlockFocus];
NSData* data = [bitmap_rep representationUsingType:NSPNGFileType
properties:[NSDictionary dictionary]];
[data writeToFile:path atomically:YES];
// (4) restore window
[_web_window setFrame:[self webWindowFrame] display:YES];
[_web_window makeKeyAndOrderFront:nil];
}
動かしてみる。ページが表示されたら右上のボタンを押す。

するとデスクトップに test.png が作成される。

開くと該当ページのスクリーンショット画像が入っているのがわかる。

ソース:WebPageScreenshotUtility-02.zip
2009年8月2日日曜日
WPSU(7) - 入力されたURLページを表示する
テキストフィールドへ入力された URL を使い Webページを表示させる。表示のためのボタンを別途用意するのではなく、テキストフィールドで returnキーが押されたらページを表示するようにしよう。
returnキーを処理するためにまず NSTextFieldのサブクラスを用意する。
URLTextField.h
@class WebController;
@interface URLTextField : NSTextField {
IBOutlet WebController* _web_controller;
}
@end
InterfaceBuilder で既に置いてあった NSTextFieldのクラスを URLTextFieldへ置き換える。またアウトレットに WebControllerを接続しておく。

URL入力後に returnキーが押されると- [NSTextField textDidEndEditing:]が呼ばれるので、ここでページ読み込みのメソッドを呼出す。
URLTextField.m
@implementation URLTextField
- (void)textDidEndEditing:(NSNotification *)notification
{
[_web_controller loadPageWithURLString:[self stringValue]];
}@end
ページ呼出しはこんな感じ。まだエラー処理はない。
WebController.m
- (void)loadPageWithURLString:(NSString*)url_string
{
[[_web_view mainFrame] loadRequest:
[NSURLRequest requestWithURL:[NSURL URLWithString:url_string]]];
}
実行しよう。テキストフィールドへ Googleのページを入力し returnキーを押してみる。

出た。
2009年8月1日土曜日
WPSU(6) - ウィンドウを2つ重ねる(解説)
前回のコードの解説。
まず WebViewを載せる前面のウィンドウのクラスを定義する。WebWindowと名付けた。
WebWindow.h
@interface WebWindow : NSWindow {
}
@end実装は初期化コードおよび key window になるための -[canBecomeKeyWindow] の実装。
WebWindow.m
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithContentRect:frame
styleMask:NSUtilityWindowMask
backing:NSBackingStoreBuffered
defer:NO];
if (self) {
[self setDisplaysWhenScreenProfileChanges:YES];
[self setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces];
}
return self;
}
- (BOOL)canBecomeKeyWindow
{
return YES;
}
Interface Builder ではベースとなる後ろのウィンドウだけを定義しておく。ここに戻るや進むのボタン、URLテキストフィールドなどを配置しておく。また WebWindowを配置するエリアを表すためにカスタムビューを配置しておく。あらかじめ Interface Builder で位置や大きさを設定しておくと、実行時に WebWindowの大きさや位置を簡単に決める事ができる。

WebViewは前面の WebWindowに載せるのでここには追加しない。ただアウトレットやアクションを Interface Builderで設定できると便利なのでインスタンス化だけしておく。

これらを使って必要な配線を済ませておく。

2つのウィンドウと WebView を統合する役割を担うのが WebController。ここで行う処理は:
・2つのウィンドウの結びつけ
・WebWindowへ WebViewを貼付ける
・NSProgressIndicatorの処理
NSProgressIndicatorは以前説明したので省略。
まずヘッダ。必要なインスタンス変数が定義されている。ほとんどがアウトレットで先ほどの InterfaceBuilderによる配線で準備される。
WebController.h
@class WebWindow;
@interface WebController : NSObject {
IBOutlet NSProgressIndicator* _progress_indicator;
IBOutlet WebView* _web_view;
IBOutlet NSWindow* _main_window;
IBOutlet NSView* _background_view;
WebWindow* _web_window;
}
@end
次に実装。Nib読み込み後に初期化する。
WebController.m
- (void)awakeFromNib
{
:
_web_window = [[WebWindow alloc] initWithFrame:[self webWindowFrame]];
[_web_window makeKeyAndOrderFront:nil];
[_web_window setContentView:_web_view];
[_main_window addChildWindow:_web_window ordered:NSWindowAbove];
:
}
まず WebWindowを生成する。続いて WebViewをコンテンツビューとして張りつける。
そして最後に -[NSWindow addChildWindow:ordered:] を使って2つのウィンドウを紐づける。紐付けによってベースのウィンドウの移動に合わせて WebWindowも位置関係を保ったまま移動するようになる。
WebWindowの位置と大きさは用意しておいたカスタムビューから取得する。
- (NSRect)webWindowFrame
{
NSRect frame = [_background_view frame];
frame.origin = [_main_window convertBaseToScreen:frame.origin];
return frame;
}
カスタムビューから得られる座標系は、そのビューが置かれているウィンドウ内のものになる。WebWindowはスクリーン座標系を使うので - [NSWindow convertBaseScreen:] で座標変換してやる。
最後にデリゲートを使ってベースのウィンドウのサイズが変わった時に WebWindowの大きさも変わる様にしておく。
// Main Window Delegate
- (void)windowDidResize:(NSNotification *)notification
{
[_web_window setFrame:[self webWindowFrame] display:YES];
}
これで2つのウィンドウがあたかも1つのウィンドウのように振る舞うようになる。

(おわり)
2009年7月31日金曜日
WPSU(5) - ウィンドウを2つ重ねる
以前、WebViewのキャプチャ検証を行った時と同じ方式を採用する。
参考:
webKit検証(26) - Webウィンドウを重ねる(4)
※上記は検証結果。さかのぼっていくと経緯がわかる。
この方式は2つのウィンドウを作り、手前のウィンドウに WebViewを載せておく。
キャプチャ時にはこのウィンドウの表示位置を画面外に移動しサイズを十分に大きくする。その上でキャプチャを行い画像ファイルを生成する。
ソース:WebPageScreenshotUtility-01.zip
実行してみる。
見た目は前回と変わらない。
- - - -
詳しい解説はまた明日。
2009年6月9日火曜日
WebKit検証(36) - キャッシュをクリアする(成功)
(キャッシュクリアの続き)
NSURLCache を調べてみた。
NSURLCache Class Reference
まず以前のサンプルコードに "Dump Cache" と "Clear Cache" ボタンを追加する(右下)。
コードはこう。
AppController.m
- (IBAction)dumpCache:(id)sender
{
NSURLCache* cache = [NSURLCache sharedURLCache];
NSLog(@"currentDiskUsage: %d", [cache currentDiskUsage]);
NSLog(@"diskCapacity: %d", [cache diskCapacity]);
NSLog(@"currentMemoryUsage: %d", [cache currentMemoryUsage]);
NSLog(@"memoryCapacity: %d", [cache memoryCapacity]);
}
- (IBAction)clearCache:(id)sender
{
NSURLCache* cache = [NSURLCache sharedURLCache];
[cache removeAllCachedResponses];
}
実行してAppleのページを開いた後のキャッシュファイルの状態はこう。

"Dump Cache"ボタンを押すと、currentDiskUsage: の値が一致している。メモリは使っていないことがわかる。

次に "Clear Cache"ボタンを押してみる。

おお、サイズが小さくなった。
"Dump Cache" で確認。

しつこく sqlite3 コマンドでテーブルの中身を調べてみる。cfurl_cache_response と cfurl_cache_blob_data は空だ。

うまくいったようだ。
まとめ:
- WebView で開いたページのキャッシュは [NSURLCache removeAllCachedResponses] でクリアできる。
- ファイルは削除されない(SQLiteデータベースのレコードが削除される)
- - - -
長々と引っ張ったがやっとキャッシュをクリアすることができた。ちょっとうれしい。
2009年6月8日月曜日
キャッシュ調査(さらに続く)
引き続き調査。NS系ネットワークライブラリを調べてみたところキャッシュに関する記述が出て来た。
URL Loading System
URL Loading System Overview - Cache Management
抄訳
・キャッシュはディスクとメモリを使う。
・キャッシュはアプリケーションごとに格納される。
・キャッシュは NSURLConnection もしくは NSURLDownload によって使われる
・キャッシュポリシーは NSURLRequest 初期化時に決められる
・NSURLCache クラスを使ってキャッシュサイズや、ディスク上の保存位置を設定できる
・NSCachedURLResponse を使うとキャッシュされたコンテンツへアクセスすることができる
・NSCachedURLResponse は NSURLResponse とコンテンツデータをカプセル化している
・現行は http/https のリクエストがキャッシュされる(httpsリクエストはディスクにキャッシュされることはない)
・NSURLConnection の connection:willCacheResponse: デリゲートメソッドを使って、キャッシュの挙動を制御することができる
Using NSURLConnection - Controlling Response Caching
Understanding Cache Access
-> NSURLRequest でキャッシュポリシー (NSURLRequestCachePolicy) を決める
WebKit、WebCore、ネットワークライブラリの解説はHMDT木下さんの資料が参考になる。
Web Kitプログラミング解説と、 その実例としての シイラプロジェクト紹介 (PDF)
で、NSURLCache に辿り着く。
これはまた明日。
2009年6月7日日曜日
キャッシュ調査(続く)
HKさんより情報をもらう。
Pulling Content Out Of OS X Cache.db Files
WebCoreを調べるという助言ももらい引き続き調査。
調べるうちに WebCoreとは別に NSURLCache も浮上して来た。
Problem with WebKit and subclasses of NSURLCache in Leopard
Cocoaはやっぱり! / インターネットにアクセスしよう / Web Kit : DRAFT
iPhoneやiPhoneシミュレータ上でNSURLCacheクラスを使う
[webkit-dev] WebKit caching
NSURLCache Class Reference
- - - -
今日は時間切れ。
明日以降、NSURLCache を少し調べてみよう。
2009年6月6日土曜日
引き続きキャッシュ調査 / Cache.db
Cache.db は CFNetowrk が作成している、という情報を得るがよくわからない。
[webkit-dev] webkit cache
Re: Leopard and Cache.db files
CFNetwork にそれらしいメソッドも見つからないのだが。。
CFNetwork Reference Collection
CFNetwork Programming Guide
2009年6月5日金曜日
WebKit検証(35) - キャッシュをクリアする(方法みつからず)
WebKitが作成したキャッシュの確認ができたが、これを消す方法がわからない。
メソッドが見つからず、ネット上にも情報が見つからない。
もしかして自分でキャッシュファイル Cache.dbを削除するのだろうか。
キャッシュファイルの位置が確実に決められれば、それもありかもしれない。
(現在は ~/Library/Cache 配下にてきる)
うーむ。
今日は進展なし。
2009年5月28日木曜日
WebKit検証(34) - キャッシュ設定を変更(その2)
ネットで調べていると WebKit のソースコードらしきものが見つかった。
WebPreferences.m
この中にこんなコードがあった。
static WebCacheModel cacheModelForMainBundle(void)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// Apps that probably need the small setting
static const char* const documentViewerIDs[] = {
"Microsoft/com.microsoft.Messenger",
"com.adiumX.adiumX",
"com.alientechnology.Proteus",
"com.apple.Dashcode",
"com.apple.iChat",
"com.barebones.bbedit",
"com.barebones.textwrangler",
"com.barebones.yojimbo",
"com.equinux.iSale4",
"com.growl.growlframework",
"com.intrarts.PandoraMan",
"com.karelia.Sandvox",
"com.macromates.textmate",
"com.realmacsoftware.rapidweaverpro",
"com.red-sweater.marsedit",
"com.yahoo.messenger3",
"de.codingmonkeys.SubEthaEdit",
"fi.karppinen.Pyro",
"info.colloquy",
"kungfoo.tv.ecto",
};
// Apps that probably need the medium setting
static const char* const documentBrowserIDs[] = {
"com.apple.Dictionary",
"com.apple.Xcode",
"com.apple.dashboard.client",
"com.apple.helpviewer",
"com.culturedcode.xyle",
"com.macrabbit.CSSEdit",
"com.panic.Coda",
"com.ranchero.NetNewsWire",
"com.thinkmac.NewsLife",
"org.xlife.NewsFire",
"uk.co.opencommunity.vienna2",
};
// Apps that probably need the large setting
static const char* const primaryWebBrowserIDs[] = {
"com.app4mac.KidsBrowser"
"com.app4mac.wKiosk",
"com.freeverse.bumpercar",
"com.omnigroup.OmniWeb5",
"com.sunrisebrowser.Sunrise",
"net.hmdt-web.Shiira",
};
WebCacheModel cacheModel;
const char* bundleID = [[[NSBundle mainBundle] bundleIdentifier] UTF8String];
if (contains(documentViewerIDs, sizeof(documentViewerIDs) / sizeof(documentViewerIDs[0]), bundleID))
cacheModel = WebCacheModelDocumentViewer;
else if (contains(documentBrowserIDs, sizeof(documentBrowserIDs) / sizeof(documentBrowserIDs[0]), bundleID))
cacheModel = WebCacheModelDocumentBrowser;
else if (contains(primaryWebBrowserIDs, sizeof(primaryWebBrowserIDs) / sizeof(primaryWebBrowserIDs[0]), bundleID))
cacheModel = WebCacheModelPrimaryWebBrowser;
else {
bool isLegacyApp = !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITH_CACHE_MODEL_API);
if (isLegacyApp)
cacheModel = WebCacheModelDocumentBrowser; // To avoid regressions in apps that depended on old WebKit's large cache.
else
cacheModel = WebCacheModelDocumentViewer; // To save memory.
}
[pool drain];
return cacheModel;
}
キャッシュモデルを自動判別しているようだが、どうもアプリの種類によって決めているようだ。
シイラがあった。
"net.hmdt-web.Shiira",
進展無し...







