chromiumのタブをいじる(1)

この記事は東京大学電子情報工学科3年の後期実験である「大規模ソフトウェアを手探る」で行われた班活動を記事にしたものです．

## 実行環境
Ubuntu 18.04.5 (LTS)

## 問題点
chromeのタブを開きすぎてタブが小さくなりすぎ，今開いているタブの情報すらわからないと言ったことが今までに何回かあったのではないでしょうか．
今開いているタブだけ大きくなったらいいのに！という欲求からchromeブラウザをいじり，今開いているタブの大きさを変えて，みやすい形にすることを目標に当面は進んでみることにしました．

## chromiumとは
とは言ってもchromeはOSSではないのでいじることはできません．そこでchromeのOSS版であるchromiumを用いてこの機能を実現することにしました．
chromiumの[wiki](https://ja.wikipedia.org/wiki/Chromium)によるとchromeはこのchromiumからコードを引き抜いて開発されているみたいです．つまりchromeには取り入れられていないような最新の機能があったりする代わりに全て正常に動作するという保証はありません．


## build完了まで
公式のリンクがあったのでそれを参考にbuildを進めることにしました．(buildが大変だと脅されていたのですが，班員三人全員この方法で誰一人つまずくことなく，完了できたのでこの方法で問題ないと思います．)
https://github.com/chromium/chromium/blob/master/docs/linux/build_instructions.md

### 準備


[instruction][1]によると、

[1]:https://github.com/chromium/chromium/blob/master/docs/linux/build_instructions.md


>At least 100GB of free disk space.

とある。実際ビルド完了後、実行ファイルとソースコード含めて26GBになったので要注意。


GitとPython2が必要らしいので、インストールした。(Ubuntu 18.04 には、Pythonは標準で入っていない。)
pythonのバージョンによるバグが多いため3系を使えなくしておくと安全

```sh

$ sudo apt install git
$ sudo apt install python
```


まずビルドに必要なツールのリポジトリをクローンする。

```sh

$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
```

先ほどcloneしたディレクトリにPATHを通す。ホームディレクトリの場合は、~/を使ってはいけないらしい。

```sh

$ export PATH="$PATH:${HOME}/depot_tools"
```

### コードを取得

ソースコードを入れる用のディレクトリを作成し、そこに移動する。

```sh

$ mkdir ~/chromium && cd ~/chromium
```

ソースコードをfetchする。今回はhistoryは必要ないので、`--no-history`オプションをつけることで高速化する。

```sh

$ fetch --nohooks --no-history chromium
```

うまくいけば数十分で完了し、srcディレクトリができるので、そこに移動する。

```sh 

$ cd src
```

次のコマンドでビルド依存関係を取得

```sh 

$ ./build/install-build-deps.sh
```

必要な追加のバイナリファイルをダウンロードする。

```sh

$ gclient runhooks
```

### ビルドの設定

ビルド先を指定する。次のコマンドを実行すると、~/chromium/src/out/Default　に実行ファイルができる。

```sh

$ gn gen out/Default
```

ビルドのオプションを設定するためのファイルを開く。

```sh 

$ gn args out/Default
```

vimをインストールしていない場合は、


```sh

$ sudo apt-get install vim
```

でインストールする。

ビルドのオプションのファイルには、

```
enable_nacl=false
is_debug=true
symbol_level=2
```

と書き込み、保存してvimを終了する。

[instruction][1]に、高速化のオプションがいろいろ書かれているが、今回は

一行目で NaCl を無効にしている。
二行目でデバッグビルドにしている。
三行目でデバッグシンボルの数を設定している。level2が最大。

([instruction][1]のオプションの一つに、ccacheというものがあり、これが上手くいけば、複数のワーキングディレクトリがある場合の二つ目以降のビルドが高速化するらしい。`$ export CCACHE_BASEDIR=${HOME}/chromium/src`として使ってみたが、効いているのかどうかわからなかった。)

### ビルド

次のコマンドでビルドできる。

```sh 

$ autoninja -C out/Default chrome
```
デバッグビルドの場合これは４〜５時間くらいかかった。
ビルドの最中にエラーが出た場合は、https://qiita.com/iseki-masaya/items/9bad09279749acdb1cf1 を参照して解決した。
ビルド完了したら、次のコマンドでchromiumが開く。

```sh 

$ out/Default/chrome
```

### 同じターゲットで2回目以降のビルド

ソースコードを書き換えて試しにビルドしたい場合、パスを通して再度autoninjaするだけで良い。

```sh 

$ export PATH="$PATH:${HOME}/depot_tools"
$ cd ~/chromium/src
$ autoninja -C out/Default chrome
```

### 設定を変えて別のターゲットにビルド

debugビルドを残しつつ、releaseビルドしたい場合など。
ターゲットはoutのサブディレクトリに設定する必要があるらしい。
例えば~/chromium/src/out/releaseにビルドしたい場合は、

```sh 

$ export PATH="$PATH:${HOME}/depot_tools"
$ cd ~/chromium/src
$ gn gen out/release
$ gn args out/release
```
vim開いたファイルに、ビルドのオプションを

```
enable_nacl=false
is_debug=false
symbol_level=0
```
と書き込んで、

```sh 

$ autoninja -C out/release chrome
```
とすれば良い。これには４時間くらいかかるので注意。(ccacheが効けば早くなるのかもしれない。)

## ソースコードを眺めてみる
- 非常に大きなソフトウェアであるため最初からgdbを使って動作を調べるのは大変です．そのためソースコードをみてあたりをつけようと思います．
- IDEの機能で見るのもいいですし,webページ上でソースコードを[検索できるサイト](https://source.chromium.org/chromium/chromium/src)も使えます.
- あたりをつけた後はそこにブレークポイントを貼ってgdbで動作を追跡します.
    - どうしてもgdbが使えない場合はprintfデバッグでがんばりましょう.

### タブに関する全体の流れを追う
新しくタブをつくるとタブ幅が調整されるので,新しくタブを作ったときに呼ばれる関数を調べてみることにします.
"new tab"などで検索をかけてみると,browser_tab_strip_controller.cc:757に

```cpp

void BrowserTabStripController::AddTab(WebContents* contents,
                                       int index,
                                       bool is_active) {
  // Cancel any pending tab transition.
  hover_tab_selector_.CancelTabTransition();

  tabstrip_->AddTabAt(index, TabRendererData::FromTabInModel(model_, index),
                      is_active);

  // Try to show tab groups IPH if needed.
  if (tabstrip_->tab_count() >= 6) {
    feature_engagement_tracker_->NotifyEvent(
        feature_engagement::events::kSixthTabOpened);

    browser_view_->feature_promo_controller()->MaybeShowPromo(
        feature_engagement::kIPHDesktopTabGroupsNewGroupFeature);
  }
}
```
が見つかります.これを呼び出している関数はbrowser_tab_strip_controller.cc:619の

```cpp

////////////////////////////////////////////////////////////////////////////////
// BrowserTabStripController, TabStripModelObserver implementation:

void BrowserTabStripController::OnTabStripModelChanged(
    TabStripModel* tab_strip_model,
    const TabStripModelChange& change,
    const TabStripSelectionChange& selection) {
  switch (change.type()) {
    case TabStripModelChange::kInserted: {
      for (const auto& contents : change.GetInsert()->contents) {
        DCHECK(model_->ContainsIndex(contents.index));
        AddTab(contents.contents, contents.index,
               selection.new_contents == contents.contents);
      }
      break;
    }
    case TabStripModelChange::kRemoved: {
    ...
    }
    case TabStripModelChange::kMoved: {
    ...
    }
    case TabStripModelChange::kReplaced: {
    ...
    }
    case TabStripModelChange::kSelectionOnly:
      break;
  }
  ...
}
```

であり,この関数がタブ全体に変更が加わったときに呼び出されて,変更内容(追加,削除,移動など)によって処理を振り分けていることが見て取れます.
TabStripというのはタブが配置されるブラウザ上部の細長いバーのことだと思われます.

### タブの幅はどこで変更されているか
- タブの変更に関するおおまかな処理の流れは見えたので,タブが追加されたときのことを細かく見ていくことにします.先程のBrowserTabStripController::AddTab()が呼び出すTabStrip::AddTab()(tab_strip.cc:1201)の中を見ていきます.
- 一つずつそれらしい関数の中を調べていくとtab_strip_layout_helper.cc:242のTabStripLayoutHelper::UpdateIdealBounds()が見つかります.(ここまでが結構大変でした)

```cpp

int TabStripLayoutHelper::UpdateIdealBounds(int available_width) {
  const std::vector<gfx::Rect> bounds = CalculateIdealBounds(available_width);
  DCHECK_EQ(slots_.size(), bounds.size());

  views::ViewModelT<Tab>* tabs = get_tabs_callback_.Run();
  const int active_tab_model_index = controller_->GetActiveIndex();
  const int active_tab_slot_index =
      controller_->IsValidIndex(active_tab_model_index)
          ? GetSlotIndexForTabModelIndex(
                active_tab_model_index,
                tabs->view_at(active_tab_model_index)->group())
          : TabStripModel::kNoTab;

  int current_tab_model_index = 0;
  for (int i = 0; i < int{bounds.size()}; ++i) {
    const TabSlot& slot = slots_[i];
    switch (slot.type) {
      case ViewType::kTab:
        if (!slot.animation->IsClosing()) {
          tabs->set_ideal_bounds(current_tab_model_index, bounds[i]);
          UpdateCachedTabWidth(i, bounds[i].width(),
                               i == active_tab_slot_index);
          ++current_tab_model_index;
        }
        break;
      case ViewType::kGroupHeader:
        if (!slot.view->dragging())
          group_header_ideal_bounds_[slot.view->group().value()] = bounds[i];
        break;
    }
  }

  return bounds.back().right();
}
```
- 一行目でavailable_widthをもとにタブの幅などを計算(CalculateIdealBounds())し,boundsの大きさだけforループを回して何らかの幅を更新(UpdateCachedTabWidth()),最後にboundsの最終要素の片方を返していることがわかります.
- タブが追加されたときのことを調べてきましたが,この関数はタブを削除,移動した時にも呼ばれていることがソースコードから確認できます.
- 結局,この関数の中身をいじればタブの幅の仕様に変更を加えることができそうです.

### 手探る途中でわかったこと
- デフォルトのタブ幅はtab_style.cc:28のkTabWidthに規定されている.
- boundsはgfx::Rect型のvectorで,gfx::Rectは二次元平面上の点(始点)と長方形の幅,高さをメンバ変数に持つ長方形を表すクラス.一つ一つのタブをこれで管理しているようなので,タブの幅を変えるにはタブの幅とx座標を変えればよさそう.
- 最終的にたどり着いたtab_strip_layout_helper.ccには様々なhelper関数が入っていてtabに関する機能を実装するときに役立ちそう(helperなのでそれはそう)
    - タブがアクティブになったときに呼ばれているsetActiveTab()(l.210)
    - タブを移動したときに呼ばれているMoveTab()(l.151)


## 実際に変更
眺めた結果からtab_strip_layout_helper.ccのCaluculateIdealboundsの一行目あたりをいじれば良いとわかったため，実際に変更していこうと思います．

```cpp

int TabStripLayoutHelper::UpdateIdealBounds(int available_width) {
  const std::vector<gfx::Rect> bounds = CalculateIdealBounds(available_width);
  DCHECK_EQ(slots_.size(), bounds.size());

  ...
```
このboundsがどのようにして作られているのかを詳細に確認してみましょう．
まずはgdbで追ってみましょう．
(スペック依存だと思いますが，gdbで追う際の注意としてbreakpointをおくとフリーズする関数があります．一度でもフリーズしたら多分そこは罠です．)
ここでboundsをみるときはvector型のメソッド(atとか）を使うとp関数で表示できます．

```
$(gdb) p bounds.at(i)
Cannot evaluate function – may be inlined
```
みたいな感じになったら(なんかよくわからないけど)配列の呼び出し方で呼べます．
availableサイズとタブ数に対応するboundsのサイズは以下のようになります
x1-x2というのはx1がタブの左端の座標,x2がタブの右端の座標を表しています．

>available_width=1070でタブ数6の時
0-193,176-369,352-545,528-720,703-895,878-1070
available_width=1070でタブ数8の時
0-149,132-281,264-413,396-545,528-677,660-808,791-939,922-1070
available_width=1070でタブ数3の時
0-256,239-495,478-734

このことからタブとタブの重なりが17で一定で他は均等に割り振るがmaxが存在するという仮説が考えられます．実際available_widthをいじってもそうなっていることが確認できました．

こんな感じで実装の概略がわかったところで実際なにをしているのかをソースコードを読んで考えます．
CalculateIdealBoundsのreturnで返している，CaluculateTabBoundsが実際の計算っぽいことをしていそうです．

```cpp

...
  int next_x = 0;
  std::vector<gfx::Rect> bounds;
  for (const TabWidthConstraints& tab : tabs) {
    const int tab_width = tab_sizer.CalculateTabWidth(tab);
    bounds.push_back(
        gfx::Rect(next_x, 0, tab_width, layout_constants.tab_height));
    next_x += tab_width - layout_constants.tab_overlap;
  }
...
```
この辺で実際にtabごとにboundsに代入していることがわかります．
何らかの方法でtab_widthを生成し，またlayout_constants.overlapの決まった値をタブ同士で被らせていることがわかり仮説が正しかったのだとわかります．
そこでこのboundsを実際に弄ります．
概略としては

1. available_widthを取得
2. Tabの個数sizeを取得
3. available_width<-available_width+(size-1)*layout_constants.tab_overlapとする
4. available_widthをこちらが定めたactive_tabとinactive_tabの比率によって分配する．
5. 左端のx座標をタブの大きさ-overlapで求めていく

みたいな感じです．これをもとに例えば下のような関数を作れば良いです．ここでactiveとinactiveは2:1の大きさであるとします．

```cpp

std::vector<gfx::Rect> TabStripLayoutHelper::myCaluculateIdealBounds(int available_width,int size){
    //sizeはslots_.size()で取得可能
    
    std::vector<gfx::Rect> bounds;
    
    const int active_tab_model_index = controller_->GetActiveIndex();
    const TabLayoutConstants layout_constants = GetTabLayoutConstants();
    
    //17の固定値
    int overlap = layout_constants.tab_overlap;
    
    //今回使える実質的なavailable_width
    int width = available_width+overlap*(size-1);

    //size+1なのはactive_tabが2倍なので2個分必要なため．
    int common_width = width/(size+1);
    //あまりも分配する必要があるので
    int res = width%(size+1);
    //タブのmaxの大きさを取得
    int standard_width = TabStyle::GetStandardWidth();

    int next_x=0;
    int tab_width;
    for(int i=0;i<size;i++){
    if(res>0){
    //あまりがまだ残っているとき
        res--;
        tab_width=common_width+1;
    }else{
        tab_width = common_width;
    }
    
    if(i==active_tab_model_index){
    //activetabは2倍なのでもう一度加算する
        tab_width+=common_width;
    }
    if(tab_width>standard_width){
    //maxを超えていたらmaxに合わせる．
        tab_width = standard_width;
    }
    
    //boundsはRectクラスを所有する
    bounds.push_back(
        gfx::Rect(next_x,0,tab_width,layout_constants.tab_height)
    );

    //next_xは現在の位置からwidth進んで被り分戻った位置
    next_x += tab_width-overlap;
    }

    return bounds;
}

```

ここでDCHECKみたいなアサーションチェッカが入っているのでslots_とboundsの大きさが違うと怒られます．エラーコードが吐かれたらboundsのpush_backがきちんと回数分行われているのかを確認しましょう．
<details><summary>エラー文の例</summary><div>

```shell

[9090:9090:1012/163929.565797:INFO:content_main_runner_impl.cc(975)] Chrome is running in full browser mode.
[9090:9090:1012/163929.854993:WARNING:account_consistency_mode_manager.cc(63)] Desktop Identity Consistency cannot be enabled as no OAuth client ID and client secret have been configured.
[9116:9116:1012/163929.920579:ERROR:sandbox_linux.cc(374)] InitializeSandbox() called with multiple threads in process gpu-process.
[9090:9090:1012/163929.922543:ERROR:trusted_vault_connection_impl.cc(16)] Not implemented reached in syncer::TrustedVaultConnectionImpl::TrustedVaultConnectionImpl(std::unique_ptr<network::PendingSharedURLLoaderFactory>, std::unique_ptr<TrustedVaultAccessTokenFetcher>)
[9090:9090:1012/163930.394158:FATAL:tab_strip_layout_helper.cc(293)] Check failed: slots_.size() == bounds.size() (1 vs. 0)
#0 0x7fce4d14259f base::debug::CollectStackTrace()
#1 0x7fce4ced507d base::debug::StackTrace::StackTrace()
#2 0x7fce4ced5038 base::debug::StackTrace::StackTrace()
#3 0x7fce4cf1847d logging::LogMessage::~LogMessage()
#4 0x7fce4cf18d2c logging::LogMessage::~LogMessage()
#5 0x7fce4ce93eae logging::CheckError::~CheckError()
#6 0x55c617fa4806 TabStripLayoutHelper::UpdateIdealBounds()
#7 0x55c617f59408 TabStrip::UpdateIdealBounds()
#8 0x55c617f57a86 TabStrip::CompleteAnimationAndLayout()
#9 0x55c617f57152 TabStrip::AddTabAt()
#10 0x55c617f1ffc3 BrowserTabStripController::AddTab()
#11 0x55c617f21b99 BrowserTabStripController::OnTabStripModelChanged()
#12 0x55c6178ff580 TabStripModel::InsertWebContentsAtImpl()
#13 0x55c6179051ed TabStripModel::AddWebContents()
#14 0x55c6178491b9 Navigate()
#15 0x55c6178eb621 StartupBrowserCreatorImpl::OpenTabsInBrowser()
#16 0x55c6178ec5af StartupBrowserCreatorImpl::RestoreOrCreateBrowser()
#17 0x55c6178eaf1c StartupBrowserCreatorImpl::DetermineURLsAndLaunch()
#18 0x55c6178ea534 StartupBrowserCreatorImpl::Launch()
#19 0x55c6178e3afc StartupBrowserCreator::LaunchBrowser()
#20 0x55c6178e59a7 StartupBrowserCreator::ProcessLastOpenedProfiles()
#21 0x55c6178e5396 StartupBrowserCreator::LaunchBrowserForLastProfiles()
#22 0x55c6178e35e1 StartupBrowserCreator::ProcessCmdLineImpl()
#23 0x55c6178e29a1 StartupBrowserCreator::Start()
#24 0x55c613cce8cf ChromeBrowserMainParts::PreMainMessageLoopRunImpl()
#25 0x55c613ccd7e2 ChromeBrowserMainParts::PreMainMessageLoopRun()
#26 0x7fce443ba803 content::BrowserMainLoop::PreMainMessageLoopRun()
#27 0x7fce443c5e63 base::internal::FunctorTraits<>::Invoke<>()
#28 0x7fce443c5d71 base::internal::InvokeHelper<>::MakeItSo<>()
#29 0x7fce443c5cf7 _ZN4base8internal7InvokerINS0_9BindStateIMN7content15BrowserMainLoopEFivEJNS0_17UnretainedWrapperIS4_EEEEEFivEE7RunImplIS6_NSt4__Cr5tupleIJS8_EEEJLm0EEEEiOT_OT0_NSD_16integer_sequenceImJXspT1_EEEE
#30 0x7fce443c5ca7 base::internal::Invoker<>::RunOnce()
#31 0x7fce447b0887 _ZNO4base12OnceCallbackIFivEE3RunEv
#32 0x7fce45621949 content::StartupTaskRunner::RunAllTasksNow()
#33 0x7fce443b90ab content::BrowserMainLoop::CreateStartupTasks()
#34 0x7fce443c9c3f content::BrowserMainRunnerImpl::Initialize()
#35 0x7fce443b6064 content::BrowserMain()
#36 0x7fce466c0324 content::RunBrowserProcessMain()
#37 0x7fce466c19b1 content::ContentMainRunnerImpl::RunServiceManager()
#38 0x7fce466c1266 content::ContentMainRunnerImpl::Run()
#39 0x7fce466bd7f7 content::RunContentProcess()
#40 0x7fce466be200 content::ContentMain()
#41 0x55c61106025d ChromeMain
#42 0x55c6110600f2 main
#43 0x7fce15cf4b97 __libc_start_main
#44 0x55c61105ffea _start

Received signal 6
#0 0x7fce4d14259f base::debug::CollectStackTrace()
#1 0x7fce4ced507d base::debug::StackTrace::StackTrace()
#2 0x7fce4ced5038 base::debug::StackTrace::StackTrace()
#3 0x7fce4d142058 base::debug::(anonymous namespace)::StackDumpSignalHandler()
#4 0x7fce17c1c8a0 (/lib/x86_64-linux-gnu/libpthread-2.27.so+0x1289f)
#5 0x7fce15d11f47 gsignal
#6 0x7fce15d138b1 abort
#7 0x7fce4d1415c6 base::debug::(anonymous namespace)::DebugBreak()
#8 0x7fce4d1415a8 base::debug::BreakDebugger()
#9 0x7fce4cf18b69 logging::LogMessage::~LogMessage()
#10 0x7fce4cf18d2c logging::LogMessage::~LogMessage()
#11 0x7fce4ce93eae logging::CheckError::~CheckError()
#12 0x55c617fa4806 TabStripLayoutHelper::UpdateIdealBounds()
#13 0x55c617f59408 TabStrip::UpdateIdealBounds()
#14 0x55c617f57a86 TabStrip::CompleteAnimationAndLayout()
#15 0x55c617f57152 TabStrip::AddTabAt()
#16 0x55c617f1ffc3 BrowserTabStripController::AddTab()
#17 0x55c617f21b99 BrowserTabStripController::OnTabStripModelChanged()
#18 0x55c6178ff580 TabStripModel::InsertWebContentsAtImpl()
#19 0x55c6179051ed TabStripModel::AddWebContents()
#20 0x55c6178491b9 Navigate()
#21 0x55c6178eb621 StartupBrowserCreatorImpl::OpenTabsInBrowser()
#22 0x55c6178ec5af StartupBrowserCreatorImpl::RestoreOrCreateBrowser()
#23 0x55c6178eaf1c StartupBrowserCreatorImpl::DetermineURLsAndLaunch()
#24 0x55c6178ea534 StartupBrowserCreatorImpl::Launch()
#25 0x55c6178e3afc StartupBrowserCreator::LaunchBrowser()
#26 0x55c6178e59a7 StartupBrowserCreator::ProcessLastOpenedProfiles()
#27 0x55c6178e5396 StartupBrowserCreator::LaunchBrowserForLastProfiles()
#28 0x55c6178e35e1 StartupBrowserCreator::ProcessCmdLineImpl()
#29 0x55c6178e29a1 StartupBrowserCreator::Start()
#30 0x55c613cce8cf ChromeBrowserMainParts::PreMainMessageLoopRunImpl()
#31 0x55c613ccd7e2 ChromeBrowserMainParts::PreMainMessageLoopRun()
#32 0x7fce443ba803 content::BrowserMainLoop::PreMainMessageLoopRun()
#33 0x7fce443c5e63 base::internal::FunctorTraits<>::Invoke<>()
#34 0x7fce443c5d71 base::internal::InvokeHelper<>::MakeItSo<>()
#35 0x7fce443c5cf7 _ZN4base8internal7InvokerINS0_9BindStateIMN7content15BrowserMainLoopEFivEJNS0_17UnretainedWrapperIS4_EEEEEFivEE7RunImplIS6_NSt4__Cr5tupleIJS8_EEEJLm0EEEEiOT_OT0_NSD_16integer_sequenceImJXspT1_EEEE
#36 0x7fce443c5ca7 base::internal::Invoker<>::RunOnce()
#37 0x7fce447b0887 _ZNO4base12OnceCallbackIFivEE3RunEv
#38 0x7fce45621949 content::StartupTaskRunner::RunAllTasksNow()
#39 0x7fce443b90ab content::BrowserMainLoop::CreateStartupTasks()
#40 0x7fce443c9c3f content::BrowserMainRunnerImpl::Initialize()
#41 0x7fce443b6064 content::BrowserMain()
#42 0x7fce466c0324 content::RunBrowserProcessMain()
#43 0x7fce466c19b1 content::ContentMainRunnerImpl::RunServiceManager()
#44 0x7fce466c1266 content::ContentMainRunnerImpl::Run()
#45 0x7fce466bd7f7 content::RunContentProcess()
#46 0x7fce466be200 content::ContentMain()
#47 0x55c61106025d ChromeMain
#48 0x55c6110600f2 main
#49 0x7fce15cf4b97 __libc_start_main
#50 0x55c61105ffea _start
  r8: 0000000000000000  r9: 00007fff790e5020 r10: 0000000000000008 r11: 0000000000000246
 r12: 0000000000000001 r13: 0000000000000000 r14: 0000000000000001 r15: 0000000000000000
  di: 0000000000000002  si: 00007fff790e5020  bp: 00007fff790e5270  bx: 0000000000000000
  dx: 0000000000000000  ax: 0000000000000000  cx: 00007fce15d11f47  sp: 00007fff790e5020
  ip: 00007fce15d11f47 efl: 0000000000000246 cgf: 002b000000000033 erf: 0000000000000000
 trp: 0000000000000000 msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.
zsh: exit 1     out/Default/chrome
```
</div>
</details>


## バグの修正
これらを実行するとタブの追加時に**確率的**にタブが見えなくなることがわかります．確率的と書いたのは同一の操作をしてもタブが見える場合があり，確実に見えなくなる操作というのが見つけられなかったためです．
またこれの他に真ん中の方にあるタブを消すと思うような動作をしてくれないというのもあります．
これらを修正していこうと思います．
まず見えなくなる方についてです．

### どんなことが起こっているのか
まず現象の確認から行います．確率的とは言ってもある程度のパターンがあり，active_tabの近傍でのみ起こるとわかります．
通常の速度でタブを開いた場合は，1個のタブが見えなくなることしか起きない．しかし連続で素早くタブを開いた際にはかなり多くのタブが見えなくなることがわかります．

### バグ1について

### 原因推定(1)
次にこれが起きた原因を考えていきます．
連続で開いたときは処理同士がぶつかり，不正な書き換えが起こってしまったことが原因ではないかと考えました．しかし，ゆっくり動作した場合でも同様の現象がみられるためその線は薄いとも考えました．これを実験するためにgdbで遡った関数の合間にsleep関数を挟んだり，変えるタブの計算を非常に単純化し，計算時間を相対的に0に近似できるような工夫をしました．しかし改善はみられませんでした．

### 原因推定(2)
次に変えた作業に関する既存の関数への影響を考えました．
前までは，タブのサイズの変更というのはavailable_widthが変わった場合や
個々の関数がうまくいっていることから，Tabstripの下位モジュールがうまくいっていないのではないと判断できます．
そのため今までの関数を呼び出している場所を調べてみると，Tabstrip.ccから呼ばれていることがわかります．
その他のTabstrip関連ファイルもこのファイルから呼ばれているため，tab関連の操作を司ると考えられます．
そこでTabstrip.hからコメントを読み，どの関数が怪しそうかを考えていきます．
するとSetTabSlotVisibilityというのが見つかると思います．これをgdbの実行最中に実行してみると，確かに全てのタブが見えるようになります．
つまりこれを持った関数がこれを呼び出されないことによって起きていると考えるのが妥当です．大きさを変えたことで何らかの必要な処理が必要になったのだと考えられます．
これを参照しているDrag関係のファイルというのは関係ないと思われるので，その他4つのファイルを見ると，4つのファイルは
Layout->CompleteAnimationAndLayout->SwapLayoutIfNecessary
の順で互いに参照しあっていることがわかります．
すなわち一番上のLayoutがCompleteAnimationAndLayout及びSetTabSlotVisibilityを呼び出していないことが問題なのではないかとわかります．

よってLayoutが実行する条件を見てみると，

```cpp

void TabStrip::Layout() {
  if (IsAnimating()) {
    // Hide tabs that have animated at least partially out of the clip region.
    SetTabSlotVisibility();
    return;
  }

  if (base::FeatureList::IsEnabled(features::kScrollableTabStrip)) {
    // With tab scrolling, the tabstrip is solely responsible for its own
    // width.
    // It should never be larger than its preferred width.
    const int max_width =
        CalculatePreferredWidthForTabs() + GetRightSideReservedWidth();
    // It should never be smaller than its minimum width.
    const int min_width = GetMinimumSize().width();
    // If it can, it should fit within the tab strip region.
    const int available_width = available_width_callback_.Run();
    // It should be as wide as possible subject to the above constraints.
    const int width = std::min(max_width, std::max(min_width, available_width));
    SetBounds(0, 0, width, GetLayoutConstant(TAB_HEIGHT));
    SetTabSlotVisibility();
  }

  if (tab_search_button_ &&
      !tab_controls_container_->Contains(tab_search_button_)) {
    auto preferred_size = tab_search_button_->GetPreferredSize();
    tab_search_button_->SetBoundsRect(
        gfx::Rect(width() - preferred_size.width(), 0, preferred_size.width(),
                  preferred_size.height()));
  }

  // Only do a layout if our size or the available width changed.
  const int available_width = GetAvailableWidthForTabStrip();
  if (last_layout_size_ == size() && last_available_width_ == available_width)
    return;
  if (drag_context_->IsDragSessionActive())
    return;
  CompleteAnimationAndLayout();
}
```
一番下が問題なのではないかと考えられます．
我々は動く時などでもタブの大きさをを変えているのでこの条件は不適切ではないかと考えました．したがってこの2つのif文の前にCompleteAnimationAndLayout()を持ってくると，実際にうまくいきます．


### バグ2について

バグ1で入れたCompleteAnimationAndLayout()によって直ってしまいました．このことからCompleteAnimationAndLayout()を見てみると

```cpp
void TabStrip::CompleteAnimationAndLayout() {
  last_available_width_ = GetAvailableWidthForTabStrip();
  last_layout_size_ = size();

  bounds_animator_.Cancel();

  SwapLayoutIfNecessary();
  if (touch_layout_)
    touch_layout_->SetWidth(GetTabAreaWidth());

  UpdateIdealBounds();
  SnapToIdealBounds();

  SetTabSlotVisibility();
  SchedulePaint();
}
```
とありUpdateIdealBoundsが呼ばれていることがわかります．このことでちゃんとした大きさに変わったのだと考えられます．

### 実は
こんな感じで論理的に見つけられたらカッコ良かったのですが，実は嘘で一回の授業分使って結構偶然見つかりました()しかも見つけたのは僕じゃない()
僕は上位階層に昇りすぎて抽象的な関数をひたすら眺めてワカンねーって感じでいました．これはあのときこうやれば良かったなーと思った感じで書いています．
まあそんなもんですw



## ひとまず完了(？)
致命的なバグが治ったためひとまず完了しましたがchromiumにはタブグループ，固定タブというものが存在し，それにはうまく対応できていません．
まだ終了まで余裕があったため，そのバグを直したり，ショートカットでタブの変更をするのかを変えたり，現在の拡張をしたりを班員で分担して行うことにしました．


## つづき
この変更をショートカットキーを用いて実現します．
[chromiumのタブをいじる(2)](https://qiita.com/Hideyu/items/4a02531b7f7ce871fbcd)

この機能を拡張し直近でactiveになったタブをn個保存しておきそれを大きくします．
[chromiumのタブをいじる(3)](https://qiita.com/momorrow/items/dc2a1611dfcbea51e56b)

ここで問題になったタブグループと固定タブのバグを直します．
[chromiumのタブをいじる(4)](https://qiita.com/kons_9/items/8e972449dbc9efa6d35f)