| Index: ui/views/view.cc | 
| diff --git a/ui/views/view.cc b/ui/views/view.cc | 
| index 19b9a6d98a5fa61200ea4ee55a8e46281f1d464c..5ca36b8412cb4d84afb27ee30184d7a342089da1 100644 | 
| --- a/ui/views/view.cc | 
| +++ b/ui/views/view.cc | 
| @@ -845,36 +845,45 @@ void View::SchedulePaintInRect(const gfx::Rect& rect) { | 
| } | 
| } | 
|  | 
| -void View::Paint(const ui::PaintContext& parent_context) { | 
| +void View::Paint(const PaintInfo& parent_paint_info) { | 
| if (!ShouldPaint()) | 
| return; | 
|  | 
| -  ui::PaintContext context(parent_context, GetPaintContextOffset()); | 
| +  const gfx::Rect& parent_bounds = !parent() | 
| +                                       ? GetPaintRecordingBounds() | 
| +                                       : parent()->GetPaintRecordingBounds(); | 
|  | 
| +  PaintInfo paint_info(parent_paint_info, GetPaintRecordingBounds(), | 
| +                       parent_bounds, GetPaintScaleType()); | 
| + | 
| +  const ui::PaintContext& context = paint_info.context(); | 
| bool is_invalidated = true; | 
| -  if (context.CanCheckInvalid()) { | 
| +  if (paint_info.context().CanCheckInvalid()) { | 
| #if DCHECK_IS_ON() | 
| -    gfx::Vector2d offset; | 
| -    context.Visited(this); | 
| -    View* view = this; | 
| -    while (view->parent() && !view->layer()) { | 
| -      DCHECK(view->GetTransform().IsIdentity()); | 
| -      offset += view->GetMirroredPosition().OffsetFromOrigin(); | 
| -      view = view->parent(); | 
| +    if (!context.is_pixel_canvas()) { | 
| +      gfx::Vector2d offset; | 
| +      context.Visited(this); | 
| +      View* view = this; | 
| +      while (view->parent() && !view->layer()) { | 
| +        DCHECK(view->GetTransform().IsIdentity()); | 
| +        offset += view->GetMirroredPosition().OffsetFromOrigin(); | 
| +        view = view->parent(); | 
| +      } | 
| +      // The offset in the PaintContext should be the offset up to the paint | 
| +      // root, which we compute and verify here. | 
| +      DCHECK_EQ(context.PaintOffset().x(), offset.x()); | 
| +      DCHECK_EQ(context.PaintOffset().y(), offset.y()); | 
| +      // The above loop will stop when |view| is the paint root, which should be | 
| +      // the root of the current paint walk, as verified by storing the root in | 
| +      // the PaintContext. | 
| +      DCHECK_EQ(context.RootVisited(), view); | 
| } | 
| -    // The offset in the PaintContext should be the offset up to the paint root, | 
| -    // which we compute and verify here. | 
| -    DCHECK_EQ(context.PaintOffset().x(), offset.x()); | 
| -    DCHECK_EQ(context.PaintOffset().y(), offset.y()); | 
| -    // The above loop will stop when |view| is the paint root, which should be | 
| -    // the root of the current paint walk, as verified by storing the root in | 
| -    // the PaintContext. | 
| -    DCHECK_EQ(context.RootVisited(), view); | 
| #endif | 
|  | 
| // If the View wasn't invalidated, don't waste time painting it, the output | 
| // would be culled. | 
| -    is_invalidated = context.IsRectInvalid(GetLocalBounds()); | 
| +    is_invalidated = | 
| +        context.IsRectInvalid(gfx::Rect(paint_info.paint_recording_size())); | 
| } | 
|  | 
| TRACE_EVENT1("views", "View::Paint", "class", GetClassName()); | 
| @@ -883,29 +892,43 @@ void View::Paint(const ui::PaintContext& parent_context) { | 
| // rather than relative to its parent. | 
| // TODO(danakj): Rework clip and transform recorder usage here to use | 
| // std::optional once we can do so. | 
| -  ui::ClipRecorder clip_recorder(parent_context); | 
| +  ui::ClipRecorder clip_recorder(parent_paint_info.context()); | 
| if (!layer()) { | 
| // Set the clip rect to the bounds of this View, or |clip_path_| if it's | 
| // been set. Note that the X (or left) position we pass to ClipRect takes | 
| // into consideration whether or not the View uses a right-to-left layout so | 
| // that we paint the View in its mirrored position if need be. | 
| if (clip_path_.isEmpty()) { | 
| -      clip_recorder.ClipRect(GetMirroredBounds()); | 
| +      clip_recorder.ClipRect(gfx::Rect(paint_info.paint_recording_size()) + | 
| +                             paint_info.offset_from_parent()); | 
| } else { | 
| gfx::Path clip_path_in_parent = clip_path_; | 
| -      clip_path_in_parent.offset(GetMirroredX(), y()); | 
| + | 
| +      // Transform |clip_path_| from local space to parent recording space. | 
| +      gfx::Transform to_parent_recording_space; | 
| + | 
| +      gfx::PointF recording_scale = paint_info.paint_recording_scale(); | 
| +      to_parent_recording_space.Scale(SkFloatToScalar(recording_scale.x()), | 
| +                                      SkFloatToScalar(recording_scale.y())); | 
| +      to_parent_recording_space.Translate(paint_info.offset_from_parent()); | 
| + | 
| +      clip_path_in_parent.transform(to_parent_recording_space.matrix()); | 
| clip_recorder.ClipPathWithAntiAliasing(clip_path_in_parent); | 
| } | 
| } | 
|  | 
| ui::TransformRecorder transform_recorder(context); | 
| -  SetupTransformRecorderForPainting(&transform_recorder); | 
| +  SetupTransformRecorderForPainting(&transform_recorder, | 
| +                                    paint_info.offset_from_parent()); | 
|  | 
| // Note that the cache is not aware of the offset of the view | 
| // relative to the parent since painting is always done relative to | 
| // the top left of the individual view. | 
| -  if (is_invalidated || !paint_cache_.UseCache(context, size())) { | 
| -    ui::PaintRecorder recorder(context, size(), &paint_cache_); | 
| +  if (is_invalidated || | 
| +      !paint_cache_.UseCache(context, paint_info.paint_recording_size())) { | 
| +    ui::PaintRecorder recorder(context, paint_info.paint_recording_size(), | 
| +                               paint_info.paint_recording_scale(), | 
| +                               &paint_cache_); | 
| gfx::Canvas* canvas = recorder.canvas(); | 
| gfx::ScopedRTLFlipCanvas scoped_canvas(canvas, width(), | 
| flip_canvas_on_paint_for_rtl_ui_); | 
| @@ -915,7 +938,7 @@ void View::Paint(const ui::PaintContext& parent_context) { | 
| } | 
|  | 
| // View::Paint() recursion over the subtree. | 
| -  PaintChildren(context); | 
| +  PaintChildren(paint_info); | 
| } | 
|  | 
| void View::SetBackground(std::unique_ptr<Background> b) { | 
| @@ -1543,9 +1566,9 @@ void View::RemovedFromWidget() {} | 
|  | 
| // Painting -------------------------------------------------------------------- | 
|  | 
| -void View::PaintChildren(const ui::PaintContext& context) { | 
| +void View::PaintChildren(const PaintInfo& paint_info) { | 
| TRACE_EVENT1("views", "View::PaintChildren", "class", GetClassName()); | 
| -  RecursivePaintHelper(&View::Paint, context); | 
| +  RecursivePaintHelper(&View::Paint, paint_info); | 
| } | 
|  | 
| void View::OnPaint(gfx::Canvas* canvas) { | 
| @@ -1817,6 +1840,10 @@ int View::GetVerticalDragThreshold() { | 
| return kDefaultVerticalDragThreshold; | 
| } | 
|  | 
| +PaintInfo::ScaleType View::GetPaintScaleType() const { | 
| +  return PaintInfo::ScaleType::kScaleToFit; | 
| +} | 
| + | 
| // Debugging ------------------------------------------------------------------- | 
|  | 
| #if !defined(NDEBUG) | 
| @@ -1969,15 +1996,17 @@ bool View::ShouldPaint() const { | 
| return visible_ && !size().IsEmpty(); | 
| } | 
|  | 
| -gfx::Vector2d View::GetPaintContextOffset() const { | 
| -  // If the View has a layer() then it is a paint root. Otherwise, we need to | 
| -  // add the offset from the parent into the total offset from the paint root. | 
| +gfx::Rect View::GetPaintRecordingBounds() const { | 
| +  // If the View has a layer() then it is a paint root and no offset information | 
| +  // is needed. Otherwise, we need bounds that includes an offset from the | 
| +  // parent to add to the total offset from the paint root. | 
| DCHECK(layer() || parent() || origin() == gfx::Point()); | 
| -  return layer() ? gfx::Vector2d() : GetMirroredPosition().OffsetFromOrigin(); | 
| +  return layer() ? GetLocalBounds() : GetMirroredBounds(); | 
| } | 
|  | 
| void View::SetupTransformRecorderForPainting( | 
| -    ui::TransformRecorder* recorder) const { | 
| +    ui::TransformRecorder* recorder, | 
| +    const gfx::Vector2d& offset_from_parent) const { | 
| // If the view is backed by a layer, it should paint with itself as the origin | 
| // rather than relative to its parent. | 
| if (layer()) | 
| @@ -1986,42 +2015,50 @@ void View::SetupTransformRecorderForPainting( | 
| // Translate the graphics such that 0,0 corresponds to where this View is | 
| // located relative to its parent. | 
| gfx::Transform transform_from_parent; | 
| -  gfx::Vector2d offset_from_parent = GetMirroredPosition().OffsetFromOrigin(); | 
| transform_from_parent.Translate(offset_from_parent.x(), | 
| offset_from_parent.y()); | 
| -  transform_from_parent.PreconcatTransform(GetTransform()); | 
| recorder->Transform(transform_from_parent); | 
| } | 
|  | 
| -void View::RecursivePaintHelper(void (View::*func)(const ui::PaintContext&), | 
| -                                const ui::PaintContext& context) { | 
| +void View::RecursivePaintHelper(void (View::*func)(const PaintInfo&), | 
| +                                const PaintInfo& info) { | 
| View::Views children = GetChildrenInZOrder(); | 
| DCHECK_EQ(child_count(), static_cast<int>(children.size())); | 
| for (auto* child : children) { | 
| if (!child->layer()) | 
| -      (child->*func)(context); | 
| +      (child->*func)(info); | 
| } | 
| } | 
|  | 
| void View::PaintFromPaintRoot(const ui::PaintContext& parent_context) { | 
| -  Paint(parent_context); | 
| +  PaintInfo paint_info(parent_context, layer() ? layer()->size() : size()); | 
| +  Paint(paint_info); | 
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( | 
| switches::kDrawViewBoundsRects)) | 
| -    PaintDebugRects(parent_context); | 
| +    PaintDebugRects(paint_info); | 
| } | 
|  | 
| -void View::PaintDebugRects(const ui::PaintContext& parent_context) { | 
| +void View::PaintDebugRects(const PaintInfo& parent_paint_info) { | 
| if (!ShouldPaint()) | 
| return; | 
|  | 
| -  ui::PaintContext context(parent_context, GetPaintContextOffset()); | 
| +  const gfx::Rect& parent_bounds = (layer() || !parent()) | 
| +                                       ? GetPaintRecordingBounds() | 
| +                                       : parent()->GetPaintRecordingBounds(); | 
| +  PaintInfo paint_info(parent_paint_info, GetPaintRecordingBounds(), | 
| +                       parent_bounds, GetPaintScaleType()); | 
| + | 
| +  const ui::PaintContext& context = paint_info.context(); | 
| + | 
| ui::TransformRecorder transform_recorder(context); | 
| -  SetupTransformRecorderForPainting(&transform_recorder); | 
| +  SetupTransformRecorderForPainting(&transform_recorder, | 
| +                                    paint_info.offset_from_parent()); | 
|  | 
| -  RecursivePaintHelper(&View::PaintDebugRects, context); | 
| +  RecursivePaintHelper(&View::PaintDebugRects, paint_info); | 
|  | 
| // Draw outline rects for debugging. | 
| -  ui::PaintRecorder recorder(context, size()); | 
| +  ui::PaintRecorder recorder(context, paint_info.paint_recording_size(), | 
| +                             paint_info.paint_recording_scale(), &paint_cache_); | 
| gfx::Canvas* canvas = recorder.canvas(); | 
| const float scale = canvas->UndoDeviceScaleFactor(); | 
| gfx::RectF outline_rect(ScaleToEnclosedRect(GetLocalBounds(), scale)); | 
|  |