Chromiumを手探る 2
前回に引き続き、chromiumにシークレットタブを導入することを目標に奮闘していきます。 記載忘れていましたが使用しているOSは macOS High Sierra です。
いざ実装
はじめに
今日のこの記事を読んで真似してみようとしてくださる方がもしいらっしゃるならば、必ず一度は最後まで目を通してからにしてください。
NewIncognitoTab() の追加
まずは Window について NewWindow(Browser* browser_);
NewIncognitoWindow(Browser* browser)
があるので、Tab についても同様にNewTab(Browser* browser_);
NewIncognitoTab(Browser* browser)
を追加して Window の構造を真似してみます。
/src/chrome/browser/ui/browser_commands.cc
に
void NewTab(Browser* browser){ NewEmptyTab(browser, browser->profile()->GetOriginalProfile()); } void NewIncognitoTab(Browser* browser){ // よくわからないフラグなのでコピーだけしておく #if 0 #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP) feature_engagement::IncognitoWindowTrackerFactory::GetInstance() ->GetForProfile(browser->profile()) ->OnIncognitoWindowOpened(); #endif #endif NewEmptyTab(browser, browser->profile()->GetOffTheRecordProfile()); } void NewEmptyTab(Browser* browser, Profile* profile){ // incognitoとbrowser->profile()を一致させる? bool incognito = profile->IsOffTheRecord(); PrefService* prefs = profile->GetPrefs(); if (incognito) { if (IncognitoModePrefs::GetAvailability(prefs) == IncognitoModePrefs::DISABLED) { incognito = false; } } else if (profile->IsGuestSession() || (browser_defaults::kAlwaysOpenIncognitoWindow && IncognitoModePrefs::ShouldLaunchIncognito( *base::CommandLine::ForCurrentProcess(), prefs))) { incognito = true; } if (incognito){ base::RecordAction(UserMetricsAction("NewIncognitoTab")); // TODO(asvitkine): This is invoked programmatically from several places. // Audit the code and change it so that the histogram only gets collected for // user-initiated commands. UMA_HISTOGRAM_ENUMERATION("Tab.NewIncognitoTab", TabStripModel::NEW_TAB_COMMAND, TabStripModel::NEW_TAB_ENUM_COUNT); SetTabProfile(profile()->GetOffTheRecordProfile()); if (browser->is_type_tabbed()) { AddTabAt(browser, GURL(), -1, true); browser->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); } else { ScopedTabbedBrowserDisplayer displayer(browser->profile()); Browser* b = displayer.browser(); AddTabAt(b, GURL(), -1, true); b->window()->Show(); // The call to AddBlankTabAt above did not set the focus to the tab as its // window was not active, so we have to do it explicitly. // See http://crbug.com/6380. b->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); } }else{ base::RecordAction(UserMetricsAction("NewTab")); // TODO(asvitkine): This is invoked programmatically from several places. // Audit the code and change it so that the histogram only gets collected for // user-initiated commands. UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_COMMAND, TabStripModel::NEW_TAB_ENUM_COUNT); SessionService* session_service = SessionServiceFactory::GetForProfileForSessionRestore(profile->GetOriginalProfile()); if (!session_service || !session_service->RestoreIfNecessary(std::vector<GURL>())) { SetTabProfile(profile()->GetOriginalProfile()); if (browser->is_type_tabbed()) { AddTabAt(browser, GURL(), -1, true); browser->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); } else { ScopedTabbedBrowserDisplayer displayer(browser->profile()); Browser* b = displayer.browser(); AddTabAt(b, GURL(), -1, true); b->window()->Show(); // The call to AddBlankTabAt above did not set the focus to the tab as its // window was not active, so we have to do it explicitly. // See http://crbug.com/6380. b->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); } } } } Browser* SetTabProfile(Profile* profile) { Browser* browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, profile, true)); return browser; }
当然ヘッダーにも新しく作った関数を入れておきます。
これで後はシークレットタブを開くGUIやらショートカットーキーを用意すれば大丈夫だとこの時は信じていました。
Chromiumのメニューにボタンを追加
chrome_command_ids.h
に大量のIDC_...
が定義されているので
#define IDC_NEW_INCOGNITO_TAB 34002
を追加。 ここからは chrome code search で IDC_NEW_TAB を検索してヒットしたところを全部読んで必要な関数とかをどんどん追記していきます。(以下長くなります)
browser_command_controller.cc
に
bool BrowserCommandController::IsReservedCommandOrKey( int command_id, const content::NativeWebKeyboardEvent& event) { ・・・ return command_id == IDC_CLOSE_TAB || command_id == IDC_CLOSE_WINDOW || command_id == IDC_NEW_INCOGNITO_TAB || ・・・ }
や
bool BrowserCommandController::ExecuteCommandWithDisposition( int id, WindowOpenDisposition disposition) { ・・・ switch (id) { ・・・ case IDC_NEW_INCOGNITO_TAB: { NewIncognitoTab(browser_); #if BUILDFLAG(ENABLE_DESKTOP_IN_PRODUCT_HELP) // This is not in NewTab() to avoid tracking programmatic creation of new // tabs by extensions. auto* new_tab_tracker = feature_engagement::NewTabTrackerFactory::GetInstance() ->GetForProfile(profile()); new_tab_tracker->OnNewTabOpened(); new_tab_tracker->CloseBubble(); #endif break; }
void BrowserCommandController::InitCommandState() { ・・・ command_updater_.UpdateCommandEnabled(IDC_NEW_INCOGNITO_TAB, true); ・・・ }
Cmd + Shift + T で新しいシークレットタブを開きたかったので accelerator.cc
に
namespace{ ・・・ const AcceleratorMapping kAcceleratorMap[] = { ・・・ #if 1 {ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR, IDC_NEW_INCOGNITO_TAB}, #else {ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_PLATFORM_ACCELERATOR, IDC_RESTORE_TAB}, #endif ・・・ } ・・・ }
とし、タブ修復のキーを消す形にしました。
tab_strip_model.cc
に
// static bool TabStripModel::ContextMenuCommandToBrowserCommand(int cmd_id, int* browser_cmd) { switch (cmd_id) { case CommandNewTab: *browser_cmd = IDC_NEW_TAB; break; case CommandNewIncognitoTab: *browser_cmd = IDC_NEW_INCOGNITO_TAB; break; ・・・ } return true; }
ちゃんとヘッダーにも CommandNewIncognitoTab を追加しています。
app_menu_model.cc
に
void AppMenuModel::LogMenuMetrics(int command_id) { base::TimeDelta delta = timer_.Elapsed(); switch (command_id) { ・・・ case IDC_NEW_INCOGNITO_TAB: if (!uma_action_recorded_) { UMA_HISTOGRAM_MEDIUM_TIMES("WrenchMenu.TimeToAction.NewIncognitoTab", delta); } LogMenuAction(MENU_ACTION_NEW_INCOGNITO_TAB); break; ・・・ } ・・・ }
void AppMenuModel::Build() { ・・・ if (ShouldShowNewIncognitoWindowMenuItem()){ AddItemWithStringId(IDC_NEW_INCOGNITO_WINDOW, IDS_NEW_INCOGNITO_WINDOW); AddItemWithStringId(IDC_NEW_INCOGNITO_TAB, IDS_NEW_INCOGNITO_TAB); } ・・・ }
MENU_ACTION_NEW_INCOGNITO_TAB を定義したいので、app_menu_model.h
に
enum AppMenuAction { ・・・ MENU_ACTION_NEW_INCOGNITO_TAB = 10, ・・・ }
を追加。
IDS_NEW_INCOGNITO_TAB を定義したいので、global_menu_bar_x11.cc
に
namespace{ ・・・ GlobalMenuBarCommand file_menu[] = { ・・・ { IDS_NEW_INCOGNITO_TAB, IDC_NEW_INCOGNITO_TAB }, ・・・ } ・・・ }
を追加。
system_menu_model_builder.cc
に
void SystemMenuModelBuilder::BuildSystemMenuForBrowserWindow( ui::SimpleMenuModel* model) { ・・・ model->AddItemWithStringId(IDC_NEW_INCOGNITO_TAB, IDS_NEW_INCOGNITO_TAB); ・・・ }
新しい定数が出てきたので startup_resources_mac.txt
に
IDS_NEW_INCOGNITO_TAB_MAC 366
を追加。
generated_resources.grd
に
・・・ <grit base_dir="." latest_public_release="0" current_release="1" output_all_resource_defines="false" source_lang_id="en" enc_check="möl"> <outputs> ・・・ <release seq="1" allow_pseudo="false"> <messages fallback_to_english="true"> ・・・ <if expr="not is_android"> <if expr="not use_titlecase"> ・・・ <message name="IDS_NEW_INCOGNITO_TAB" desc="The text label of a menu item for opening a new incognito tab"> New &incognito tab </message> ・・・ </if> <if expr="use_titlecase"> ・・・ <message name="IDS_NEW_INCOGNITO_TAB" desc="In Title Case: The text label of a menu item for opening a new incognito tab"> New &Incognito Tab </message> ・・・ </if> </if> ・・・ <if expr="is_macosx"> ・・・ <message name="IDS_NEW_INCOGNITO_TAB_MAC" desc="The Mac menu item for opening a new incognito tab in the file menu."> New Incognito Tab </message> ・・・ </if> ・・・ </messages> </release> </grit>
app_controller_mac.mm
に
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { ・・・ if (action == @selector(commandDispatch:) || action == @selector(commandFromDock:)) { ・・・ if (menuState_ && // NULL in tests. menuState_->SupportsCommand(tag)) { switch (tag) { ・・・ case IDC_NEW_INCOGNITO_TAB: ・・・ } } ・・・ } ・・・ } - (void)commandDispatch:(id)sender { ・・・ switch (tag) { ・・・ case IDC_NEW_INCOGNITO_TAB: // Create a new tab in an existing browser window (which we activate) if // possible. if (Browser* browser = ActivateBrowser(lastProfile->GetOffTheRecordProfile())) { chrome::ExecuteCommand(browser, IDC_NEW_INCOGNITO_TAB); break; } FALLTHROUGH; // To create new window. } }
- (void)initMenuState { menuState_ = std::make_unique<CommandUpdaterImpl>(nullptr); menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true); menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_TAB, true); ・・・ }
cocoa とかいう macOS でしか使わなさそうなファイルの中にも aceelerator がいたので編集します。
accelerators_cocoa.mm
にて
namespace { const struct AcceleratorMapping { int command_id; int modifiers; // The ui::EventFlag modifiers ui::KeyboardCode key_code; // The key used for cross-platform compatibility. } kAcceleratorMap[] = { ・・・ #if 1 {IDC_NEW_INCOGNITO_TAB, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN, ui::VKEY_T}, #else {IDC_RESTORE_TAB, ui::EF_COMMAND_DOWN | ui::EF_SHIFT_DOWN, ui::VKEY_T}, #endif ・・・ } ・・・ }
今度は NEW_TAB で当たってみました。
ash なので意味がないかもしれませんが、accelerators.h
に
namespace ash{ enum AcceleratorAction{ ・・・ NEW_INCOGNITO_TAB, ・・・ } }
accelerators.cc
に
namespace ash { const AcceleratorData kAcceleratorData[] = { ・・・ #if 1 {true, ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, NEW_INCOGNITO_TAB}, #else {true, ui::VKEY_T, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, RESTORE_TAB}, #endif ・・・ } const size_t kAcceleratorDataLength = arraysize(kAcceleratorData); }
ビルドしてみたが...?
おそらく大体の変更が終了したのでひとまずビルドしてみることに。 1回目は上記の全てをきちんと行えていたわけではないので、出てくるエラーを修正しながら根性でビルドビルドビルド......
ついにビルドが終わった!!といって意気揚々と起動してみるわけですよ。 読者の方々は察していると思いますが、ね。。。
メニューには "New Incognito Tab" と表示されますが、クリックしても普通のタブが開かれてしまいます。 (スクショ撮り忘れたのでお見せできなくて申し訳ない)
果たして原因はなんなのでしょうか...次回へ続く。
Chromiumを手探る
思いつき
シークレットウィンドウって便利ですよね。
でもあれなんでウィンドウしかないんでしょうね。タブごとにモード切り替えとかできたら便利だろうに。
ということで、chrome code searchを用いてソースを探って弄って、chromiumにそんな機能を搭載してみようじゃないか、という初心者の無謀なチャレンジです。1
(GDBとかでデバッグしようとしたり、XCodeで追っかけたりしようかと思ったが無理だった。XCodeに至ってはなぜかbuildもできなかった。つらい)
codeをひたすら追ってみる
まずは「とりあえずwindowかtabをopenするのに関連してる関数を見つけてそれを追っていけばなんか見つかるやろ」作戦です。2 いやいやこれがつらいのなんのその。
適当に "new_window command" とかで検索かけると/src/chrome/app/chrome_command_ids.h L.31
からそれっぽいものが見つかりました。
// Window management commands #define IDC_NEW_WINDOW 34000 #define IDC_NEW_INCOGNITO_WINDOW 34001
/src/chrome/browser/app_controller_mac.mm L.1046
から
switch (tag) { case IDC_NEW_TAB: // Create a new tab in an existing browser window (which we activate) if // possible. if (Browser* browser = ActivateBrowser(lastProfile)) { chrome::ExecuteCommand(browser, IDC_NEW_TAB); break; } FALLTHROUGH; // To create new window. case IDC_NEW_WINDOW: CreateBrowser(lastProfile); break;
まずはTABについて見てみます。
ActivateBrowser(lastProfile) について/src/chrome/browser/app_controller_mac.mm L.138
にて
Browser* ActivateBrowser(Profile* profile) { Browser* browser = chrome::FindLastActiveWithProfile( profile->IsGuestSession() ? profile->GetOffTheRecordProfile() : profile); if (browser) browser->window()->Activate(); return browser; }
ExecuteCommand(browser, IDC_NEW_TAB) について/src/chrome/browser/ui/browser_commands.cc L.336
にて
bool ExecuteCommand(Browser* browser, int command) { return browser->command_controller()->ExecuteCommand(command); }
command_updater_impl.h
のL.34に
// Overriden from CommandUpdater:
とあったので見てみると、L.40にて
bool CommandUpdaterImpl::ExecuteCommand(int id) { return ExecuteCommandWithDisposition(id, WindowOpenDisposition::CURRENT_TAB); }
ExecuteCommandWithDisposition(id, WindowOpenDisposition::CURRENT_TAB)
について
/src/chrome/browser/ui/browser_command_controller.cc L.288
にて
bool BrowserCommandController::ExecuteCommandWithDisposition( int id, WindowOpenDisposition disposition) {
すると同じソースにswitch(id)
から
L.340
// Window management commands case IDC_NEW_WINDOW: NewWindow(browser_); break; case IDC_NEW_INCOGNITO_WINDOW: NewIncognitoWindow(browser_); break;
とか
L.351
case IDC_NEW_TAB: { NewTab(browser_);
を発見。ようやくそれらしいものを見つけました。
おそらくbrowser_
というのはWindowOpenDisposition disposition
で指定されたWindowOpenDispositonの二個上のclass(?)を表しているっぽいですね。
とりあえずWindowについて、/src/chrome/browser/ui/browser_commands.cc L.576
にて
void NewWindow(Browser* browser) { NewEmptyWindow(browser->profile()->GetOriginalProfile()); }
"NewEmptyWindow"で検索をかけると/src/chrome/browser/ui/browser_commands.cc L.382
にて
void NewEmptyWindow(Profile* profile) { bool incognito = profile->IsOffTheRecord(); PrefService* prefs = profile->GetPrefs(); if (incognito) { if (IncognitoModePrefs::GetAvailability(prefs) == IncognitoModePrefs::DISABLED) { incognito = false; } } else if (profile->IsGuestSession() || (browser_defaults::kAlwaysOpenIncognitoWindow && IncognitoModePrefs::ShouldLaunchIncognito( *base::CommandLine::ForCurrentProcess(), prefs))) { incognito = true; } if (incognito) { base::RecordAction(UserMetricsAction("NewIncognitoWindow")); OpenEmptyWindow(profile->GetOffTheRecordProfile()); } else { base::RecordAction(UserMetricsAction("NewWindow")); SessionService* session_service = SessionServiceFactory::GetForProfileForSessionRestore( profile->GetOriginalProfile()); if (!session_service || !session_service->RestoreIfNecessary(std::vector<GURL>())) { OpenEmptyWindow(profile->GetOriginalProfile()); } } } Browser* OpenEmptyWindow(Profile* profile) { Browser* browser = new Browser(Browser::CreateParams(Browser::TYPE_TABBED, profile, true)); AddTabAt(browser, GURL(), -1, true); browser->window()->Show(); return browser; }
これやん。やったぜ。 この枠組みをopen tabみたいなところに流用すればいいのでは? ということで今度はtabの方を見てみます。
"NewTab"で検索をかけると、すこし関係なさそうではあるのですが、いろんなタブの開き方をまとめているような感じのソースを見つけました。(上の方にAddTabTypes
とか書いてる)
/src/chrome/browser/ui/tabs/tab_strip_model.h L.101
にて
// Enumerates different ways to open a new tab. Does not apply to opening // existing links or searches in a new tab, only to brand new empty tabs. enum NewTab { // New tab was opened using the new tab button on the tab strip. NEW_TAB_BUTTON, // New tab was opened using the menu command - either through the keyboard // shortcut, or by opening the menu and selecting the command. Applies to // both app menu and the menu bar's File menu (on platforms that have one). NEW_TAB_COMMAND, // New tab was opened through the context menu on the tab strip. NEW_TAB_CONTEXT_MENU, // Number of enum entries, used for UMA histogram reporting macros. NEW_TAB_ENUM_COUNT, };
同様に検索すると/src/chrome/browser/ui/browser_commands.cc L. 594
にて
void NewTab(Browser* browser) { base::RecordAction(UserMetricsAction("NewTab")); // TODO(asvitkine): This is invoked programmatically from several places. // Audit the code and change it so that the histogram only gets collected for // user-initiated commands. UMA_HISTOGRAM_ENUMERATION("Tab.NewTab", TabStripModel::NEW_TAB_COMMAND, TabStripModel::NEW_TAB_ENUM_COUNT); if (browser->is_type_tabbed()) { AddTabAt(browser, GURL(), -1, true); browser->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); } else { ScopedTabbedBrowserDisplayer displayer(browser->profile()); Browser* b = displayer.browser(); AddTabAt(b, GURL(), -1, true); b->window()->Show(); // The call to AddBlankTabAt above did not set the focus to the tab as its // window was not active, so we have to do it explicitly. // See http://crbug.com/6380. b->tab_strip_model()->GetActiveWebContents()->RestoreFocus(); } }
共通なのはAddTabAt()
なのでここを見ればいいのかも?
とりあえず現時点の動作としては、
・windowを開くときにincognitoかどうか判断
→profileを決定 (GetOffTheRecordProfile()かGetOriginalProfile()か)
→OpenEmptyWindowの中でBrowser *browser
を決定 (引数にprofileあり)
→AddTabAt(browser, GURL(), -1, true)でTabが作られてshow();で多分Tabが表示
・tabを開くときはprofileがwindowと違いprofileについてincognitoかどうか判定がない →なぜならwindowを開く時点(NewEmptyWindow())でincognitoの値を決定しているから →ならばtabを開くときにNewEmptyTab()みたいな関数を入れてincognitoの判定をtab作成のステップに移せばいけるのでは
まとめ
ソースを見る限り意外と実装は簡単そう...? だが現実はそんな甘くないんだろうな、と想像しつつ次回へ続きます。
-
実は大学の後期実験 大規模ソフトウェアを手探る だったりします。↩