12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2013 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/ash/tab_scrubber.h"
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/shell.h"
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ash/wm/window_util.h"
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/metrics/histogram.h"
107dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "chrome/browser/chrome_notification_types.h"
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser.h"
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/browser_finder.h"
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/tabs/tab_strip_model.h"
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/frame/browser_view.h"
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/ui/views/tabs/tab_strip.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/common/pref_names.h"
182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_service.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "content/public/browser/notification_source.h"
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/aura/window.h"
21d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event.h"
22d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "ui/events/event_utils.h"
2368043e1e95eeb07d5cae7aca370b26518b0867d6Torne (Richard Coles)#include "ui/events/gestures/gesture_configuration.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "ui/views/controls/glow_hover_controller.h"
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace {
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)const int64 kActivationDelayMS = 200;
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TabScrubber* TabScrubber::GetInstance() {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static TabScrubber* instance = NULL;
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!instance)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    instance = new TabScrubber();
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return instance;
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// static
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)gfx::Point TabScrubber::GetStartPoint(
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStrip* tab_strip,
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    int index,
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabScrubber::Direction direction) {
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int initial_tab_offset = Tab::GetMiniWidth() / 2;
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Rect tab_bounds = tab_strip->tab_at(index)->bounds();
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  float x = direction == LEFT ?
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      tab_bounds.x() + initial_tab_offset :
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          tab_bounds.right() - initial_tab_offset;
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return gfx::Point(x, tab_bounds.CenterPoint().y());
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TabScrubber::IsActivationPending() {
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return activate_timer_.IsRunning();
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TabScrubber::TabScrubber()
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    : scrubbing_(false),
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser_(NULL),
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      swipe_x_(-1),
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      swipe_y_(-1),
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      swipe_direction_(LEFT),
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      highlighted_tab_(-1),
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      activate_timer_(true, false),
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      activation_delay_(kActivationDelayMS),
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      use_default_activation_delay_(true),
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      weak_ptr_factory_(this) {
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ash::Shell::GetInstance()->AddPreTargetHandler(this);
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  registrar_.Add(
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      this,
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome::NOTIFICATION_BROWSER_CLOSED,
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      content::NotificationService::AllSources());
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)TabScrubber::~TabScrubber() {
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Note: The weak_ptr_factory_ should invalidate  its weak pointers before
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // any other members are destroyed.
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  weak_ptr_factory_.InvalidateWeakPtrs();
772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::OnScrollEvent(ui::ScrollEvent* event) {
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event->type() == ui::ET_SCROLL_FLING_CANCEL ||
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      event->type() == ui::ET_SCROLL_FLING_START) {
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FinishScrub(true);
832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    immersive_reveal_lock_.reset();
842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (event->finger_count() != 3)
882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Browser* browser = GetActiveBrowser();
912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!browser || (scrubbing_ && browser_ && browser != browser_) ||
922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (highlighted_tab_ != -1 &&
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          highlighted_tab_ >= browser->tab_strip_model()->count())) {
942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FinishScrub(false);
952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BrowserView* browser_view =
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      BrowserView::GetBrowserViewForNativeWindow(
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          browser->window()->GetNativeWindow());
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  TabStrip* tab_strip = browser_view->tabstrip();
1022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (tab_strip->IsAnimating()) {
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FinishScrub(false);
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We are handling the event.
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  event->StopPropagation();
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  float x_offset = event->x_offset();
1122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int last_tab_index = highlighted_tab_ == -1 ?
1132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser->tab_strip_model()->active_index() : highlighted_tab_;
1142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!scrubbing_) {
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_direction_ = (x_offset < 0) ? LEFT : RIGHT;
1162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const gfx::Point start_point =
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        GetStartPoint(tab_strip,
1182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      browser->tab_strip_model()->active_index(),
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                      swipe_direction_);
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    browser_ = browser;
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scrubbing_ = true;
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_x_ = start_point.x();
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_y_ = start_point.y();
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ImmersiveModeController* immersive_controller =
1262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        browser_view->immersive_mode_controller();
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    if (immersive_controller->IsEnabled()) {
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      immersive_reveal_lock_.reset(immersive_controller->GetRevealedLock(
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          ImmersiveModeController::ANIMATE_REVEAL_YES));
130c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    }
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->AddObserver(this);
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  } else if (highlighted_tab_ == -1) {
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    Direction direction = (x_offset < 0) ? LEFT : RIGHT;
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (direction != swipe_direction_) {
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      const gfx::Point start_point =
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          GetStartPoint(tab_strip,
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        browser->tab_strip_model()->active_index(),
1382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                        direction);
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      swipe_x_ = start_point.x();
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      swipe_y_ = start_point.y();
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      swipe_direction_ = direction;
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  swipe_x_ += x_offset;
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* first_tab = tab_strip->tab_at(0);
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int first_tab_center = first_tab->bounds().CenterPoint().x();
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* last_tab = tab_strip->tab_at(tab_strip->tab_count() - 1);
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int last_tab_tab_center = last_tab->bounds().CenterPoint().x();
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (swipe_x_ < first_tab_center)
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_x_ = first_tab_center;
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (swipe_x_ > last_tab_tab_center)
1532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_x_ = last_tab_tab_center;
1542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* initial_tab = tab_strip->tab_at(last_tab_index);
1562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  gfx::Point tab_point(swipe_x_, swipe_y_);
1572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  views::View::ConvertPointToTarget(tab_strip, initial_tab, &tab_point);
1582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Tab* new_tab = tab_strip->GetTabAt(initial_tab, tab_point);
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!new_tab)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int new_index = tab_strip->GetModelIndexOfTab(new_tab);
1632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (highlighted_tab_ == -1 &&
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new_index == browser->tab_strip_model()->active_index())
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_index != highlighted_tab_) {
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (activate_timer_.IsRunning()) {
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      activate_timer_.Reset();
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int delay = use_default_activation_delay_ ?
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          ui::GestureConfiguration::tab_scrub_activation_delay_in_ms() :
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          activation_delay_;
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (delay >= 0) {
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        activate_timer_.Start(FROM_HERE,
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              base::TimeDelta::FromMilliseconds(delay),
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                              base::Bind(&TabScrubber::FinishScrub,
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         weak_ptr_factory_.GetWeakPtr(),
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                         true));
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (highlighted_tab_ != -1) {
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Tab* tab = tab_strip->tab_at(highlighted_tab_);
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      tab->hover_controller()->HideImmediately();
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (new_index == browser->tab_strip_model()->active_index()) {
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      highlighted_tab_ = -1;
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      highlighted_tab_ = new_index;
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new_tab->hover_controller()->Show(views::GlowHoverController::PRONOUNCED);
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (highlighted_tab_ != -1) {
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    gfx::Point hover_point(swipe_x_, swipe_y_);
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    views::View::ConvertPointToTarget(tab_strip, new_tab, &hover_point);
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    new_tab->hover_controller()->SetLocation(hover_point);
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::Observe(int type,
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          const content::NotificationSource& source,
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                          const content::NotificationDetails& details) {
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (content::Source<Browser>(source).ptr() == browser_) {
2042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    activate_timer_.Stop();
2052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_x_ = -1;
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    swipe_y_ = -1;
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scrubbing_ = false;
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    highlighted_tab_ = -1;
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    browser_ = NULL;
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::TabStripAddedTabAt(TabStrip* tab_strip, int index) {
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (highlighted_tab_ == -1)
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (index < highlighted_tab_)
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ++highlighted_tab_;
2192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::TabStripMovedTab(TabStrip* tab_strip,
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   int from_index,
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   int to_index) {
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (highlighted_tab_ == -1)
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (from_index == highlighted_tab_)
2282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    highlighted_tab_ = to_index;
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else if (from_index < highlighted_tab_&& highlighted_tab_<= to_index)
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    --highlighted_tab_;
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  else if (from_index > highlighted_tab_ && highlighted_tab_ >= to_index)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    ++highlighted_tab_;
2332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::TabStripRemovedTabAt(TabStrip* tab_strip, int index) {
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (highlighted_tab_ == -1)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (index == highlighted_tab_) {
2392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    FinishScrub(false);
2402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (index < highlighted_tab_)
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    --highlighted_tab_;
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::TabStripDeleted(TabStrip* tab_strip) {
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (highlighted_tab_ == -1)
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return;
2492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)Browser* TabScrubber::GetActiveBrowser() {
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  aura::Window* active_window = ash::wm::GetActiveWindow();
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!active_window)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  Browser* browser = chrome::FindBrowserWithWindow(active_window);
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!browser || browser->type() != Browser::TYPE_TABBED)
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return NULL;
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return browser;
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void TabScrubber::FinishScrub(bool activate) {
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  activate_timer_.Stop();
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (browser_ && browser_->window()) {
2672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    BrowserView* browser_view =
2682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        BrowserView::GetBrowserViewForNativeWindow(
2692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            browser_->window()->GetNativeWindow());
2702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    TabStrip* tab_strip = browser_view->tabstrip();
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (activate && highlighted_tab_ != -1) {
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Tab* tab = tab_strip->tab_at(highlighted_tab_);
2732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      tab->hover_controller()->HideImmediately();
2742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      int distance =
2752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          std::abs(
2762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)              highlighted_tab_ - browser_->tab_strip_model()->active_index());
2771320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      UMA_HISTOGRAM_CUSTOM_COUNTS("Tabs.ScrubDistance", distance, 0, 20, 21);
2782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      browser_->tab_strip_model()->ActivateTabAt(highlighted_tab_, true);
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    tab_strip->RemoveObserver(this);
2812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  swipe_x_ = -1;
2832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  swipe_y_ = -1;
2842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scrubbing_ = false;
2852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  highlighted_tab_ = -1;
2862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
287