Bug 313840
| Summary: | REGRESSION (Safari 26): setProperty fails to apply !important priority to existing inline style if integer value is <= 255, breaking cascade | ||
|---|---|---|---|
| Product: | WebKit | Reporter: | Jacob Goldman <jmgoldman> |
| Component: | CSS | Assignee: | Ahmad Saleem <ahmad.saleem792> |
| Status: | RESOLVED FIXED | ||
| Severity: | Normal | CC: | karlcow, koivisto, webkit-bug-importer |
| Priority: | P2 | Keywords: | InRadar |
| Version: | Safari 26 | ||
| Hardware: | Unspecified | ||
| OS: | Unspecified | ||
| Bug Depends on: | 285900 | ||
| Bug Blocks: | |||
Jacob Goldman
When an element has an existing inline style containing an integer value of 255 or lower (e.g., `width: 200px`), using `CSSStyleDeclaration.setProperty()` to re-apply the exact same value string alongside the `!important` priority flag silently fails.
Because the priority flag is dropped, the inline style fails to override conflicting `!important` rules in the global stylesheet, resulting in a layout rendering failure.
**Steps to Reproduce:**
Save the following code as an HTML file and open it in Safari:
```html
<!DOCTYPE html>
<html>
<head>
<style>
/* Global stylesheet with !important */
#test-el {
width: 100px !important;
}
</style>
</head>
<body>
<div id="test-el"></div>
<script>
const el = document.getElementById('test-el');
// 1. Set standard inline style.
el.style.setProperty('width', '255px');
console.log("offsetWidth (from global):", el.offsetWidth);
console.log("width priority:", el.style.getPropertyPriority('width'));
// 2. (fails) Attempt to upgrade priority to !important with the exact same value
el.style.setProperty('width', '255px', 'important');
console.log("offsetWidth (from inline):", el.offsetWidth);
console.log("width priority:", el.style.getPropertyPriority('width'));
// 3. (works) Attempt to upgrade priority to !important with a value >= uint8.
el.style.setProperty('width', '256px', 'important');
console.log("offsetWidth (from inline):", el.offsetWidth);
console.log("width priority:", el.style.getPropertyPriority('width'));
</script>
</body>
</html>
```
**Actual Results:**
```
[Log] offsetWidth (from global): – 100 (minimal.html, line 19)
[Log] width priority: – "" (minimal.html, line 20)
[Log] offsetWidth (from inline): – 100 (minimal.html, line 24)
[Log] width priority: – "important" (minimal.html, line 25)
[Log] offsetWidth (from inline): – 256 (minimal.html, line 29)
[Log] width priority: – "important" (minimal.html, line 30)
```
The `!important` flag was ignored with 255px, the engine skipped style invalidation, and the global stylesheet incorrectly won the cascade until 256px was provided.
**Expected Results:**
[Log] offsetWidth (from global): – 100 (minimal.html, line 19)
[Log] width priority: – "" (minimal.html, line 20)
[Log] offsetWidth (from inline): – 255 (minimal.html, line 24)
[Log] width priority: – "important" (minimal.html, line 25)
[Log] offsetWidth (from inline): – 256 (minimal.html, line 29)
[Log] width priority: – "important" (minimal.html, line 30)
The inline `!important` style should override the stylesheet `!important` style, rendering the element at 200px wide.
**Build Date & Hardware:**
Safari 26.0 on macOS Sequoia / macOS Sonoma.
**Additional Builds and Platforms:**
Doesn't Occur On Safari v18.6.
Doesn't Occur On Google Chrome or Mozilla Firefox.
**Additional Information:**
*Fact:* If the integer value used in the script is changed to `256px` or higher (e.g., `257px`), the bug does not occur.
*Fact:* Calling `el.style.removeProperty('width')` immediately before the `setProperty` call successfully works around the issue, forcing the layout engine to apply the priority and render at `200px`.
*Speculation (from LLM):* This appears to be a regression in WebCore's mutation optimization (`MutableStyleProperties::setProperty`), specifically interacting with the `CSSValuePool`. WebKit caches CSS primitive values between 0 and 255. Because both the existing inline value and the incoming `setProperty` value use the cached pointer for `200px`, the engine performs a fast-path memory address comparison, incorrectly concludes the style has not changed, and aborts the update before parsing the priority flag or invalidating the layout. Values of 256+ exceed the cache, forcing a new memory allocation, which bypasses the pointer equality check and allows the cascade to resolve properly.
| Attachments | ||
|---|---|---|
| Add attachment proposed patch, testcase, etc. |
Radar WebKit Bug Importer
<rdar://problem/176099619>
Karl Dubost
It may be the result of the optimization done in Bug 285900.
Karl Dubost
Jakob, thanks for the report. Did it break one of the websites you are working on?
Ahmad Saleem
Pull request: https://github.com/WebKit/WebKit/pull/64670
Jacob Goldman
(In reply to Karl Dubost from comment #3)
> Jakob, thanks for the report. Did it break one of the websites you are
> working on?
Thank you all for the quick fix. I work on Google display ads, this may have broken styling on certain ad formats but depends on any collisions with the website's own !important styles (bug was surfaced in a unit test). We will run an experiment soon with a workaround to remove the style before adding it back with important.
EWS
Committed 313159@main (e297a5ca30f3): <https://commits.webkit.org/313159@main>
Reviewed commits have been landed. Closing PR #64670 and removing active labels.