| Index: chrome/browser/ui/views/tabs/tab_strip.cc | 
| diff --git a/chrome/browser/ui/views/tabs/tab_strip.cc b/chrome/browser/ui/views/tabs/tab_strip.cc | 
| index 564405dfbb3d268910180d3b9847176667414c3b..34f422f8b1236189025e3e56a860ac2d22e5997e 100644 | 
| --- a/chrome/browser/ui/views/tabs/tab_strip.cc | 
| +++ b/chrome/browser/ui/views/tabs/tab_strip.cc | 
| @@ -19,6 +19,7 @@ | 
| #include "chrome/browser/ui/layout_constants.h" | 
| #include "chrome/browser/ui/tabs/tab_strip_model.h" | 
| #include "chrome/browser/ui/view_ids.h" | 
| +#include "chrome/browser/ui/views/frame/browser_view.h" | 
| #include "chrome/browser/ui/views/tabs/stacked_tab_strip_layout.h" | 
| #include "chrome/browser/ui/views/tabs/tab.h" | 
| #include "chrome/browser/ui/views/tabs/tab_drag_controller.h" | 
| @@ -31,11 +32,16 @@ | 
| #include "content/public/browser/user_metrics.h" | 
| #include "content/public/common/content_switches.h" | 
| #include "grit/theme_resources.h" | 
| +#include "third_party/skia/include/core/SkColorFilter.h" | 
| +#include "third_party/skia/include/effects/SkBlurMaskFilter.h" | 
| +#include "third_party/skia/include/effects/SkLayerDrawLooper.h" | 
| +#include "third_party/skia/include/pathops/SkPathOps.h" | 
| #include "ui/accessibility/ax_view_state.h" | 
| #include "ui/base/default_theme_provider.h" | 
| #include "ui/base/dragdrop/drag_drop_types.h" | 
| #include "ui/base/l10n/l10n_util.h" | 
| #include "ui/base/models/list_selection_model.h" | 
| +#include "ui/base/resource/material_design/material_design_controller.h" | 
| #include "ui/base/resource/resource_bundle.h" | 
| #include "ui/compositor/compositing_recorder.h" | 
| #include "ui/compositor/paint_recorder.h" | 
| @@ -126,6 +132,28 @@ int GetNewTabButtonWidth() { | 
| GetLayoutConstant(TABSTRIP_NEW_TAB_BUTTON_OVERLAP); | 
| } | 
|  | 
| +skia::RefPtr<SkDrawLooper> CreateShadowDrawLooper(SkAlpha alpha) { | 
| +  SkLayerDrawLooper::Builder looper_builder; | 
| +  looper_builder.addLayer(); | 
| + | 
| +  SkLayerDrawLooper::LayerInfo layer_info; | 
| +  layer_info.fPaintBits |= SkLayerDrawLooper::kMaskFilter_Bit; | 
| +  layer_info.fPaintBits |= SkLayerDrawLooper::kColorFilter_Bit; | 
| +  layer_info.fColorMode = SkXfermode::kDst_Mode; | 
| +  layer_info.fOffset.set(0, 1); | 
| +  skia::RefPtr<SkMaskFilter> blur_mask = | 
| +      skia::AdoptRef(SkBlurMaskFilter::Create( | 
| +          kNormal_SkBlurStyle, 0.5, SkBlurMaskFilter::kHighQuality_BlurFlag)); | 
| +  skia::RefPtr<SkColorFilter> color_filter = | 
| +      skia::AdoptRef(SkColorFilter::CreateModeFilter( | 
| +          SkColorSetA(SK_ColorBLACK, alpha), SkXfermode::kSrcIn_Mode)); | 
| +  SkPaint* layer_paint = looper_builder.addLayer(layer_info); | 
| +  layer_paint->setMaskFilter(blur_mask.get()); | 
| +  layer_paint->setColorFilter(color_filter.get()); | 
| + | 
| +  return skia::AdoptRef(looper_builder.detachLooper()); | 
| +} | 
| + | 
| // Animation delegate used for any automatic tab movement.  Hides the tab if it | 
| // is not fully visible within the tabstrip area, to prevent overflow clipping. | 
| class TabAnimationDelegate : public gfx::AnimationDelegate { | 
| @@ -267,11 +295,22 @@ class NewTabButton : public views::ImageButton, | 
| // views::MaskedTargeterDelegate: | 
| bool GetHitTestMask(gfx::Path* mask) const override; | 
|  | 
| +  // Computes a path corresponding to the button's outer border for a given | 
| +  // |scale| and stores it in |path|.  |button_y| is used as the y-coordinate | 
| +  // for the top of the button.  If |extend_to_top| is true, the path is | 
| +  // extended vertically to y = 0.  The caller uses this for Fitts' Law purposes | 
| +  // in maximized/fullscreen mode. | 
| +  void GetBorderPath(float button_y, | 
| +                     float scale, | 
| +                     bool extend_to_top, | 
| +                     SkPath* path) const; | 
| + | 
| // Paints the fill region of the button into |canvas|, according to the | 
| -  // supplied values from GetImage(). | 
| +  // supplied values from GetImage() and the given |fill| path. | 
| void PaintFill(bool pressed, | 
| double hover_value, | 
| float scale, | 
| +                 const SkPath& fill, | 
| gfx::Canvas* canvas) const; | 
|  | 
| // Tab strip that contains this button. | 
| @@ -339,32 +378,77 @@ void NewTabButton::OnPaint(gfx::Canvas* canvas) { | 
| hover_value = hover_animation_->GetCurrentValue(); | 
| const float scale = canvas->image_scale(); | 
|  | 
| -  // Fill. | 
| -  gfx::ImageSkia* mask = | 
| -      GetThemeProvider()->GetImageSkiaNamed(IDR_NEWTAB_BUTTON_MASK); | 
| -  // The canvas and mask have to use the same scale factor. | 
| -  const float fill_canvas_scale = mask->HasRepresentation(scale) ? | 
| -      scale : ui::GetScaleForScaleFactor(ui::SCALE_FACTOR_100P); | 
| -  gfx::Canvas fill_canvas(GetNewTabButtonSize(), fill_canvas_scale, false); | 
| -  PaintFill(pressed, hover_value, fill_canvas_scale, &fill_canvas); | 
| -  gfx::ImageSkia image(fill_canvas.ExtractImageRep()); | 
| -  canvas->DrawImageInt( | 
| -      gfx::ImageSkiaOperations::CreateMaskedImage(image, *mask), 0, 0); | 
| - | 
| -  // Stroke.  Draw the button border with a slight alpha. | 
| -  static const SkAlpha kGlassFrameOverlayAlpha = 178; | 
| -  static const SkAlpha kOpaqueFrameOverlayAlpha = 230; | 
| -  const SkAlpha alpha = GetWidget()->ShouldWindowContentsBeTransparent() ? | 
| -      kGlassFrameOverlayAlpha : kOpaqueFrameOverlayAlpha; | 
| -  const int overlay_id = pressed ? IDR_NEWTAB_BUTTON_P : IDR_NEWTAB_BUTTON; | 
| -  canvas->DrawImageInt(*GetThemeProvider()->GetImageSkiaNamed(overlay_id), 0, 0, | 
| -                       alpha); | 
| +  SkPath fill; | 
| +  if (ui::MaterialDesignController::IsModeMaterial()) { | 
| +    // Fill. | 
| +    fill.moveTo(9.75 * scale, 16 * scale); | 
| +    fill.rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, | 
| +                  -1.5 * scale); | 
| +    fill.rLineTo(-5.75 * scale, -12.5 * scale); | 
| +    fill.rCubicTo(0, -0.5 * scale, 0.25 * scale, -scale, scale, -scale); | 
| +    fill.rLineTo(23.25 * scale, 0); | 
| +    fill.rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale, | 
| +                  1.5 * scale); | 
| +    fill.rLineTo(5.75 * scale, 12.5 * scale); | 
| +    fill.rCubicTo(0, 0.5 * scale, -0.25 * scale, scale, -scale, scale); | 
| +    fill.close(); | 
| +    PaintFill(pressed, hover_value, scale, fill, canvas); | 
| + | 
| +    // Stroke. | 
| +    gfx::ScopedCanvas scoped_canvas(canvas); | 
| +    canvas->UndoDeviceScaleFactor(); | 
| +    SkPath stroke; | 
| +    GetBorderPath(0, scale, false, &stroke); | 
| +    // We want to draw a drop shadow either inside or outside the stroke, | 
| +    // depending on whether we're pressed; so, either clip out what's outside | 
| +    // the stroke, or clip out the fill inside it. | 
| +    if (pressed) | 
| +      canvas->ClipPath(stroke, true); | 
| +    Op(stroke, fill, kDifference_SkPathOp, &stroke); | 
| +    if (!pressed) | 
| +      canvas->sk_canvas()->clipPath(fill, SkRegion::kDifference_Op, true); | 
| +    // Now draw the stroke and shadow; the stroke will always be visible, while | 
| +    // the shadow will be affected by the clip we set above. | 
| +    SkPaint paint; | 
| +    paint.setAntiAlias(true); | 
| +    skia::RefPtr<SkDrawLooper> stroke_looper = CreateShadowDrawLooper(0x8C); | 
| +    paint.setLooper(stroke_looper.get()); | 
| +    paint.setColor(SkColorSetA(SK_ColorBLACK, pressed ? 0x38 : 0x27)); | 
| +    canvas->DrawPath(stroke, paint); | 
| +  } else { | 
| +    // Fill. | 
| +    gfx::ImageSkia* mask = | 
| +        GetThemeProvider()->GetImageSkiaNamed(IDR_NEWTAB_BUTTON_MASK); | 
| +    // The canvas and mask have to use the same scale factor. | 
| +    const float fill_canvas_scale = mask->HasRepresentation(scale) ? | 
| +        scale : ui::GetScaleForScaleFactor(ui::SCALE_FACTOR_100P); | 
| +    gfx::Canvas fill_canvas(GetNewTabButtonSize(), fill_canvas_scale, false); | 
| +    PaintFill(pressed, hover_value, fill_canvas_scale, fill, &fill_canvas); | 
| +    gfx::ImageSkia image(fill_canvas.ExtractImageRep()); | 
| +    canvas->DrawImageInt( | 
| +        gfx::ImageSkiaOperations::CreateMaskedImage(image, *mask), 0, 0); | 
| + | 
| +    // Stroke.  Draw the button border with a slight alpha. | 
| +    static const SkAlpha kGlassFrameOverlayAlpha = 178; | 
| +    static const SkAlpha kOpaqueFrameOverlayAlpha = 230; | 
| +    const SkAlpha alpha = GetWidget()->ShouldWindowContentsBeTransparent() ? | 
| +        kGlassFrameOverlayAlpha : kOpaqueFrameOverlayAlpha; | 
| +    const int overlay_id = pressed ? IDR_NEWTAB_BUTTON_P : IDR_NEWTAB_BUTTON; | 
| +    canvas->DrawImageInt(*GetThemeProvider()->GetImageSkiaNamed(overlay_id), 0, | 
| +                         0, alpha); | 
| +  } | 
| } | 
|  | 
| bool NewTabButton::GetHitTestMask(gfx::Path* mask) const { | 
| DCHECK(mask); | 
|  | 
| -  if (tab_strip_->SizeTabButtonToTopOfTabStrip()) { | 
| +  if (ui::MaterialDesignController::IsModeMaterial()) { | 
| +    SkPath border; | 
| +    const float scale = GetWidget()->GetCompositor()->device_scale_factor(); | 
| +    GetBorderPath(TabStrip::kNewTabButtonVerticalOffset * scale, scale, | 
| +                  tab_strip_->SizeTabButtonToTopOfTabStrip(), &border); | 
| +    mask->addPath(border, SkMatrix::MakeScale(1 / scale)); | 
| +  } else if (tab_strip_->SizeTabButtonToTopOfTabStrip()) { | 
| // When the button is sized to the top of the tab strip, we want the hit | 
| // test mask to be defined as the complete (rectangular) bounds of the | 
| // button. | 
| @@ -393,56 +477,134 @@ bool NewTabButton::GetHitTestMask(gfx::Path* mask) const { | 
| return true; | 
| } | 
|  | 
| +void NewTabButton::GetBorderPath(float button_y, | 
| +                                 float scale, | 
| +                                 bool extend_to_top, | 
| +                                 SkPath* path) const { | 
| +  path->moveTo(9.75 * scale - 1, button_y + 16 * scale + 1); | 
| +  path->rCubicTo(-0.75 * scale, 0, -1.625 * scale, -0.5 * scale, -2 * scale, | 
| +                 -1.5 * scale); | 
| +  path->rLineTo(-5.75 * scale, -12.5 * scale); | 
| +  if (extend_to_top) { | 
| +    // Create the vertical extension by extending the side diagonals at the | 
| +    // upper left and lower right corners until they reach the top and bottom of | 
| +    // the border, respectively (in other words, "un-round-off" those corners | 
| +    // and turn them into sharp points).  Then extend upward from the corner | 
| +    // points to the top of the bounds. | 
| +    const float dy = scale + 2; | 
| +    const float dx = 11.5 / 25 * dy; | 
| +    path->rLineTo(-dx, -dy); | 
| +    path->rLineTo(0, -button_y - scale + 1); | 
| +    path->lineTo(34 * scale + 1 + dx, 0); | 
| +    path->rLineTo(0, button_y + 16 * scale + 1); | 
| +  } else { | 
| +    path->rCubicTo(-0.5 * scale, -1.125 * scale, 0.5 * scale, -scale - 2, scale, | 
| +                   -scale - 2); | 
| +    path->rLineTo(23.25 * scale + 2, 0); | 
| +    path->rCubicTo(0.75 * scale, 0, 1.625 * scale, 0.5 * scale, 2 * scale, | 
| +                   1.5 * scale); | 
| +    path->rLineTo(5.75 * scale, 12.5 * scale); | 
| +    path->rCubicTo(0.5 * scale, 1.125 * scale, -0.5 * scale, scale + 2, -scale, | 
| +                   scale + 2); | 
| +  } | 
| +  path->close(); | 
| +} | 
| + | 
| void NewTabButton::PaintFill(bool pressed, | 
| double hover_value, | 
| float scale, | 
| +                             const SkPath& fill, | 
| gfx::Canvas* canvas) const { | 
| bool custom_image; | 
| const int bg_id = tab_strip_->GetBackgroundResourceId(&custom_image); | 
|  | 
| +  gfx::ScopedCanvas scoped_canvas(canvas); | 
| + | 
| +  const bool md = ui::MaterialDesignController::IsModeMaterial(); | 
| +  if (md) { | 
| +    canvas->UndoDeviceScaleFactor(); | 
| + | 
| +    // For unpressed buttons, draw the fill and its shadow. | 
| +    if (!pressed) { | 
| +      gfx::ScopedCanvas scoped_canvas(canvas); | 
| + | 
| +      // For custom themes, clip out the fill path itself so only the shadow | 
| +      // around it is drawn.  We'll draw the fill image below. | 
| +      if (custom_image) | 
| +        canvas->sk_canvas()->clipPath(fill, SkRegion::kDifference_Op, true); | 
| + | 
| +      SkPaint paint; | 
| +      paint.setAntiAlias(true); | 
| +      skia::RefPtr<SkDrawLooper> looper = CreateShadowDrawLooper(0x26); | 
| +      paint.setLooper(looper.get()); | 
| +      paint.setColor(Tab::kInactiveTabColor); | 
| +      canvas->DrawPath(fill, paint); | 
| +    } | 
| + | 
| +    // Clip to just the fill region for any theme drawing and hover/pressed | 
| +    // state overlay drawing below.  In non-MD mode, this is done on the caller | 
| +    // side using image masking operations. | 
| +    canvas->ClipPath(fill, true); | 
| +    canvas->sk_canvas()->scale(scale, scale); | 
| +  } | 
| + | 
| // Draw the fill background image. | 
| const gfx::Size size(GetNewTabButtonSize()); | 
| -  const ui::ThemeProvider* theme_provider = GetThemeProvider(); | 
| -  gfx::ImageSkia* background = theme_provider->GetImageSkiaNamed(bg_id); | 
| -  // For custom tab backgrounds the background starts at the top of the tab | 
| -  // strip. Otherwise the background starts at the top of the frame. | 
| -  const int offset_y = theme_provider->HasCustomImage(bg_id) ? | 
| -      -GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT) : background_offset_.y(); | 
| - | 
| -  // The new tab background is mirrored in RTL mode, but the theme background | 
| -  // should never be mirrored. Mirror it here to compensate. | 
| -  float x_scale = 1.0f; | 
| -  int x = GetMirroredX() + background_offset_.x(); | 
| -  if (base::i18n::IsRTL()) { | 
| -    x_scale = -1.0f; | 
| -    // Offset by |width| such that the same region is painted as if there was | 
| -    // no flip. | 
| -    x += size.width(); | 
| -  } | 
| -  canvas->TileImageInt(*background, x, | 
| -                       TabStrip::kNewTabButtonVerticalOffset + offset_y, | 
| -                       x_scale, 1.0f, 0, 0, size.width(), size.height()); | 
| - | 
| -  // Adjust the alpha of the fill to match that of inactive tabs (except for | 
| -  // pressed buttons, which get a different value). | 
| -  static const SkAlpha kPressedAlpha = 145; | 
| -  const SkAlpha alpha = | 
| -      pressed ? kPressedAlpha : tab_strip_->GetInactiveAlpha(true); | 
| -  if (alpha != 255) { | 
| -    SkPaint paint; | 
| -    paint.setAlpha(alpha); | 
| -    paint.setXfermodeMode(SkXfermode::kDstIn_Mode); | 
| -    paint.setStyle(SkPaint::kFill_Style); | 
| -    canvas->DrawRect(gfx::Rect(size), paint); | 
| +  if (custom_image || !md) { | 
| +    const ui::ThemeProvider* theme_provider = GetThemeProvider(); | 
| +    gfx::ImageSkia* background = theme_provider->GetImageSkiaNamed(bg_id); | 
| +    // For custom tab backgrounds the background starts at the top of the tab | 
| +    // strip. Otherwise the background starts at the top of the frame. | 
| +    const int offset_y = theme_provider->HasCustomImage(bg_id) ? | 
| +        -GetLayoutConstant(TAB_TOP_EXCLUSION_HEIGHT) : background_offset_.y(); | 
| + | 
| +    // The new tab background is mirrored in RTL mode, but the theme background | 
| +    // should never be mirrored. Mirror it here to compensate. | 
| +    float x_scale = 1.0f; | 
| +    int x = GetMirroredX() + background_offset_.x(); | 
| +    if (base::i18n::IsRTL()) { | 
| +      x_scale = -1.0f; | 
| +      // Offset by |width| such that the same region is painted as if there was | 
| +      // no flip. | 
| +      x += size.width(); | 
| +    } | 
| +    canvas->TileImageInt(*background, x, | 
| +                         TabStrip::kNewTabButtonVerticalOffset + offset_y, | 
| +                         x_scale, 1.0f, 0, 0, size.width(), size.height()); | 
| + | 
| +    // For non-MD, adjust the alpha of the fill to match that of inactive tabs | 
| +    // (except for pressed buttons, which get a different value).  For MD, we do | 
| +    // this with an opacity recorder in TabStrip::PaintChildren() so the fill | 
| +    // and stroke are both affected, to better match how tabs are handled, but | 
| +    // in non-MD, the button stroke is already lighter than the tab stroke, and | 
| +    // using the opacity recorder washes it out too much. | 
| +    static const SkAlpha kPressedAlpha = 145; | 
| +    const SkAlpha alpha = | 
| +        pressed ? kPressedAlpha : tab_strip_->GetInactiveAlpha(true); | 
| +    if (alpha != 255 && !md) { | 
| +      SkPaint paint; | 
| +      paint.setAlpha(alpha); | 
| +      paint.setXfermodeMode(SkXfermode::kDstIn_Mode); | 
| +      paint.setStyle(SkPaint::kFill_Style); | 
| +      canvas->DrawRect(gfx::Rect(size), paint); | 
| +    } | 
| } | 
|  | 
| // White highlight on hover. | 
| if (hover_value) { | 
| const int alpha = | 
| -        gfx::Tween::LinearIntValueBetween(hover_value, 0x00, 0x40); | 
| +        gfx::Tween::LinearIntValueBetween(hover_value, 0x00, md ? 0x4D : 0x40); | 
| canvas->FillRect(GetLocalBounds(), | 
| SkColorSetA(SK_ColorWHITE, static_cast<SkAlpha>(alpha))); | 
| } | 
| + | 
| +  // For MD, most states' opacities are adjusted using an opacity recorder in | 
| +  // TabStrip::PaintChildren(), but the pressed state is excluded there and | 
| +  // instead rendered using a dark overlay here.  This produces a different | 
| +  // effect than for non-MD, and avoiding the use of the opacity recorder keeps | 
| +  // the stroke more visible in this state. | 
| +  if (md && pressed) | 
| +    canvas->FillRect(GetLocalBounds(), SkColorSetA(SK_ColorBLACK, 0x14)); | 
| } | 
|  | 
| /////////////////////////////////////////////////////////////////////////////// | 
| @@ -1270,13 +1432,11 @@ void TabStrip::PaintChildren(const ui::PaintContext& context) { | 
| Tabs selected_tabs; | 
|  | 
| { | 
| -    const chrome::HostDesktopType host_desktop_type = | 
| -        chrome::GetHostDesktopTypeForNativeView(GetWidget()->GetNativeView()); | 
| -    const uint8_t inactive_tab_alpha = | 
| -        (host_desktop_type == chrome::HOST_DESKTOP_TYPE_ASH) ? | 
| -            GetInactiveAlpha(false) : 255; | 
| +    // TODO(pkasting): This results in greyscale AA for the tab titles.  Work | 
| +    // with Mike Reed to fix, or else convert to passing the tabs a memory | 
| +    // canvas to paint on and then manually compositing. | 
| ui::CompositingRecorder opacity_recorder(context, size(), | 
| -                                             inactive_tab_alpha); | 
| +                                             GetInactiveAlpha(false)); | 
|  | 
| PaintClosingTabs(tab_count(), context); | 
|  | 
| @@ -1319,28 +1479,6 @@ void TabStrip::PaintChildren(const ui::PaintContext& context) { | 
| } | 
| } | 
|  | 
| -  if (GetWidget()->ShouldWindowContentsBeTransparent()) { | 
| -    ui::PaintRecorder recorder(context, size()); | 
| -    // Make sure non-active tabs are somewhat transparent. | 
| -    SkPaint paint; | 
| -    // If there are multiple tabs selected, fade non-selected tabs more to make | 
| -    // the selected tabs more noticable. | 
| -    uint8_t alpha = GetInactiveAlpha(false); | 
| -    paint.setColor(SkColorSetA(SK_ColorWHITE, alpha)); | 
| -    paint.setXfermodeMode(SkXfermode::kDstIn_Mode); | 
| -    paint.setStyle(SkPaint::kFill_Style); | 
| - | 
| -    gfx::Rect bounds(GetLocalBounds()); | 
| -    // The tab graphics include some shadows at the top, plus a 1 pixel top | 
| -    // stroke.  Exclude this region when trying to make tabs transparent as it's | 
| -    // transparent enough already, and drawing in this region can overlap the | 
| -    // avatar button, leading to visual artifacts.  Also exclude the toolbar | 
| -    // overlap region at the bottom. | 
| -    gfx::Insets tab_insets(GetLayoutInsets(TAB)); | 
| -    bounds.Inset(0, tab_insets.top(), 0, tab_insets.bottom()); | 
| -    recorder.canvas()->DrawRect(bounds, paint); | 
| -  } | 
| - | 
| // Now selected but not active. We don't want these dimmed if using native | 
| // frame, so they're painted after initial pass. | 
| for (size_t i = 0; i < selected_tabs.size(); ++i) | 
| @@ -1351,7 +1489,16 @@ void TabStrip::PaintChildren(const ui::PaintContext& context) { | 
| active_tab->Paint(context); | 
|  | 
| // Paint the New Tab button. | 
| -  newtab_button_->Paint(context); | 
| +  const bool md = ui::MaterialDesignController::IsModeMaterial(); | 
| +  if (md && (newtab_button_->state() != views::CustomButton::STATE_PRESSED)) { | 
| +    // Match the inactive tab opacity for non-pressed states.  See comments in | 
| +    // NewTabButton::PaintFill() for why we don't do this for the pressed state. | 
| +    ui::CompositingRecorder opacity_recorder(context, size(), | 
| +                                             GetInactiveAlpha(true)); | 
| +    newtab_button_->Paint(context); | 
| +  } else { | 
| +    newtab_button_->Paint(context); | 
| +  } | 
|  | 
| // And the dragged tabs. | 
| for (size_t i = 0; i < tabs_dragging.size(); ++i) | 
| @@ -1360,6 +1507,18 @@ void TabStrip::PaintChildren(const ui::PaintContext& context) { | 
| // If the active tab is being dragged, it goes last. | 
| if (active_tab && is_dragging) | 
| active_tab->Paint(context); | 
| + | 
| +  if (md) { | 
| +    ui::PaintRecorder recorder(context, size()); | 
| +    gfx::Canvas* canvas = recorder.canvas(); | 
| +    if (active_tab) { | 
| +      canvas->sk_canvas()->clipRect( | 
| +          gfx::RectToSkRect(active_tab->GetMirroredBounds()), | 
| +          SkRegion::kDifference_Op); | 
| +    } | 
| +    BrowserView::Paint1pxHorizontalLine( | 
| +        canvas, SkColorSetA(SK_ColorBLACK, 0x40), GetLocalBounds(), true); | 
| +  } | 
| } | 
|  | 
| const char* TabStrip::GetClassName() const { | 
|  |