雑記

ものを書く練習。学びをまとめる場。

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作成のステップに移せばいけるのでは

まとめ

ソースを見る限り意外と実装は簡単そう...?
だが現実はそんな甘くないんだろうな、と想像しつつ次回へ続きます。


  1. 実は大学の後期実験 大規模ソフトウェアを手探る だったりします。

  2. これペアプログラミングなんですが相方は真面目に起動時からデバッガで追跡してました。ほんますまん。