Bug 218798

Summary: -webkit-text-fill-color:currentColor not working on disabled inputs
Product: WebKit Reporter: Tomas Carnecky <tomc>
Component: CSSAssignee: Nobody <webkit-unassigned>
Status: RESOLVED INVALID    
Severity: Normal CC: akeerthi, hi, koivisto, mmaxfield, smoley, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: Safari 14   
Hardware: Mac   
OS: macOS 10.15   

Description Tomas Carnecky 2020-11-11 05:27:53 PST
Given the following markup:

<div style="color:rgb(0,0,100);">
  <input value="regular" style="color:currentColor;-webkit-text-fill-color:currentColor;" />
  <input value="disabled" disabled style="color:currentColor;-webkit-text-fill-color:currentColor;" />
</div>

The disabled input has visibly different text color from the regular input. Nothing short of explicitly setting a color (ie. a specific rgb value) via -webkit-text-fill-color changes the color. Neither color:rgb(0,0,100), nor color:currentColor, nor -webkit-text-fill-color:currentColor has any effect.

Additionally, the computed style of both inputs show "rgb(0, 0, 100)" for both color / webkitTextFillColor, even though that is evidently not true.

window.getComputedStyle($1).color
"rgb(0, 0, 100)"
window.getComputedStyle($1).webkitTextFillColor
"rgb(0, 0, 100)"
Comment 1 Smoley 2020-11-12 19:48:11 PST
Does not reproduce on Safari 13.1.3, does reproduce on 14.0.1 and STP 115 (14.1).
Comment 2 Radar WebKit Bug Importer 2020-11-12 19:48:22 PST
<rdar://problem/71355347>
Comment 3 Antti Koivisto 2021-01-12 01:59:11 PST
This appears to work as designed. The behavior change Safari 13->14 is a progression from handling currentColor correctly.

The logic is that the disabled color is a lightened (or darkened, depending on the background color) version of the original color. In this case it produces a lighter shade of blue. The color change is only applied to the inner shadow element so it is not visible via getComputedStyle.

As you note you can disable the behavior by specifying the color explicitly using -webkit-text-fill-color. Using currentColor there does nothing as it is the default value (and currentColor is then resolved against the modified color in the inner element).

Here is the full color change logic:

Color RenderTheme::disabledTextColor(const Color& textColor, const Color& backgroundColor) const
{
    // The explicit check for black is an optimization for the 99% case (black on white).
    // This also means that black on black will turn into grey on black when disabled.
    Color disabledColor;
    if (equalIgnoringSemanticColor(textColor, Color::black) || backgroundColor.alphaAsFloat() < minDisabledColorAlphaValue || textColor.luminance() < backgroundColor.luminance())
        disabledColor = textColor.lightened();
    else
        disabledColor = textColor.darkened();
    
    // If there's not very much contrast between the disabled color and the background color,
    // just leave the text color alone. We don't want to change a good contrast color scheme so that it has really bad contrast.
    // If the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme.
    if (contrastRatio(disabledColor.toSRGBALossy<float>(), backgroundColor.toSRGBALossy<float>()) < minColorContrastValue)
        return textColor;

    return disabledColor;
}