Chromium Code Reading: Experimentalな機能の実装(WebPのAcceptヘッダ送出)
はじめに
Google+を見ていたら、面白そうなポストがありました。
https://plus.google.com/100132233764003563318/posts/KRU6nxp7LXG
Canaryビルドに新しいExperimentalな機能が追加されました。簡単に言うと、画像のリクエスト時にHTTPリクエストのAcceptヘッダにWebP画像をサポートしていることをWebサーバに伝える仕組みです。
今回Canaryに追加されたこの機能は、差分量も小さく、Experimentalな機能の追加方法を学ぶのに最適と思いました。
以下が対象のコードです。
- Issue 14273007: Add a switch to Chromium to enable/disable 'image/webp' accept header.
https://codereview.chromium.org/14273007/
- Issue 13814024: Add a runtime flag in WebRuntimeFeatures to enable 'image/webp' accept header
chrome://flags
ChromeのURLバーから chrome://flags にアクセスすると、Experimentalな機能のON/OFFができます(以下、スイッチと呼びます)。
最新のChromiumでは以下のような画面が表示されます。
スイッチの定義
chrome://flags で表示されるスイッチの説明文はgenerated_resources.grdに定義されています。
src/chrome/app/generated_resources.grd
<message name="IDS_FLAGS_ENABLE_WEBP_IN_ACCEPT_HEADER_NAME" desc="Title for the flag to enable 'image/webp' in accept header."> Enable 'image/webp' accept header </message> <message name="IDS_FLAGS_ENABLE_WEBP_IN_ACCEPT_HEADER_DESCRIPTION" desc="Description for the flag to enable 'image/webp' in accept header."> Enables 'image/webp' accept header in HTTP requests for images, to denote WebP image support. </message>
スイッチの特徴がkExperiments定数内に定義されます。
スイッチの説明文、対応OS、スイッチの種類(SINGLE_VALUEはON/OFFのみのスイッチ)など。
const Experiment kExperiments[] = { [...] { "enable-webp-in-accept-header", IDS_FLAGS_ENABLE_WEBP_IN_ACCEPT_HEADER_NAME, IDS_FLAGS_ENABLE_WEBP_IN_ACCEPT_HEADER_DESCRIPTION, kOsAll, SINGLE_VALUE_TYPE(switches::kEnableWebPInAcceptHeader) }, };
初期化
スイッチのデフォルト値はfalseになっていました。
bool RuntimeEnabledFeatures::isWebPInAcceptHeaderEnabled = false;
Blinkの初期化時に、chrome://flagsで設定されたスイッチの状態取得とランタイム機能郡(WebRuntimeFeatures)にスイッチ状態をセットします。
void RenderThreadImpl::EnsureWebKitInitialized() { [...] WebRuntimeFeatures::enableWebPInAcceptHeader( command_line.HasSwitch(switches::kEnableWebPInAcceptHeader)); [...] }
WebRuntimeFeaturesからRuntimeEnabledFeaturesに委譲されています。
void WebRuntimeFeatures::enableWebPInAcceptHeader(bool enable) { RuntimeEnabledFeatures::setWebPInAcceptHeaderEnabled(enable); }
class RuntimeEnabledFeatures { [...] static void setWebPInAcceptHeaderEnabled(bool isEnabled) { isWebPInAcceptHeaderEnabled = isEnabled; } static bool webPInAcceptHeaderEnabled() { return isWebPInAcceptHeaderEnabled; }
スイッチの利用
ここからは、Experimentalな機能の実装に依存するコードです。
void CachedImage::setCustomAcceptHeader() { if (RuntimeEnabledFeatures::webPInAcceptHeaderEnabled()) setAccept("image/webp,*/*;q=0.8"); }
余談: バックトレース
バックトレースの取得にcontent shellを使います。content shellとはレンダリングエンジン(Blink)の動作確認に使える軽量ブラウザです。
こんな画面です。質素です。
CachedImage::setCustomAcceptHeaderにブレークポイントを設定して、バックトレースを取ってみます。
と、その前に、content_shellプロジェクトのプロパティからコマンドライン引数に「--single-process」オプションを設定しておきます。
これをしておかないとうまくバックトレースが取れません。
バックトレースをざっくり読み解くと、HTMLのパース(最近HTMLのパースはBackgroundParserでスレッド化された!)、CSSのスタイル解決、画像のロード、キャッシュイメージの生成、を経たことがわかります。
誰が画像を要求したのか、解明してみます。
スタックとレースからStyleResolver::loadPendingImagesを見てみると、CSSのbackground-imageのURLに指定された画像を取得しようとしているようです。
どんな画像?
StyleResolver::loadPendingImageのローカル変数imageValueのm_urlをデバッガで見ると、http://www.google.co.jp/images/srpr/logo4w.png でした。
content shellを起動すると、http://www.google.co.jp/に行くので、Googleトップページのロゴ画像と言うわけです。
> webkit.dll!WebCore::CachedImage::setCustomAcceptHeader() 行 301 C++ webkit.dll!WebCore::CachedImage::CachedImage(const WebCore::ResourceRequest & resourceRequest) 行 62 C++ webkit.dll!WebCore::createResource(WebCore::CachedResource::Type type, WebCore::ResourceRequest & request, const WTF::String & charset) 行 73 + 0x29 バイト C++ webkit.dll!WebCore::CachedResourceLoader::loadResource(WebCore::CachedResource::Type type, WebCore::CachedResourceRequest & request, const WTF::String & charset) 行 478 + 0x16 バイト C++ webkit.dll!WebCore::CachedResourceLoader::requestResource(WebCore::CachedResource::Type type, WebCore::CachedResourceRequest & request) 行 405 + 0x20 バイト C++ webkit.dll!WebCore::CachedResourceLoader::requestImage(WebCore::CachedResourceRequest & request) 行 155 + 0x12 バイト C++ webkit.dll!WebCore::CSSImageValue::cachedImage(WebCore::CachedResourceLoader * loader) 行 79 + 0x16 バイト C++ webkit.dll!WebCore::StyleResolver::loadPendingImage(WebCore::StylePendingImage * pendingImage) 行 4248 + 0xc バイト C++ webkit.dll!WebCore::StyleResolver::loadPendingImages() 行 4285 + 0x1b バイト C++ webkit.dll!WebCore::StyleResolver::loadPendingResources() 行 4360 C++ webkit.dll!WebCore::StyleResolver::applyMatchedProperties(const WebCore::StyleResolver::MatchResult & matchResult, const WebCore::Element * element) 行 1915 C++ webkit.dll!WebCore::StyleResolver::styleForElement(WebCore::Element * element, WebCore::RenderStyle * defaultParent, WebCore::StyleSharingBehavior sharingBehavior, WebCore::RuleMatchingBehavior matchingBehavior, WebCore::RenderRegion * regionForStyling) 行 1010 C++ webkit.dll!WebCore::Element::styleForRenderer() 行 1372 + 0x26 バイト C++ webkit.dll!WebCore::NodeRenderingContext::createRendererForElementIfNeeded() 行 247 + 0xc バイト C++ webkit.dll!WebCore::Element::createRendererIfNeeded() 行 1263 + 0x24 バイト C++ webkit.dll!WebCore::Element::attach() 行 1274 C++ webkit.dll!WebCore::executeTask(WebCore::HTMLConstructionSiteTask & task) 行 100 + 0x20 バイト C++ webkit.dll!WebCore::HTMLConstructionSite::executeQueuedTasks() 行 143 + 0x12 バイト C++ webkit.dll!WebCore::HTMLTreeBuilder::constructTree(WebCore::AtomicHTMLToken * token) 行 379 C++ webkit.dll!WebCore::HTMLDocumentParser::constructTreeFromCompactHTMLToken(const WebCore::CompactHTMLToken & compactToken) 行 617 C++ webkit.dll!WebCore::HTMLDocumentParser::processParsedChunkFromBackgroundParser(WTF::PassOwnPtr<WebCore::HTMLDocumentParser::ParsedChunk> popChunk) 行 428 C++ webkit.dll!WebCore::HTMLDocumentParser::pumpPendingSpeculations() 行 485 C++ webkit.dll!WebCore::HTMLDocumentParser::didReceiveParsedChunkFromBackgroundParser(WTF::PassOwnPtr<WebCore::HTMLDocumentParser::ParsedChunk> chunk) 行 334 C++ webkit.dll!WTF::FunctionWrapper<void (__thiscall WebCore::HTMLDocumentParser::*)(WTF::PassOwnPtr<WebCore::HTMLDocumentParser::ParsedChunk>)>::operator()(const WTF::WeakPtr<WebCore::HTMLDocumentParser> & c, WTF::PassOwnPtr<WebCore::HTMLDocumentParser::ParsedChunk> p1) 行 254 + 0x24 バイト C++ webkit.dll!WTF::BoundFunctionImpl<WTF::FunctionWrapper<void (__thiscall WebCore::HTMLDocumentParser::*)(WTF::PassOwnPtr<WebCore::HTMLDocumentParser::ParsedChunk>)>,void __cdecl(WTF::WeakPtr<WebCore::HTMLDocumentParser>,WTF::PassOwnPtr<WebCore::HTMLDocumentParser::ParsedChunk>)>::operator()() 行 523 C++ webkit.dll!WTF::Function<void __cdecl(void)>::operator()() 行 704 + 0x1a バイト C++ webkit.dll!WTF::callFunctionObject(void * context) 行 62 C++ glue.dll!base::internal::RunnableAdapter<void (__cdecl*)(void *)>::Run(void * const & a1) 行 171 + 0x18 バイト C++ glue.dll!base::internal::InvokeHelper<0,void,base::internal::RunnableAdapter<void (__cdecl*)(void *)>,void __cdecl(void * const &)>::MakeItSo(base::internal::RunnableAdapter<void (__cdecl*)(void *)> runnable, void * const & a1) 行 872 C++ glue.dll!base::internal::Invoker<1,base::internal::BindState<base::internal::RunnableAdapter<void (__cdecl*)(void *)>,void __cdecl(void *),void __cdecl(void *)>,void __cdecl(void *)>::Run(base::internal::BindStateBase * base) 行 1173 + 0x19 バイト C++ base.dll!base::Callback<void __cdecl(void)>::Run() 行 396 + 0xe バイト C++ base.dll!base::MessageLoop::RunTask(const base::PendingTask & pending_task) 行 476 C++ base.dll!base::MessageLoop::DeferOrRunPendingTask(const base::PendingTask & pending_task) 行 489 C++ base.dll!base::MessageLoop::DoWork() 行 669 + 0xc バイト C++ base.dll!base::MessagePumpForUI::DoRunLoop() 行 241 + 0x1d バイト C++ base.dll!base::MessagePumpWin::RunWithDispatcher(base::MessagePump::Delegate * delegate, base::MessagePumpDispatcher * dispatcher) 行 64 + 0xf バイト C++ base.dll!base::MessagePumpWin::Run(base::MessagePump::Delegate * delegate) 行 48 + 0x1c バイト C++ base.dll!base::MessageLoop::RunInternal() 行 431 + 0x29 バイト C++ base.dll!base::MessageLoop::RunHandler() 行 405 C++ base.dll!base::RunLoop::Run() 行 46 C++ base.dll!base::MessageLoop::Run() 行 312 C++ base.dll!base::Thread::Run(base::MessageLoop * message_loop) 行 153 C++ base.dll!base::Thread::ThreadMain() 行 197 + 0x16 バイト C++ base.dll!base::`anonymous namespace'::ThreadFunc(void * params) 行 57 + 0xe バイト C++ kernel32.dll!751c33aa()
void StyleResolver::loadPendingImages() { if (m_state.pendingImageProperties().isEmpty()) return; PendingImagePropertyMap::const_iterator::Keys end = m_state.pendingImageProperties().end().keys(); for (PendingImagePropertyMap::const_iterator::Keys it = m_state.pendingImageProperties().begin().keys(); it != end; ++it) { CSSPropertyID currentProperty = *it; switch (currentProperty) { case CSSPropertyBackgroundImage: { for (FillLayer* backgroundLayer = m_state.style()->accessBackgroundLayers(); backgroundLayer; backgroundLayer = backgroundLayer->next()) { if (backgroundLayer->image() && backgroundLayer->image()->isPendingImage()) backgroundLayer->setImage(loadPendingImage(static_cast<StylePendingImage*>(backgroundLayer->image()))); } break; }
PassRefPtr<StyleImage> StyleResolver::loadPendingImage(StylePendingImage* pendingImage) { CachedResourceLoader* cachedResourceLoader = m_state.document()->cachedResourceLoader(); if (pendingImage->cssImageValue()) { CSSImageValue* imageValue = pendingImage->cssImageValue(); return imageValue->cachedImage(cachedResourceLoader); }