大規模ソフトウェアを手探る Chromeのソースコードとドキュメントをひたすら漁る
Chromeの膨大なソースコードから変更するクラスを見つける
Chromeをビルドできたところでいよいよ実際にコードの変更を行なっていきます。 今回私たちが追加しようとした機能はChromeの上部にある検索ボックスに表示される検索履歴に単語フィルターをかける(つまり特定の単語が含まれる検索履歴は表示しない)というものでした。 そこで私たちの考えたアイディアとしては「入力に従って検索ボックス情報を更新しているモジュール上で、特定の単語が含まれる検索履歴は更新するデータに追加しないようにする」もしくは「特定の単語が含まれる検索ワードは検索履歴のデータに保存されないように」しようと言うものでした。 最初は検索ボックス内でヒストリー(過去の検索履歴)を読み出してautocompleteの候補を作成しているクラスでちょっとif文追加するだけの簡単なお仕事かな〜と楽観的に考えていたのですが、現実はそこまで甘くはありませんでした…
いきなりChrome code searchの限界に直面する
まず最初は前回紹介したChrome Code Searchで検索をかけます。とりあえず「history search」で検索をかけてみる… 4400件wwwwwww
これを全部見ていくのはしんどそう…
とりあえずいきなり適当な単語でcode searchはあんまりよくないと言うことに気がつき(それはそう)、まずどういったモジュールがあの検索ボックスの自動補完に対応しているのか、Chromeのドキュメントから調べていくことにしました。
Chromium (Chrome Browser, Chrome OS)の開発者向けドキュメント (design doc) は公式ホームペーシ、The Chromium ProjectのFor Developers>Design Documentsから検索できます。
Google検索していたらChromeの上部にある検索ボックスはomniboxと呼ばれることがわかったので、このomnibox関連のdesign docを調べていくことにしました。
omnibox関連のドキュメントをとりあえずぐぐりまくる
公式のDesign Docsのうち、まず注目したのはOmnibox: History Providerについてのドキュメントです。
One of the autocomplete providers for the omnibox (the HistoryQuickProvider, HQP for short) serves up autocomplete candidates from the profile's history database. ふむふむ…autocomplete providersなるものが自動補完候補をユーザープロファイルのDBから取得していることがわかります。 またこのautocomplete providersにはいくつか種類があることも読み取れます。
次はDesign Docではないのですが、一般向けのOmniboxの説明をしたドキュメントを見つけました。 これによると、Omniboxの自動補完は以下のタイプに分類できるようです。
Search for... This searches for the typed query - this will be the topmost and default item if the input looks like a search string.
URL If the input (after auto-complete, if applicable) looks like a URL, this will repeat the user's input and be the default, top-most item.
Previous URLs If the input matches a previous URL, those URLs will be shown here.
Nav Suggest Uses the user's default search provider to suggest URLs based on the user's input.
Search Suggest Uses the user's default search provider to suggest search terms based on the user's input.
History Results Shows the number of entries in the user's history that match the given input - selecting this item will take the user to the history results page for their given search term.
これを見た感じ検索ワードの履歴を返しているのはHistory Resultsみたいですね…History Resultに関連してそうなSearchProviderクラスが見つかりました。
とりあえずSearchProviderクラスを変更してみる
SearchHistoryクラスを見ていくと、ScoreHistoryResults
という関数が、過去の検索履歴のうち入力文字列とマッチするHistoryResults
ついて、関連度スコアrelevance
をつけてそれに基づいてソートしていることわかりました。ScoreHistoryResults
自体はSearchProvider::SearchHistoryResultHelperという別のヘルパー関数を呼んで、実際に自動補完候補のスコア付などを行なっています。
とりあえず、この関数の中でHistoryResults
から、検索文字列があるキーワードと一致する時にscored_resutsに追加しないようにするように書き換えて見ました。
SearchProvider::ScoreHistoryResultsHelper(const HistoryResults& results, bool base_prevent_inline_autocomplete, bool input_multiple_words, const base::string16& input_text, bool is_keyword) { ... // resultsは過去の検索履歴のうち入力文字列とマッチするものを全て取り出している。 for (HistoryResults::const_iterator i(results.begin()); i != results.end(); ++i) { ... SearchSuggestionParser::SuggestResult history_suggestion( trimmed_suggestion, AutocompleteMatchType::SEARCH_HISTORY, 0, trimmed_suggestion, base::string16(), base::string16(), base::string16(), base::string16(), nullptr, std::string(), std::string(), is_keyword, relevance, false, false, trimmed_input); history_suggestion.set_received_after_last_keystroke(false); // scored_resultsにキーワードとマッチしていない時のみ追加する。 // 変更ここから if (base::EqualsASCII(history_suggestions.suggestion(), “harry”) == false){ scored_results.insert(insertion_position, history_suggestion); } // 変更ここまで } ... }
よしこれでひとまずフィルター機能は実装できたはず…...と思うも、search_providerの返す結果では"harry"が消えているのに表示される結果には"harry"が残っていると言う謎の事態に......
次回のブログではSearchProviderから供給されるデータに対してフィルターをかけるだけではなぜダメだったのか、OmniboxのAutocompleteのデータフローを追いながら原因を解明していきます。