はじめに
中々開発者泣かせなWebKitのクッキー管理。色々はまりどころがあり、とうとうAppleもWKWebViewの移行期限を無期限に伸ばしました。と言うわけでソースは公開されているので中がどうなっているか解析していきます。
Androidについては以前にこの記事で解説しています。
WebKitのソースコード
WebKitのプロセス構成
セキュリティ対策や重たいコンテンツを読み込んでもアプリが落ちないように、WebKitはここやここ、ここにある通り、UIProcess, WebProcess, NetworkProcess, StorageProcess(WorkerProcess)と4つのプロセスに分かれています。それぞれのプロセスはIPCでやりとりしています。
Cookieの取得
Cookieの取得は以下のメソッドを通じて行います。
class WKHTTPCookieStore
func getAllCookies(_ completionHandler: @escaping ([HTTPCookie]) -> Void)
UIプロセス側のコールスタック
取得完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。イベントループ(RunLoop)から
WebKit〜IPC受信〜WebKit APIと呼び出しているのが分かります。
#1 0x000000018e6b5250 in WTF::Detail::CallableWrapper<WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) ()
#2 0x000000018ea2b6c4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&)::$_0, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&) ()
#3 0x000000018ea0fa68 in WTF::Detail::CallableWrapper<WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const::$_12, void, WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&>::call(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) ()
#4 0x000000018ea0c028 in WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&)>::operator()(WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&, WTF::HashSet<WTF::String, WTF::DefaultHash<WTF::String>, WTF::HashTraits<WTF::String> > const&) ()
#5 0x000000018ea0c1a0 in WebKit::WebsiteDataStore::getAppBoundDomains(WTF::CompletionHandler<void (WTF::HashSet<WebCore::RegistrableDomain, WTF::DefaultHash<WebCore::RegistrableDomain>, WTF::HashTraits<WebCore::RegistrableDomain> > const&)>&&) const ()
#6 0x000000018ea28ab0 in API::HTTPCookieStore::filterAppBoundCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) ()
#7 0x000000018ea2bfc4 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&>::call(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&) ()
#8 0x000000018ec0e3c8 in Messages::WebCookieManager::GetAllCookies::callReply(IPC::Decoder&, WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)>&&) ()
#9 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) ()
#10 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) ()
#11 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() ()
#12 0x000000018c3ac5e0 in WTF::RunLoop::performWork() ()
#13 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) ()
#14 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#15 0x000000018203d668 in __CFRunLoopDoSource0 ()
#16 0x000000018203c960 in __CFRunLoopDoSources0 ()
#17 0x0000000182036a8c in __CFRunLoopRun ()
#18 0x000000018203621c in CFRunLoopRunSpecific ()
#19 0x0000000199c02784 in GSEventRunModal ()
#20 0x0000000184a76ee8 in -[UIApplication _run] ()
#21 0x0000000184a7c75c in UIApplicationMain ()
#22 0x0000000104b308a0 in main at /app/main.m:22
#23 0x0000000181cf66b0 in start ()
Webプロセス側の解析
前述のコールスタックにある
WTF::Detail::CallableWrapper<API::HTTPCookieStore::cookies(WTF::CompletionHandler<void (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&)::$_2, void, WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul,
より、APIHTTPCookieStore.mを見ると、
auto* cookieManager = pool->supplement<WebKit::WebCookieManagerProxy>();
cookieManager->getAllCookies(m_owningDataStore->sessionID(), [this, protectedThis = makeRef(*this), pool = WTFMove(pool), completionHandler = WTFMove(completionHandler)] (const Vector<WebCore::Cookie>& cookies) mutable {
filterAppBoundCookies(cookies, WTFMove(completionHandler));
});
WebCookieManagerProxyと言う記載があります。コールスタックにもありますね。〜Proxyと言うのはProxyパターンの事を指します。実態クラスがあるはずなんで、Proxyを外して
WebCookieManagerで検索すると、WebCookieManager.h、WebCookieManager.cppと言うファイルがでてきました。ここからはNetworkProcess側の解析になります。
コールスタックをさらに解析するとgetAllCookiesと言う記載があります。多分↓のメソッドを呼び出しているのでしょう。
void WebCookieManager::getAllCookies(PAL::SessionID sessionID, CompletionHandler<void(Vector<WebCore::Cookie>&&)>&& completionHandler)
{
Vector<Cookie> cookies;
if (auto* storageSession = m_process.storageSession(sessionID))
cookies = storageSession->getAllCookies();
completionHandler(WTFMove(cookies));
}
storageSession->getAllCookies()でクッキを取り出しています。と言うわけでm_process、storageSessionを見てみます。
m_processを調べるとNetworkProcessクラスのインスタンス変数です。NetworkProcessクラスのstorageSessionメソッドを見てみます。
WebCore::NetworkStorageSession* storageSession(const PAL::SessionID&) const;
NetworkStorageSessionと言うクラスを返すようになっています。クラス名、NetworkStorageSessionで定義しているメソッドを見るとCookieやキャッシュのに永続化に関するいろんな仕事をやるようです。
後、NetworkStorageSessionCocoa.mmと言うファイル名から、プラットホーム依存のクラスらしいです。
で、↑よりgetAllCookiesメソッドを調査すると
Vector<Cookie> NetworkStorageSession::getAllCookies()
{
ASSERT(hasProcessPrivilege(ProcessPrivilege::CanAccessRawCookies));
return nsCookiesToCookieVector(nsCookieStorage().cookies);
}
↑よりさらにnsCookieStorage()を調べると
WEBCORE_EXPORT NSHTTPCookieStorage *nsCookieStorage() const;
と言う宣言がありました。よく知っているNSHTTPCookieStorageクラスです。実装は見当たりませんでした。
まとめ
Cookieの取り出しを見ると、WebProcessにあるNSHTTPCookieStorageからCookieを取り出してUIProcessに返しているだけでした。
Cookieの設定
Cookieの設定は以下のメソッドを通じて行います。
class WKHTTPCookieStore
func setCookie(_ cookie: HTTPCookie,
completionHandler: (() -> Void)? = nil)
UIプロセス側のコールスタック
設定完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c3f8 in WTF::Detail::CallableWrapper<auto API::HTTPCookieStore::setCookies(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WTF::CompletionHandler<void ()>&&)::$_4::operator()<WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> >(WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc>&&)::'lambda'(), void>::call() ()
#2 0x000000018e9821a8 in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::SetCookie, WTF::CompletionHandler<void ()> >(Messages::WebCookieManager::SetCookie&&, WTF::CompletionHandler<void ()>&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) ()
#3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) ()
#4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) ()
#5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() ()
#6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() ()
#7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) ()
#8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#9 0x000000018203d668 in __CFRunLoopDoSource0 ()
#10 0x000000018203c960 in __CFRunLoopDoSources0 ()
#11 0x0000000182036a8c in __CFRunLoopRun ()
#12 0x000000018203621c in CFRunLoopRunSpecific ()
#13 0x0000000199c02784 in GSEventRunModal ()
#14 0x0000000184a76ee8 in -[UIApplication _run] ()
#15 0x0000000184a7c75c in UIApplicationMain ()
#16 0x0000000104b308a0 in main at /app/main.m:22
#17 0x0000000181cf66b0 in start ()
Webプロセス側の解析
取り出しとほぼ同じ機構のはず。
Cookieの削除
Cookieの削除は以下のメソッドを通じて行います。
class WKHTTPCookieStore
func delete(_ cookie: HTTPCookie,
completionHandler: (() -> Void)? = nil)
UIプロセス側のコールスタック
削除完了ハンドラーに復帰した時のUIスレッド(プロセス)のコールスタックは以下になります。
#1 0x000000018ea2c638 in WTF::Detail::CallableWrapper<API::HTTPCookieStore::deleteCookie(WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_6, void>::call() ()
#2 0x000000018e981ebc in WTF::Detail::CallableWrapper<void WebKit::AuxiliaryProcessProxy::sendWithAsyncReply<Messages::WebCookieManager::DeleteCookie, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0>(Messages::WebCookieManager::DeleteCookie&&, WebKit::WebCookieManagerProxy::deleteCookie(PAL::SessionID, WebCore::Cookie const&, WTF::CompletionHandler<void ()>&&)::$_0&&, unsigned long long, WTF::OptionSet<IPC::SendOption>, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::'lambda'(IPC::Decoder*), void, IPC::Decoder*>::call(IPC::Decoder*) ()
#3 0x000000018e92a8f4 in WTF::Detail::CallableWrapper<WebKit::AuxiliaryProcessProxy::sendMessage(std::__1::unique_ptr<IPC::Encoder, std::__1::default_delete<IPC::Encoder> >, WTF::OptionSet<IPC::SendOption>, WTF::Optional<std::__1::pair<WTF::CompletionHandler<void (IPC::Decoder*)>, unsigned long long> >&&, WebKit::AuxiliaryProcessProxy::ShouldStartProcessThrottlerActivity)::$_0, void, IPC::Decoder*>::call(IPC::Decoder*) ()
#4 0x000000018e683dd8 in IPC::Connection::dispatchMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >) ()
#5 0x000000018e6866c8 in WTF::Detail::CallableWrapper<IPC::Connection::enqueueIncomingMessage(std::__1::unique_ptr<IPC::Decoder, std::__1::default_delete<IPC::Decoder> >)::$_7, void>::call() ()
#6 0x000000018c3ac5e0 in WTF::RunLoop::performWork() ()
#7 0x000000018c3ad2c8 in WTF::RunLoop::performWork(void*) ()
#8 0x000000018203d76c in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ ()
#9 0x000000018203d668 in __CFRunLoopDoSource0 ()
#10 0x000000018203c960 in __CFRunLoopDoSources0 ()
#11 0x0000000182036a8c in __CFRunLoopRun ()
#12 0x000000018203621c in CFRunLoopRunSpecific ()
#13 0x0000000199c02784 in GSEventRunModal ()
#14 0x0000000184a76ee8 in -[UIApplication _run] ()
#15 0x0000000184a7c75c in UIApplicationMain ()
#16 0x0000000104b308a0 in main at /app/main.m:22
#17 0x0000000181cf66b0 in start ()
Webプロセス側の解析
取り出しとほぼ同じ機構のはず。
疑問
- ソースを読むとよくでてくるWTFってなんの略? WebKit Transport Framework?
- Cookie管理について。Androidは同期呼び出しで、iOSは非同期呼び出しなのはなんで? どっちもプロセス間通信が走っているけど。
- iOSの非公開APIにアクセスしまくっているけど、これはアップル主導のプロジェクトからなのか。