Visual StudioによるOBSのプラグイン開発手順

はじめに

Visual StuioでOBSのプラグインを開発する準備について説明します。 詳細な手順がネットで見つからなかったので、独自に手順を検討しました。 デバッガで効率的にプラグインデバッグできるようにします。

手順

OBS Studioをビルドする。(以下、ビルドディレクトリは C:\projects\obs-build として説明)

OBS Studioのビルド手順の詳細についてはこちらを参照。

fixme.hatenadiary.org

プラグインのテンプレートをローカルにcloneする。

github.com

注:詳細はまだ研究できていないが、github上で動くCIがこのテンプレートで実装されており、マルチプラットフォーム開発に便利なテンプレートになっている。

プラグインディレクトリでCMakeを実行する。

$ cd [PLUGIN DIR]
$ cmake -S . -B build -Dlibobs_DIR="/c/projects/obs-build/libobs" -Dw32-pthreads_DIR="/c/projects/obs-build/deps/w32-pthreads"

生成された [PLUGIN DIR]/build/obs-plugintemplate.sln をVisual Stuioで開く。

obs-plugintemplate プロジェクト > Properties > Configuration Properties > Debugging にて

  • Commandに C:\projects\obs-build\rundir\Debug\bin\64bit\obs64.exe
  • Working DirectoryにC:\projects\obs-build\rundir\Debug\bin\64bit

を設定する。

また obs-plugintemplate プロジェクト > Properties > Configuration Properties > Build Events > Post-Build Event にて

  • Command Lineに copy C:\projects\<PLUGIN DIR>\build\rundir\Debug\obs-plugins\64bit\* C:\projects\obs-build\rundir\Debug\obs-plugins\64bit

を既存のコマンドの最後に追記し、OBS本体にデプロイされるようにする。

注: ここまで説明したように、手動でプロジェクトに設定変更を加える方法では、CMakeでプロジェクトを生成し直す度に再設定が必要になる。 CMakeのスクリプトに以下のような修正を加えておくことで、手動での設定が不要になる。

github.com

注:別の方法として、プロセスにアタッチする方法もあるが、この方法の欠点は bool obs_module_load(void) の実行段階のデバッグができないこと。OBSにアタッチするタイミングではすでにプラグインのロードが完了してしまっているため。

プロジェクトの設定

obs-plugintemplate プロジェクトをビルドする。

Visual Studioにてソースコードにブレイクポイントを設定する。

Visual StudioにてLocal Windows Debugger を実行する。

するとOBSが起動し、ブレイクポイントで実行が停止する。

ブレイクポイントで停止した様子

OBS Code Reading: ウインドウタイトルのバージョン情報の仕様

はじめに

これは、OBSのウインドウのタイトルがどのように決められているのか突き止めるまでのメモです。

OBSのタイトル部分

今回使用したバージョンはこちらです。

https://github.com/obsproject/obs-studio/tree/e8dc70d0eef4503378d6ac300e680215eb5c9379

結論

開発版のOBSは、ビルド対象のOBSの作業ツリーで

git describe --always --tags --dirty=-modified

を実行した結果がウインドウのタイトルとして使用される。

このコマンドを実行すると、具体的には、このような結果が得られる。

$ git describe --always --tags --dirty=-modified
28.0.0-8-gbfa2053b0-modified

読み方は

  • 作業ディレクトリのツリーは、直近の「28.0.0」というタグから数えて8番目にあたる位置
  • 「bfa2053b0」(先頭の「g」は除く)が直前のコミットのハッシュ値
  • 修正あり

つまり、タイトルを見れば、ビルドされたOBSのバージョンを正確に知ることができる。

調査

OBS 28.0.0がリリースされた時付近に、このバージョン番号変更のコミットがあるのを覚えていました。 github.com

/*
 * Increment if major breaking API changes
 */
#define LIBOBS_API_MAJOR_VER 28

LIBOBS_API_MAJOR_VER の値がタイトルに使われているのでは?という予想の元、LIBOBS_API_MAJOR_VER で検索してみます。 そうすると、以下の関数が見つかりました。コードの内容的に、ここでウインドウタイトルのバージョン情報が作られているのは確実です。

string OBSApp::GetVersionString() const
{
    stringstream ver;

#ifdef HAVE_OBSCONFIG_H
    ver << OBS_VERSION;
#else
    ver << LIBOBS_API_MAJOR_VER << "." << LIBOBS_API_MINOR_VER << "."
        << LIBOBS_API_PATCH_VER;

#endif
    ver << " (";

#ifdef _WIN32
    if (sizeof(void *) == 8)
        ver << "64-bit, ";
    else
        ver << "32-bit, ";

    ver << "windows)";
#elif __APPLE__
    ver << "mac)";
#elif __OpenBSD__
    ver << "openbsd)";
#elif __FreeBSD__
    ver << "freebsd)";
#else /* assume linux for the time being */
    ver << "linux)";
#endif

    return ver.str();
}

Visual Studio上では HAVE_OBSCONFIG_H の分岐が有効になっています。 つまり、開発中は先ほど検索したLIBOBS_API_MAJOR_VERは参照されません。

OBS_VERSION で検索します。 すると、以下のcmake/Modules/VersionConfig.cmakeで値が決定されていることが分かりました。

# Set full and canonical OBS version from current git tag or manual override
if(NOT DEFINED OBS_VERSION_OVERRIDE)
  if(NOT DEFINED RELEASE_CANDIDATE
     AND NOT DEFINED BETA
     AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
    execute_process(
      COMMAND git describe --always --tags --dirty=-modified
      OUTPUT_VARIABLE _OBS_VERSION
      WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
      RESULT_VARIABLE _OBS_VERSION_RESULT
      OUTPUT_STRIP_TRAILING_WHITESPACE)

    if(_OBS_VERSION_RESULT EQUAL 0)
      if(${_OBS_VERSION} MATCHES "rc[0-9]+$")
        set(RELEASE_CANDIDATE ${_OBS_VERSION})
      elseif(${_OBS_VERSION} MATCHES "beta[0-9]+$")
        set(BETA ${_OBS_VERSION})
      else()
        string(REPLACE "-" "." _CANONICAL_SPLIT ${_OBS_VERSION})
        string(REPLACE "." ";" _CANONICAL_SPLIT ${_CANONICAL_SPLIT})
        list(GET _CANONICAL_SPLIT 0 1 2 _OBS_VERSION_CANONICAL)
        string(REPLACE "." ";" _OBS_VERSION ${_OBS_VERSION})
      endif()
    endif()
  endif()

git describe --always --tags --dirty=-modified の値が使われているようです。

実際に実行してみると

$ git describe --always --tags --dirty=-modified
28.0.0-8-gbfa2053b0-modified

のような結果が得られました。ウインドウのタイトルのバージョン情報と一致しています。

このコマンドは、以下のように解釈できます。

  • 作業ディレクトリのツリーは、直近の「28.0.0」というタグから数えて8番目にあたる位置
  • 「bfa2053b0」(先頭の「g」は除く)が直前のコミットのハッシュ値
  • 修正あり

以下の libobs/obsconfig.h.in をテンプレートとして入力し、バージョン情報を置換してobsconfig.hを生成します。

#pragma once

#ifndef ON
#define ON 1
#endif

#ifndef OFF
#define OFF 0
#endif

#define OBS_VERSION "@OBS_VERSION@"
#define OBS_VERSION_CANONICAL "@OBS_VERSION_CANONICAL@"
#define OBS_DATA_PATH "@OBS_DATA_PATH@"
#define OBS_INSTALL_PREFIX "@OBS_INSTALL_PREFIX@"
#define OBS_PLUGIN_DESTINATION "@OBS_PLUGIN_DESTINATION@"
#define OBS_QT_VERSION @_QT_VERSION@
[...]

さらにビルドディレクトリまで検索範囲を広げると、生成されたobsconfig.hが見つかりました。

$ cd obs-build
$ grep -rn OBS_VERSION .
[...]
./config/obsconfig.h:12:#define OBS_VERSION "28.0.0-8-gbfa2053b0-modified"
[...]

これで、ウインドウのタイトルが決められるまでの過程を追うことができました。

Visual Studio Community 2022でOBSをビルドする手順

はじめに

これはVisual Studio Community 2022でOBSをビルドする手順の記録です。 64bitのWindows版のOBSをビルドします。

基本的にこの手順に従います。

Wiki - Build Instructions For Windows | OBS

必要なソフト

手順

コードをクローンする。

% mkdir ~/projects
% cd ~projects/
% git clone --recursive https://github.com/obsproject/obs-studio.git

CMakeでの作業

CMake(GUI)を起動する。

以下を設定する。

  • Where is the source code →C:/projects/obs-studio

  • Where to build the binaries →C:/projects/obs-build

最低限、以下のエントリーを追加する。

  • CMAKE_PREFIX_PATH  例: C:/projects/obs-deps/windows-deps-2022-08-02-x64

  • QTDIR  例: C:/projects/obs-deps/windows-deps-qt6-2022-08-02-x64

Configureを実行する。 コンパイラの指定が求められるので「Visual Studio 17 2022」を選択する。

Generateを実行する。

コマンドでのプロジェクト生成

CMakeの使い方に慣れてきたら、コマンド版のCMakeを利用するとより便利になる。 以下のコマンドで上記と同様のプロジェクトを生成できる。

$ cd /c/projects/obs-studio
$ cmake -S . -B ../obs-build -DCMAKE_PREFIX_PATH="/c/projects/obs-deps/windows-deps-2022-08-02-x64" -DQTDIR="/c/projects/obs-deps/windows-deps-qt6-2022-08-02-x64"

Visual Studioでの作業

生成が成功したら、C:\projects\obs-build\obs-studio.slnを開く

ソリューションをビルドする。

ビルドが成功したら、C:\projects\obs-build\rundir\Debug\bin\64bit以下にobs64.exeが生成される。

任意の場所にブレークポイントをセットすることで、Visual Studioでのデバッグも可能。

注意事項

ビルドに関する問題の対処について記します。

キャッシュ問題

別のブランチに変更した場合、キャッシュが残って、リビルドしても前の状態が残ってしまう場合がある。 対処としては、2パターン考えられる。

  • ソフトリセット:CMakeによるプロジェクト生成を再実行する (git pullで軽微な変更を適用する場合)
  • ハードリセット:ビルドディレクトリ(C:/projects/obs-build)を丸ごと削除し、CMakeによるプロジェクト生成を再実行する。

warning C4819エラーの対処

CP932環境として設定されたWindowsでビルドすると、以下のエラーが出てビルドできない場合がある。

17>C:\projects\obs-studio\plugins\obs-ffmpeg\external/nvEncodeAPI.h(1291,1): error C2220: 次の警告はエラーとして処理されます (ソース ファイルをコンパイルしています C:\projects\obs-studio\plugins\obs-ffmpeg\jim-nvenc-helpers.c)

以下の記事を参考に、システムの設定を変更することで、このエラーを回避できる。

qiita.com

XcodeでOBSをビルドとデバッグするまで

はじめに

今回はXcodeでOBSをビルドし、デバッグできるようにするまでの手順を記録します。 色々うまくいかなかったですが、試行錯誤してデバッグできる状態まで持っていけました。

手順

基本的にここのマニュアルの通りでOK。

Build Instructions For Mac · obsproject/obs-studio Wiki · GitHub

Xcodeを削除してしまっていたので、再インストールした。 AppStoreからだと異様に遅く、進捗が見えないので、ここからXcodeをダウンロードしてインストールした。 https://developer.apple.com/download/all/

Swigがうまく認識しなかったので、-DSWIG_DIRでobs-deps配下のSwigを使用するように指定。

% cmake -S . -B build -G Xcode \
    -DCEF_ROOT_DIR="~/development/obs-build-dependencies/cef_binary_4638_macos_x86_64" \
    -DCMAKE_PREFIX_PATH="~/development/obs-build-dependencies/obs-deps" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
    -DCMAKE_OSX_ARCHITECTURES="x86_64" -DSWIG_DIR="/Users/yoshinori_sano/development/obs-build-dependencies/obs-deps/share/swig/4.1.0"

cmakeでエラー

cmakeを実行すると以下のようにコンパイラが見つからないエラーが発生した。

% cmake -S . -B build -G Xcode \
    -DCEF_ROOT_DIR="~/development/obs-build-dependencies/cef_binary_4638_macos_x86_64" \
    -DCMAKE_PREFIX_PATH="~/development/obs-build-dependencies/obs-deps" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
    -DCMAKE_OSX_ARCHITECTURES="x86_64" -DSWIG_DIR="/Users/yoshinori_sano/development/obs-build-dependencies/obs-deps/share/swig/4.1.0"
-- OBS:  Application Version: 28.0.0-beta1-8-g76e9c64f4 - Build Number: 63
-- The C compiler identification is unknown
-- The CXX compiler identification is unknown
CMake Error at CMakeLists.txt:15 (project):
  No CMAKE_C_COMPILER could be found.



CMake Error at CMakeLists.txt:15 (project):
  No CMAKE_CXX_COMPILER could be found.

エラーログを確認すると、以下のエラーがあった。

% less ~/projects/obs-studio/build/CMakeFiles/CMakeError.log
[...]
xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

調べたら、XcodeのPreferenceの設定をすれば良いことが分かった。

qiita.com

Xcodeを再インストールした時に、状態がおかしくなったのかもしれない。

Preference > Locations > Command Line Toolsが空の状態になっていたので、Xcode 13.4.1を選択。

Command Line Toolsを設定

ビルドと実行

以下のコマンドを実行したら、buildディレクトリ配下に「obs-studio.xcodeproj」が生成されるのでXcodeで開く。

% cmake -S . -B build -G Xcode \
    -DCEF_ROOT_DIR="~/development/obs-build-dependencies/cef_binary_4638_macos_x86_64" \
    -DCMAKE_PREFIX_PATH="~/development/obs-build-dependencies/obs-deps" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
    -DCMAKE_OSX_ARCHITECTURES="x86_64" -DSWIG_DIR="/Users/yoshinori_sano/development/obs-build-dependencies/obs-deps/share/swig/4.1.0"

Schemaをobsに変更してビルドを実行する。しばらくすると、OBSが起動する。

Schemaをobsに変更

OBSのビルドと起動に成功

デバッグできない問題

ブレークポイントを追加しても、無効になってしまう問題が発生。

こんな感じでブレークピントが無効になってしまう

OBSのDiscordで質問してみたものの、よく分からず。

discord.com

デバッグシンボルが付いてないのかなと思ってcmakeからやり直してみることに。

解決方法

Xcodeのプロジェクトファイルを生成する時に、-DCMAKE_BUILD_TYPE=Debug をcmakeのオプションに追加することでデバッグ版のOBSが生成されるようになる。

% cmake -S . -B build -G Xcode \
    -DCEF_ROOT_DIR="~/development/obs-build-dependencies/cef_binary_4638_macos_x86_64" \
    -DCMAKE_PREFIX_PATH="~/development/obs-build-dependencies/obs-deps" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 \
    -DCMAKE_OSX_ARCHITECTURES="x86_64" -DSWIG_DIR="/Users/yoshinori_sano/development/obs-build-dependencies/obs-deps/share/swig/4.1.0" -DCMAKE_BUILD_TYPE=Debug

これでブレークポイントを設定して実行したら、期待通りブレークポイントで実行停止するようになった。

ブレークポイントで実行が止まるようになった

Blink: Shadow DOMのLayoutTestを実行する

はじめに

BlinkでShadow DOMのLayoutTestを実行する手順のメモです。

仕様

今回は以下の仕様のLayoutTestをピンポイントで実行します。
http://www.w3.org/TR/shadow-dom/

Invoking the cloneNode() method on a ShadowRoot instance must always throw a DATA_CLONE_ERR exception.

テストコード

以下のようなテストコードが用意されています。Shadow Rootに対してcloneNode()すると25番のExceptionが投げられるという単純なテストです。25番は仕様のDATA_CLONE_ERRに対応していて、core/dom/ExceptionCode.hで25と定義されています。


LayoutTest/fast/dom/shadow/shadowroot-clonenode.html

<!DOCTYPE>
<html>
<script src="../../js/resources/js-test-pre.js"></script>

<pre id="console"></pre>

<script>
description('Calling ShadowRoot.cloneNode() should throw a DATA_CLONE_ERR exception.');

var host = document.createElement('div');
var shadowRoot = host.webkitCreateShadowRoot();
shouldThrow('shadowRoot.cloneNode()', '"Error: DataCloneError: DOM Exception 25"');
</script>
<script src="../../js/resources/js-test-post.js"></script>
</html>

core/dom/ExceptionCode.h

        // Others introduced in HTML5:
        NETWORK_ERR = 19,
        ABORT_ERR = 20,
        URL_MISMATCH_ERR = 21,
        QUOTA_EXCEEDED_ERR = 22,
        TIMEOUT_ERR = 23,
        INVALID_NODE_TYPE_ERR = 24,
        DATA_CLONE_ERR = 25,

LayoutTestの実行

LayoutTestを実行しています。成功します。

> cd src\webkit\tools\layout_tests
> python.exe run_webkit_tests.py --nocheck-sys-deps --debug fast\dom\shadow\shadowroot-clonenode.html
Using port 'chromium-win-win7'
Test configuration: <win7, x86, debug>
Placing test results in F:\ssdcygwin\home\fixme\chrome\src\webkit\Debug\layout-t
est-results
Baseline search path: chromium-win -> generic
Using Debug build
Pixel tests enabled
Regular timeout: 12000, slow test timeout: 60000
Command line: F:\ssdcygwin\home\fixme\chrome\src\build\Debug\DumpRenderTree.exe
-

Found 0 tests; running 0, skipping 0.
No tests to run.

失敗させてみる

shadowroot-clonenode.htmlを適当にいじって、再度実行すると、失敗しました。

> python.exe run_webkit_tests.py --nocheck-sys-deps --debug fast\dom\shadow\shadowroot-conenode.html
Using port 'chromium-win-win7'
Test configuration: <win7, x86, debug>
Placing test results in F:\ssdcygwin\home\fixme\chrome\src\webkit\Debug\layout-
est-results
Baseline search path: chromium-win -> generic
Using Debug build
Pixel tests enabled
Regular timeout: 12000, slow test timeout: 60000
Command line: F:\ssdcygwin\home\fixme\chrome\src\build\Debug\DumpRenderTree.exe
-

Found 1 test; running 1, skipping 0.
Ruby is not installed; can't generate pretty patches.

Running 1 DumpRenderTree.

[1/1] fast/dom/shadow/shadowroot-clonenode.html failed unexpectedly (text diff)

0 tests ran as expected, 1 didn't:


Regressions: Unexpected text-only failures (1)
  fast/dom/shadow/shadowroot-clonenode.html [ Failure ]

失敗のレポートがブラウザに表示されます。リンクをたどることで、失敗の詳細が見れます。

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

https://codereview.chromium.org/13814024/

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);
    }

Chromeにはじめて取り込まれたパッチ

記念すべき第1号です。
https://chromium.googlesource.com/chromium/src/+/15fc083b4a23e23417a6bf71f38e2f7966908590

単なるtypoの修正ですが、LGTM(Looks Good To Me)をもらった時は嬉しかったです。

Botがビルドとテストを走らせる様子も体験してみると楽しかったです。
https://codereview.chromium.org/14145003/
https://chromium-status.appspot.com/cq/yoshinori.sano%40gmail.com/14145003/6001