Chromiumを手探った#3 - URLの受け渡しを解明しよう
#2はこちら。
ファイルの中でURLが受け渡される流れを追います。
printfデバッグ
前回url_constant.ccというファイル内で定義されたkChromeUINewTabURLという変数が発見されましたが、const char型であり外部から書き換えるのが困難でした。
そのため、一先ず設定画面(chrome://settings)から、デザイン「ホームボタンを表示する」にチェックを入れたときに表示されるホームページ変更画面
に存在するURL入力ボックスを用いてURLを取得し、それをkChromeUINewTabURLの代わりに用いるという方針を立てました。
ここからはこのボックス内に入力されたURL(文字列)がどのように受け渡され、実際にホームページを開く際にどのように活用されているのかを確認していきます。
ファイル間でのデータの受け渡しを追うのはソースコードとにらめっこしていても難しいため、ここで所謂"printfデバッグ"を活用します。
数々の.ccファイルや.jsファイルの中身を(grepなどを用いて)確認する中で、.ccファイル内での関数RegisterMessageCallback()とCallJavascriptFunctionUnsafe()を発見しました。前者はどうやら.jsファイル内のchrome.send()関数で送られた関数の中身を受け取る関数であり、後者は.jsファイル内の関数を呼び出す関数のようです。
これらによって実際に送られてきたデータを調べるために、.ccファイルでは
LOG(INFO)<<hoge;
と気になる変数hogeの直後に入力すれば、その部分が実行されたときに標準出力にhogeの中身が出力されることを利用して、その変数が本当にURLかどうか、また今回確認したい動きに関係のある部分かどうかを判断することができます。また、.jsファイルでは
alert(hoge);
でアラートダイアログを発生させ、同様の確認を行うことができます。
ホームページURLの流れ
先ほどのホームページ変更画面のURLと、"homepage(home_page)"でのgrepの結果、.../src/chrome/browser/resources/options/home_page_overlay.js(html)なるファイルが発見されました。
ここから入力されたURLを前述のprintfデバッグも活用しながら追跡すると、次のようになりました。
- .../src/chrome/browser/resources/options/home_page_overlay.js
... /** * Sets the 'show home button' and 'home page is new tab page' preferences. * (The home page url preference is set automatically by the SettingsDialog * code.) */ handleConfirm: function() { // Strip whitespace. var urlField = $('homepage-url-field'); var homePageValue = urlField.value.replace(/\s*/g, ''); urlField.value = homePageValue; // Don't save an empty URL for the home page. If the user left the field // empty, switch to the New Tab page. if (!homePageValue) $('homepage-use-ntp').checked = true; SettingsDialog.prototype.handleConfirm.call(this); }, ...
home_page_overlay.htmlで入力されたURLはhomepage-url-fieldに格納され、home_page_overlay.jsのこの部分でpref="homepage",type="url",valueがURLのurlFieldなる変数としてSettingsDialog.prototype.handleConfirm.call(this);によって同ディレクトリ内のsettings_dialog.jsに送られます。
- .../src/chrome/browser/resources/options/settings_dialog.js
... SettingsDialog.prototype = { __proto__: Page.prototype, /** @override */ initializePage: function() { this.okButton.onclick = this.handleConfirm.bind(this); this.cancelButton.onclick = this.handleCancel.bind(this); }, /** * Handles the confirm button by saving the dialog preferences. */ handleConfirm: function() { PageManager.closeOverlay(); var prefs = Preferences.getInstance(); var els = this.pageDiv.querySelectorAll('[dialog-pref]'); for (var i = 0; i < els.length; i++) { if (els[i].pref) prefs.commitPref(els[i].pref, els[i].metric); } }, ...
ここからprefs.commitPref(els[i].pref, els[i].metric);によって同ディレクトリ内のpreferences.jsに送られています。
- .../src/chrome/browser/resources/options/preferences.js
... /** * Sets a string preference that represents a URL and signals its new value. * The value will be fixed to be a valid URL when it gets committed to Chrome. * @param {string} name Preference name. * @param {string} value New preference value. * @param {boolean} commit Whether to commit the change to Chrome. * @param {string} metric User metrics identifier. */ Preferences.setURLPref = function(name, value, commit, metric) { if (!commit) { Preferences.getInstance().setPrefNoCommit_(name, 'url', String(value)); return; } var argumentList = [name, String(value)]; if (metric != undefined) argumentList.push(metric); chrome.send('setURLPref', argumentList); }; ...
ここでchrome.sendが行われ、URLが.jsファイルから.ccファイルに受け渡されます。
URLは.../src/chrome/browser/ui/webui/options/core_options_handler.ccによって受け取られています。
- .../src/chrome/browser/ui/webui/options/core_options_handler.cc
... void CoreOptionsHandler::RegisterMessages() { registrar_.Init(Profile::FromWebUI(web_ui())->GetPrefs()); local_state_registrar_.Init(g_browser_process->local_state()); web_ui()->RegisterMessageCallback("coreOptionsInitialize", base::Bind(&CoreOptionsHandler::HandleInitialize, base::Unretained(this))); ... web_ui()->RegisterMessageCallback("setURLPref", base::Bind(&CoreOptionsHandler::HandleSetURLPref, base::Unretained(this))); } ... void CoreOptionsHandler::HandleSetURLPref(const base::ListValue* args) { HandleSetPref(args, TYPE_URL); } ... void CoreOptionsHandler::HandleSetPref(const base::ListValue* args, PrefType type) { DCHECK_GT(static_cast<int>(args->GetSize()), 1); std::string pref_name; if (!args->GetString(0, &pref_name)) return; const base::Value* value; if (!args->Get(1, &value)) return; std::unique_ptr<base::Value> temp_value; switch (type) { case TYPE_BOOLEAN: if (!value->IsType(base::Value::TYPE_BOOLEAN)) { NOTREACHED(); return; } break; case TYPE_INTEGER: { // In JS all numbers are doubles. double double_value; if (!value->GetAsDouble(&double_value)) { NOTREACHED(); return; } int int_value = static_cast<int>(double_value); temp_value.reset(new base::FundamentalValue(int_value)); value = temp_value.get(); break; } case TYPE_DOUBLE: if (!value->IsType(base::Value::TYPE_DOUBLE)) { NOTREACHED(); return; } break; case TYPE_STRING: if (!value->IsType(base::Value::TYPE_STRING)) { NOTREACHED(); return; } break; case TYPE_URL: { std::string original; if (!value->GetAsString(&original)) { NOTREACHED(); return; } GURL fixed = url_formatter::FixupURL(original, std::string()); temp_value.reset(new base::StringValue(fixed.spec())); value = temp_value.get(); break; } case TYPE_LIST: { // In case we have a List pref we got a JSON string. std::string json_string; if (!value->GetAsString(&json_string)) { NOTREACHED(); return; } temp_value = base::JSONReader::Read(json_string); value = temp_value.get(); if (!value || !value->IsType(base::Value::TYPE_LIST)) { NOTREACHED(); return; } break; } default: NOTREACHED(); } std::string metric; if (args->GetSize() > 2 && !args->GetString(2, &metric)) LOG(WARNING) << "Invalid metric parameter: " << pref_name; SetPref(pref_name, value, metric); } ...
URLはweb_ui()->RegisterMessageCallback("setURLPref",base::Bind(&CoreOptionsHandler::HandleSetURLPref,base::Unretained(this)));で関数HandleSetURLPrefの引数として渡され、
HandleSetURTPref内、HandleSetPref(args, TYPE_URL);で関数HandleSetPrefの引数として渡され、
HandleSetPref内、SetPref(pref_name, value, metric);によって登録されます。(pref_nameはsrc/chrome/common/pref_names.ccにおいて登録されているconst char kHomePage[] = “homepage”;を参照していると思われます。)
ホームページ設定画面で入力したURLがどのようにホームページとして新たに登録されるか、ここまで辿ってようやく判明しましたので、次回これを利用して「新しいタブページを設定画面から変更できるようにする」方法について具体的に見ていくことになります。