| Index: ui/events/devices/x11/device_data_manager_x11.cc
 | 
| diff --git a/ui/events/devices/x11/device_data_manager_x11.cc b/ui/events/devices/x11/device_data_manager_x11.cc
 | 
| index e9769f5d14934a14d5be70eabd6593f9dc2ac58b..58eded9ba91215fb72e16ecdd857102538e66c93 100644
 | 
| --- a/ui/events/devices/x11/device_data_manager_x11.cc
 | 
| +++ b/ui/events/devices/x11/device_data_manager_x11.cc
 | 
| @@ -218,6 +218,7 @@ bool DeviceDataManagerX11::IsXInput2Available() const {
 | 
|  void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
 | 
|    cmt_devices_.reset();
 | 
|    touchpads_.reset();
 | 
| +  scrollclass_devices_.reset();
 | 
|    master_pointers_.clear();
 | 
|    for (int i = 0; i < kMaxDeviceNum; ++i) {
 | 
|      valuator_count_[i] = 0;
 | 
| @@ -225,6 +226,10 @@ void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
 | 
|      data_type_lookup_[i].clear();
 | 
|      valuator_min_[i].clear();
 | 
|      valuator_max_[i].clear();
 | 
| +    scroll_data_[i].horizontal.number = -1;
 | 
| +    scroll_data_[i].horizontal.seen = false;
 | 
| +    scroll_data_[i].vertical.number = -1;
 | 
| +    scroll_data_[i].vertical.seen = false;
 | 
|      for (int j = 0; j < kMaxSlotNum; j++)
 | 
|        last_seen_valuator_[i][j].clear();
 | 
|    }
 | 
| @@ -280,21 +285,14 @@ void DeviceDataManagerX11::UpdateDeviceList(Display* display) {
 | 
|      for (int j = 0; j < kMaxSlotNum; j++)
 | 
|        last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0);
 | 
|      for (int j = 0; j < info.num_classes; ++j) {
 | 
| -      if (info.classes[j]->type != XIValuatorClass)
 | 
| -        continue;
 | 
| -
 | 
| -      XIValuatorClassInfo* v =
 | 
| -          reinterpret_cast<XIValuatorClassInfo*>(info.classes[j]);
 | 
| -      for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type) {
 | 
| -        if (v->label == atoms[data_type]) {
 | 
| -          valuator_lookup_[deviceid][data_type] = v->number;
 | 
| -          data_type_lookup_[deviceid][v->number] = data_type;
 | 
| -          valuator_min_[deviceid][data_type] = v->min;
 | 
| -          valuator_max_[deviceid][data_type] = v->max;
 | 
| -          if (IsCMTDataType(data_type))
 | 
| -            possible_cmt = true;
 | 
| -          break;
 | 
| -        }
 | 
| +      if (info.classes[j]->type == XIValuatorClass) {
 | 
| +        if (UpdateValuatorClassDevice(
 | 
| +                reinterpret_cast<XIValuatorClassInfo*>(info.classes[j]), atoms,
 | 
| +                deviceid))
 | 
| +          possible_cmt = true;
 | 
| +      } else if (info.classes[j]->type == XIScrollClass) {
 | 
| +        UpdateScrollClassDevice(
 | 
| +            reinterpret_cast<XIScrollClassInfo*>(info.classes[j]), deviceid);
 | 
|        }
 | 
|      }
 | 
|  
 | 
| @@ -425,6 +423,42 @@ bool DeviceDataManagerX11::IsCMTDeviceEvent(
 | 
|    return cmt_devices_[xievent->sourceid];
 | 
|  }
 | 
|  
 | 
| +int DeviceDataManagerX11::GetScrollClassEventDetail(
 | 
| +    const base::NativeEvent& native_event) const {
 | 
| +  if (native_event->type != GenericEvent)
 | 
| +    return SCROLL_TYPE_NO_SCROLL;
 | 
| +
 | 
| +  XIDeviceEvent* xievent =
 | 
| +      static_cast<XIDeviceEvent*>(native_event->xcookie.data);
 | 
| +  if (xievent->sourceid >= kMaxDeviceNum)
 | 
| +    return SCROLL_TYPE_NO_SCROLL;
 | 
| +  if (!scrollclass_devices_[xievent->sourceid])
 | 
| +    return SCROLL_TYPE_NO_SCROLL;
 | 
| +  int horizontal_id = scroll_data_[xievent->sourceid].horizontal.number;
 | 
| +  int vertical_id = scroll_data_[xievent->sourceid].vertical.number;
 | 
| +  return (XIMaskIsSet(xievent->valuators.mask, horizontal_id)
 | 
| +              ? SCROLL_TYPE_HORIZONTAL
 | 
| +              : 0) |
 | 
| +         (XIMaskIsSet(xievent->valuators.mask, vertical_id)
 | 
| +              ? SCROLL_TYPE_VERTICAL
 | 
| +              : 0);
 | 
| +}
 | 
| +
 | 
| +int DeviceDataManagerX11::GetScrollClassDeviceDetail(
 | 
| +    const base::NativeEvent& native_event) const {
 | 
| +  XEvent& xev = *native_event;
 | 
| +  if (xev.type != GenericEvent)
 | 
| +    return SCROLL_TYPE_NO_SCROLL;
 | 
| +
 | 
| +  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
 | 
| +  if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
 | 
| +    return SCROLL_TYPE_NO_SCROLL;
 | 
| +  const int sourceid = xiev->sourceid;
 | 
| +  const ScrollInfo& device_data = scroll_data_[sourceid];
 | 
| +  return (device_data.vertical.number >= 0 ? SCROLL_TYPE_VERTICAL : 0) |
 | 
| +         (device_data.horizontal.number >= 0 ? SCROLL_TYPE_HORIZONTAL : 0);
 | 
| +}
 | 
| +
 | 
|  bool DeviceDataManagerX11::IsCMTGestureEvent(
 | 
|      const base::NativeEvent& native_event) const {
 | 
|    return (IsScrollEvent(native_event) ||
 | 
| @@ -517,6 +551,49 @@ void DeviceDataManagerX11::GetScrollOffsets(
 | 
|      *finger_count = static_cast<int>(data[DT_CMT_FINGER_COUNT]);
 | 
|  }
 | 
|  
 | 
| +void DeviceDataManagerX11::GetScrollClassOffsets(
 | 
| +    const base::NativeEvent& native_event,
 | 
| +    double* x_offset,
 | 
| +    double* y_offset) {
 | 
| +  DCHECK_NE(SCROLL_TYPE_NO_SCROLL, GetScrollClassDeviceDetail(native_event));
 | 
| +  XEvent& xev = *native_event;
 | 
| +
 | 
| +  *x_offset = 0;
 | 
| +  *y_offset = 0;
 | 
| +
 | 
| +  if (xev.type != GenericEvent)
 | 
| +    return;
 | 
| +
 | 
| +  XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
 | 
| +  if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
 | 
| +    return;
 | 
| +  const int sourceid = xiev->sourceid;
 | 
| +  double* valuators = xiev->valuators.values;
 | 
| +
 | 
| +  ScrollInfo* info = &scroll_data_[sourceid];
 | 
| +
 | 
| +  const int horizontal_number = info->horizontal.number;
 | 
| +  const int vertical_number = info->vertical.number;
 | 
| +
 | 
| +  for (int i = 0; i <= valuator_count_[sourceid]; ++i) {
 | 
| +    if (!XIMaskIsSet(xiev->valuators.mask, i))
 | 
| +      continue;
 | 
| +    if (i == horizontal_number) {
 | 
| +      *x_offset = ExtractAndUpdateScrollOffset(&info->horizontal, *valuators);
 | 
| +    } else if (i == vertical_number) {
 | 
| +      *y_offset = ExtractAndUpdateScrollOffset(&info->vertical, *valuators);
 | 
| +    }
 | 
| +    valuators++;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void DeviceDataManagerX11::InvalidateScrollClasses() {
 | 
| +  for (int i = 0; i < kMaxDeviceNum; i++) {
 | 
| +    scroll_data_[i].horizontal.seen = false;
 | 
| +    scroll_data_[i].vertical.seen = false;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
|  void DeviceDataManagerX11::GetFlingData(
 | 
|      const base::NativeEvent& native_event,
 | 
|      float* vx,
 | 
| @@ -697,6 +774,60 @@ void DeviceDataManagerX11::InitializeValuatorsForTest(int deviceid,
 | 
|    }
 | 
|  }
 | 
|  
 | 
| +bool DeviceDataManagerX11::UpdateValuatorClassDevice(
 | 
| +    XIValuatorClassInfo* valuator_class_info,
 | 
| +    Atom* atoms,
 | 
| +    int deviceid) {
 | 
| +  DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum);
 | 
| +  Atom* label =
 | 
| +      std::find(atoms, atoms + DT_LAST_ENTRY, valuator_class_info->label);
 | 
| +  if (label == atoms + DT_LAST_ENTRY) {
 | 
| +    return false;
 | 
| +  }
 | 
| +  int data_type = label - atoms;
 | 
| +  DCHECK_GE(data_type, 0);
 | 
| +  DCHECK_LT(data_type, DT_LAST_ENTRY);
 | 
| +
 | 
| +  valuator_lookup_[deviceid][data_type] = valuator_class_info->number;
 | 
| +  data_type_lookup_[deviceid][valuator_class_info->number] = data_type;
 | 
| +  valuator_min_[deviceid][data_type] = valuator_class_info->min;
 | 
| +  valuator_max_[deviceid][data_type] = valuator_class_info->max;
 | 
| +  return IsCMTDataType(data_type);
 | 
| +}
 | 
| +
 | 
| +void DeviceDataManagerX11::UpdateScrollClassDevice(
 | 
| +    XIScrollClassInfo* scroll_class_info,
 | 
| +    int deviceid) {
 | 
| +  DCHECK(deviceid >= 0 && deviceid < kMaxDeviceNum);
 | 
| +  ScrollInfo& info = scroll_data_[deviceid];
 | 
| +  switch (scroll_class_info->scroll_type) {
 | 
| +    case XIScrollTypeVertical:
 | 
| +      info.vertical.number = scroll_class_info->number;
 | 
| +      info.vertical.increment = scroll_class_info->increment;
 | 
| +      info.vertical.position = 0;
 | 
| +      info.vertical.seen = false;
 | 
| +      break;
 | 
| +    case XIScrollTypeHorizontal:
 | 
| +      info.horizontal.number = scroll_class_info->number;
 | 
| +      info.horizontal.increment = scroll_class_info->increment;
 | 
| +      info.horizontal.position = 0;
 | 
| +      info.horizontal.seen = false;
 | 
| +      break;
 | 
| +  }
 | 
| +  scrollclass_devices_[deviceid] = true;
 | 
| +}
 | 
| +
 | 
| +double DeviceDataManagerX11::ExtractAndUpdateScrollOffset(
 | 
| +    ScrollInfo::AxisInfo* axis,
 | 
| +    double valuator) const {
 | 
| +  double offset = 0;
 | 
| +  if (axis->seen)
 | 
| +    offset = axis->position - valuator;
 | 
| +  axis->seen = true;
 | 
| +  axis->position = valuator;
 | 
| +  return offset / axis->increment;
 | 
| +}
 | 
| +
 | 
|  bool DeviceDataManagerX11::TouchEventNeedsCalibrate(int touch_device_id) const {
 | 
|  #if defined(OS_CHROMEOS)
 | 
|    if (!base::SysInfo::IsRunningOnChromeOS())
 | 
| 
 |