# Tab Scrubber Tab scrubbing is a feature on ChromeOS where you can switch the tab by touch pad. Swipe on touch pad with 3-fingers, then you can see the tab switches to the direction you swiped. [TabScrubberChromeOS](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/tabs/tab_scrubber_chromeos.h;l=30;drc=6cf91926d52f754a64421f8ac4348559a43e07bf) is a class which is responsible for handling the scrub behavior. ## Scroll event 3-finger swipe is one of the [ScrollEvent](https://source.chromium.org/chromium/chromium/src/+/main:ui/events/event.h;l=998;drc=f85e2931070f20b81171b91298bda280ca337a09). ScrollEvent can specifies `finger_count_`. As for the tab scrubbing, we observe [kFingerCount = 3](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc;l=134;drc=6cf91926d52f754a64421f8ac4348559a43e07bf) gesture only except for FlingScrollEvent. [OnScrollEvent](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc;l=110;drc=6cf91926d52f754a64421f8ac4348559a43e07bf) will be notified when any of the scroll event is dispatched. Scrub will start with 3-finger ScrollEvent and finish at FlingScrollEvent. On Ash side, it will determine whether we should scrub Ash window or Lacros window. [MaybeDelegateHandlingToLacros](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc;l=377;drc=f85e2931070f20b81171b91298bda280ca337a09) checks whether there is an active Lacros window. If so, it will send tab scrubbing event to Lacros via crosapi. ### Input event via crosapi Generally, input event should be sent via wayland, not crosapi. As for tab scrubbing, it's difficult to send event using wayland now since we need to dispatch the event even when the window is not activated while wayland does not support such usage. The correct thing to do should be to add such wayland extension, but for now we are using crosapi. [BrowserManager::HandleTabScrubbing](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ash/crosapi/browser_manager.cc;l=518;drc=f85e2931070f20b81171b91298bda280ca337a09) is called to performe the action via crosapi. ## How to judge Lacros should scrub on Ash side? [BeginScrub](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc;l=245;drc=f85e2931070f20b81171b91298bda280ca337a09) alwasy starts scrubbing and [FinihsScrub](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/tabs/tab_scrubber_chromeos.cc;l=268;drc=f85e2931070f20b81171b91298bda280ca337a09) ends scrubbing when it's scrubbingh. On Ash side, if `scrubbing_` is true, then it is scrubbing so it's easy to judge whether we should finish it. However, whether Lacros is scrubbing is not observable from Ash currrenly because crosapi is set regardless of the condition to start scrubbing. There are 2 logics to finish scrubbing other than fling scroll event. ```cpp= // If browser instance is invalid or hightlighted tab is invalid, finish scrubbing. if (!browser || (scrubbing_ && browser_ && browser != browser_) || (highlighted_tab_ != -1 && highlighted_tab_ >= browser->tab_strip_model()->count())) { if (FinishScrub(false)) { event->SetHandled(); } return; } // If tab is animating, stop scrubbing. if (tab_strip->IsAnimating()) { if (FinishScrub(false)) { event->SetHandled(); } return; } ``` If Ash can know the tabs on Lacros, then we can use the same logic.