コードリーディング

ふとChromeのアドレスバーにURLを入力した時にどんな処理(主にURL文字列の処理について)がされているのか気になったため、Chromiumを読むことにした

  • とりあえず、まずBardに質問
    • ぼく「Chromiumのソースコード内で、Omniboxにテキストが入力された時の処理が書かれている箇所を、ファイルパス及びその周辺のソースコードとともに示してください。なお、GitHub上にあるミラーリポジトリ(https://github.com/chromium/chromium)を参照してください」
      • Chromeのアドレスバー兼検索欄はOmniboxと呼ばれる
    • Bard「Chromium のソースコードで、Omnibox にテキストが入力されたときに処理される場所は次のとおりです。ファイル: chrome/browser/ui/views/location_bar/location_bar.cc、関数: OnInputChanged()
  • 調べてみましたが、location_bar.ccはありませんでした!いかがでしたか?
  • とりあえず似ているlocation_bar_view.ccを見る
    • なんかOmniboxについての言及が多かったので、そっちのコードを読んだほうが良さそう
      • なんでOmniboxにたどり着いたのかは忘れた
  • omnibox_view_views.hに移動
    • 色々見てると HandleAfterKeyEvent() が目に止まった
      • omnibox_view_views.cc:327でEnterキーが押されたときの挙動を規定してそう
        • Altキーが押されていたら新しいタブで開くようにしている(知らなかった)
      • model()->AcceptInput() が呼ばれている
        • model() が返すのは OmniboxEditModel*
  • omnibox_edit_model.cc:495 に移動、OmniboxEditModel::AcceptInput() を読んでいく
    • GetInfoForCurrentText() でなんか AutocompleteMatch 型の変数 match を設定している
      • おそらくOmniboxに入力した時に出てくる候補とかの選択状況とかを確認し、選択中の候補に対応するURLを取得してそう
        • 引数、参照じゃなくてポインタなんですね
        • popup_->IsOpen() かつ query_in_progress() っていかにもそれっぽい
        • これがfalseのときに使われている、AutocompleteClassifierFactory::GetForProfile(profile_)->Classify() は何?
          • Autocomplete~は「Singleton that owns all AutocompleteClassifiers and associates them with Profiles」らしいです(autocomplete_classifier_factory.h:15) 知るか
            • ProfileはChromeの機能で、ケバブメニュー(Omnibarがある行の一番右にある︙)の横、自分のGoogleアカウントのアイコンが表示されているところから管理できるアレ
          • 内部で AutoCompleteResult なるものを取得しており、これは「All matches from all providers for a particular query. This also tracks what the default match should be if the user doesn’t manually select another match」らしい(autocomplete_result.h:19
          • ここでview_->GetText() を呼んでおり、これによってOmniboxViewViewsのtextfield_->text() を取得している
            • textfield_->SetText(text); を呼んでいるのは OmniboxViewViews::SetTextAndSelectedRange() で、これは void OmniboxViewViews::SetWindowTextAndCaretPos() から呼ばれている
              • CaretPosは「Omnibox 内のカーソルの位置を表す」らしい(By Bard)
                • Bardは「構造体」って言ってるけどsize_t型なんだよなあ
            • SetWindowTextAndCaretPos 自体はgtkやcocoa等、実際のフロントエンド実装部分から呼び出されてそう あと OmniboxView::SetUserText()
          • まあどうあれ、ポップアップから自動補完・履歴などの候補を選択していなければ、ユーザーの入力が用いられるという解釈で良いのではないか
    • match.destination_url がvalidじゃなければreturn
      • destination_urlGURL
        • GoogleURL
        • pyautolib.i というファイルで定義されている?
          • Pythonとの連携?pyauto.py 内にそれらしきものはなかった
      • まあURLのバリデーションだろう
      • match がURLを持っていると考えて良いのかな
    • view_->OpenMatch() で多分実際に開く処理を行っている
      • view_OmniboxView で、OpenMatch() 内部で model_->OpenMatch() を呼んでいる
        • で、この model_OmniboxEditModel というオチ OmniboxEditModel から OmniboxViewOpenMatch() を呼んだと思っていたら、いつの間にか OmniboxEditModelOpenMatch() になっていた……
  • omnibox_edit_model.cc:534 に移動、OmniboxEditModel::OpenMatch() を読んでいく
    • とりあえず引数として渡された matchAcceptInput() でつくったものと同一だから、これを使っている箇所だけ見ていこう
      • 627行目あたりがそれっぽい?
        • 拡張機能のURLを指定してたら ExtensionAppProvider::LaunchAppFromOmnibox() という関数を呼んでる
          • 本質じゃないから雑にしか読んでないけど、 この関数の最後で application_launch::OpenApplication() を呼んでいるらしい
        • URLが拡張機能でない、即ち普通のURLであれば、match.destination_url から GURL 型のオブジェクト destination_url を作成して、controller_->OnAutocompleteAccept() に渡す
          • またautocomplete?
          • controller_OmniboxEditController*
            • そういや今までの型においてポインタ無視してたかもしれない まあいっか
          • OnAutocompleteAccept() の定義元をVSCodeに探してもらうと、LocationBarView::OnAutocompleteAccept() が出てきた
            • LocationBarViewOmniboxEditController を継承している
            • LocationBarViewGtk も出てきたけど一旦無視
  • location_bar_view.cc:1093OnAutocompleteAccept() を見る
    • 第1引数が OpenMatch() の最後で作られた destination_url と同じURLだと思うので、これが使われているところを見る
    • といっても、urlがvalidなら location_input_ に代入してるだけ
      • これは string16 型 UTF8ToUTF16(url.spec()) としてから代入している
    • そしてその後、command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); という処理が!
      • command_updater_CommandUpdater*
  • command_updater.cc:44ExecuteCommand()
    • 引数としてもらった int id をそのまま CommandUpdater::ExecuteCommandWithDisposition() に渡してその戻り値をreturnしてるだけ
      • CommandUpdater::ExecuteCommandWithDisposition() も、id に対応するコマンドが使えるかどうかを確認してから、delegate_->ExecuteCommandWithDisposition() を呼んでる
        • delegate_ は `CommandUpdaterDelegate*
        • ExecuteCommandWithDisposition() の定義はいくつかあって、どれもswitch文で id に対応するコマンドを呼び出している
          • 最もそれらしきものは BrowserCommandController のもので、これ以外(simple_web_view_dialog だの panel だの)は IDC_OPEN_CURRENT_URL に対応するコマンドを用意していない
          • BrowserCommandController は、idIDC_OPEN_CURRENT_URL のとき browser_commands.cc@OpenCurrentURL() を呼んでいる
  • browser_commands.cc:353OpenCurrentURL()
    • 引数に Browser* 型の browser を受け取る
    • browser->window()->GetLocationBarlocation_barを取得し、 location_bar->GetInputString() で取得した文字列から GURL 型のオブジェクト url を作成している
      • GetInputString()location_barlocation_input_ を返しているだけ
      • そう、LocationBarView::OnAutocompleteAccept() で代入されていたメンバですね 伏線回収だ~~~
        • これは OmniboxEditModel::OpenMatch() で作られた destination_url と同一で、こっちは OmniboxEditModel::AcceptInput() で作られた match をもとにしていた
          • そういえば結局この match があんまりよく分かってないような気がする……
    • url などをもとに NavigateParams 型のオブジェクトを作り、 Navigate() に渡す
      • 多分これでオープン処理してそう
      • もう読むのやめて良いような気がするな
  • (蛇足?)browser_navigator.cc:390Navigate()
    • 523行目で LoadURLInContents() を呼び出していて、この直前に「Perform the actual navigation, tracking whether it came from the renderer」とのコメントがあった
      • 引数として渡しているもので気になるのは params->target_contents->web_contents()url
        • 後者については、基本的に params->url (つまりアドレスバーの中身だろうな)を代入している
        • ただし、それが空っぽだった場合、プロフィールを参照してホームページのURLに置き換えている
        • 前者について、481行目あたりに params->target_contents = chrome::TabContentsFactory(...) という処理がある
          • 第2引数(content::SiteInstance)に渡すオブジェクトを作るために url を用いている(tab_util::GetSiteInstanceForNewTab(params->browser->profile(), url)
          • このGetSiteなんちゃらをたどると site_instance_impl.cc:241GURL SiteInstanceImpl::GetSiteForURL() にたどり着くことが出来て、ここでurlからoriginを取ってきたりしてるらしいですよ

とりあえずURL入力後の処理はおおよそ掴めたような気がするけど、気になるところも残ってはいる

  • match の正体とURLバリデーション
    • Pythonのライブラリっぽさがありそう?
  • 実際にfetchしてるところがどこなのか
    • SiteInstance を辿ったら良いのかなあ