Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the incorrect percentage calculation of width (#34665) #34714

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

Asun0204
Copy link

@Asun0204 Asun0204 commented Dec 20, 2024

The conversion from percentage * basis to Au is unreasonably. Round method is used in this process and the size of box is larger.


  • There are tests for these changes OR
  • These changes do not require tests because ___

Comment on lines 761 to 764
if v.to_percentage().is_some() && basis.inline.is_some() {
Some(Au::from_f32_px(
v.to_pixel_length(basis.inline?).px().floor(),
))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if v.to_percentage().is_some() && basis.inline.is_some() {
Some(Au::from_f32_px(
v.to_pixel_length(basis.inline?).px().floor(),
))
if v.to_percentage().is_some() {
basis.inline.map(|inline| Au::from_f32_px(v.to_pixel_length(inline).px().floor()))

I'm not familiar with this code, so I can't comment on the correctness, but I believe we can simplify your code by using map

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with rust, do you mean the following usage?

v.to_percentage().map_or_else(
    || v.maybe_to_used_value(basis.inline),
    |_| {
        Some(Au::from_f32_px(
            v.to_pixel_length(basis.inline?).px().floor(),
        ))
    },
)

Copy link
Contributor

@Loirooriol Loirooriol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would force percentages to resolve to a whole number of app units, which is bad.
Also, this isn't covering all cases of to_used_value and maybe_to_used_value, which is inconsistent.
It would be better to either:

  • Add a new method in Au which is like from _f32_px but truncating instead of rounding. Then modify to_used_value and maybe_to_used_value in Stylo to use that.
  • Modify from_f32_px itself, if that doesn't break anything in Firefox nor Servo.

@Loirooriol
Copy link
Contributor

Also, the current behavior isn't technically "incorrect". It indeed seems beneficial to change it, but the specs basically leave subpixel arithmetic up to the UAs.

@jschwe
Copy link
Member

jschwe commented Dec 20, 2024

If I'm not mistaken, I think this issue is causing the mobile jingdong website https://m.jingdong.com/ to have an issue where the bar at the bottom to overflows in servo, but not in Firefox or chrome.

Edit:
I can't reproduce on desktop, but here is screenshot from Android servo:

Screenshot_20241220_182209.jpg

@Asun0204
Copy link
Author

Asun0204 commented Dec 23, 2024

Althrough subpixel arithmetic is not defined in W3C, the current behavior has something wrong obviously (#34665). Truncate method is used in percentage calculation of firefox and chromium.

It is the code in firefox.

static inline nscoord DefaultPercentLengthToAppUnits(float aPixelLength) {
  return NSToCoordTruncClamped(aPixelLength);
}

It is the code in chromium.

LayoutUnit MinimumValueForLengthInternal(const Length& length,
                                         LayoutUnit maximum_value) {
  switch (length.GetType()) {
    case Length::kPercent:
      // Don't remove the extra cast to float. It is needed for rounding on
      // 32-bit Intel machines that use the FPU stack.
      return LayoutUnit(
          static_cast<float>(maximum_value * length.Percent() / 100.0f));
      // ...
}
  constexpr explicit LayoutUnit(float value)
      : value_(base::saturated_cast<int>(value * kFixedPointDenominator)) {}

So should we only modify the implementation of percentages_relative_to_basis and maybe_percentages_relative_to_basis?

@Loirooriol
Copy link
Contributor

the current behavior has something wrong obviously

I would say undesirable instead of wrong.

So should we only modify the implementation of percentages_relative_to_basis and maybe_percentages_relative_to_basis?

No, that's the wrong place, as I said in #34714 (review)

@Asun0204
Copy link
Author

Asun0204 commented Dec 24, 2024

to_used_value is called widely in firefox and servo, it is difficult for me to evalute the impact about it. And
percentages_relative_to_basis and maybe_percentages_relative_to_basis are only called in content_box_sizes_and_padding_border_margin, the impact will be controlled in content size calcuation.

Another reason is that to_used_value and maybe_to_used_value are not only used to calculate percentage value but also include normal length and calc length.

@Asun0204
Copy link
Author

Asun0204 commented Dec 26, 2024

I omplete the code but don't know how to merge it. What to do next? @Loirooriol
servo/stylo#102
servo/app_units#60

Signed-off-by: Oriol Brufau <obrufau@igalia.com>
@Loirooriol Loirooriol added the T-linux-wpt-2020 Do a try run of the WPT label Jan 8, 2025
@github-actions github-actions bot removed the T-linux-wpt-2020 Do a try run of the WPT label Jan 8, 2025
Copy link

github-actions bot commented Jan 8, 2025

🔨 Triggering try run (#12672394680) for Linux WPT

Copy link

github-actions bot commented Jan 8, 2025

Test results for linux-wpt-layout-2020 from try job (#12672394680):

Flaky unexpected result (25)
  • TIMEOUT /FileAPI/url/url-in-tags-revoke.window.html (#19978)
    • TIMEOUT [expected PASS] subtest: Fetching a blob URL immediately before revoking it works in &lt;script&gt; tags.

      Test timed out
      

  • FAIL [expected PASS] /_mozilla/css/dirty_viewport.html (#13731)
  • TIMEOUT [expected OK] /css/css-flexbox/abspos/position-absolute-013.html
  • OK /css/css-grid/alignment/grid-content-alignment-with-abspos-001.html (#34339)
    • FAIL [expected PASS] subtest: .grid 1

      assert_equals: 
      &lt;div class="grid" data-expected-width="800" data-expected-height="600"&gt;
          &lt;div class="a" id="item" data-offset-x="329" data-offset-y="269" data-expected-width="142" data-expected-height="62" style="place-self: center;"&gt;&lt;/div&gt;
        &lt;/div&gt;
      offsetLeft expected 329 but got 0
      

  • FAIL [expected PASS] /css/css-overflow/overflow-video.html (#34720)
  • PASS [expected FAIL] /css/css-sizing/dynamic-available-size-iframe.html (#31559)
  • OK /css/css-values/cap-invalidation.html (#32757)
    • FAIL [expected PASS] subtest: CSS Values and Units Test: cap invalidation

      uncaught exception: Error: assert_not_equals: expect the capital height of Ahem and sans-serif to be different got disallowed value 371.3333333333333
      

  • OK /encoding/legacy-mb-japanese/shift_jis/sjis-encode-href-errors-hangul.html?1-1000
    • FAIL [expected PASS] subtest: hangul U+AC00 가 %26%2344032%3B

      assert_equals: expected "%26%2344032%3B" but got ""
      

    • FAIL [expected PASS] subtest: hangul U+AC01 각 %26%2344033%3B

      assert_equals: expected (string) "%26%2344033%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC02 갂 %26%2344034%3B

      assert_equals: expected (string) "%26%2344034%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC03 갃 %26%2344035%3B

      assert_equals: expected (string) "%26%2344035%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC04 간 %26%2344036%3B

      assert_equals: expected (string) "%26%2344036%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC05 갅 %26%2344037%3B

      assert_equals: expected (string) "%26%2344037%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC06 갆 %26%2344038%3B

      assert_equals: expected (string) "%26%2344038%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC07 갇 %26%2344039%3B

      assert_equals: expected (string) "%26%2344039%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC08 갈 %26%2344040%3B

      assert_equals: expected (string) "%26%2344040%3B" but got (undefined) undefined
      

    • FAIL [expected PASS] subtest: hangul U+AC09 갉 %26%2344041%3B

      assert_equals: expected (string) "%26%2344041%3B" but got (undefined) undefined
      

    • And 390 more unexpected results...
  • OK /fetch/metadata/generated/css-font-face.sub.tentative.html (#34624)
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Not sent to non-trustworthy same-site destination
  • OK /html/browsers/browsing-the-web/navigating-across-documents/empty-iframe-load-event.html (#29066)
    • PASS [expected FAIL] subtest: Check execution order on load handler
    • PASS [expected FAIL] subtest: Check execution order from nested timeout
  • OK /html/browsers/browsing-the-web/navigating-across-documents/javascript-url-return-value-handling-dynamic.html (#28066)
    • FAIL [expected PASS] subtest: 0041 set in href="" targeting a frame and clicked

      assert_equals: expected "A" but got ""
      

    • FAIL [expected PASS] subtest: 0080 00FF set in href="" targeting a frame and clicked

      assert_equals: expected "�ÿ" but got ""
      

    • FAIL [expected PASS] subtest: 0080 00FF 0100 set in href="" targeting a frame and clicked

      assert_equals: expected "�ÿĀ" but got ""
      

    • FAIL [expected PASS] subtest: D83D DE0D set in href="" targeting a frame and clicked

      assert_equals: expected "😍" but got ""
      

    • FAIL [expected PASS] subtest: DE0D 0041 set in href="" targeting a frame and clicked

      assert_equals: expected "\ufffdA" but got ""
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted

      assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
      

  • OK /html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html (#29046)
    • PASS [expected FAIL] subtest: first argument: absolute url
  • ERROR [expected TIMEOUT] /html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html (#34119)
  • CRASH [expected OK] /html/canvas/offscreen/canvas-host/2d.canvas.host.size.large.html (#34117)
  • OK /html/interaction/focus/the-autofocus-attribute/document-with-fragment-valid.html (#28259)
    • PASS [expected FAIL] subtest: Autofocus elements in top-level browsing context's documents with URL fragments should be skipped.
  • OK [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK /html/semantics/forms/form-submission-0/text-plain.window.html (#28687)
    • PASS [expected FAIL] subtest: text/plain: 0x00 in filename (normal form)
    • PASS [expected FAIL] subtest: text/plain: non-ASCII in name and value (formdata event)
  • OK /html/semantics/links/links-created-by-a-and-area-elements/htmlanchorelement_noopener.html (#23205)
    • PASS [expected FAIL] subtest: Check that rel=noopener with target=_self does a normal load
  • OK /html/semantics/scripting-1/the-script-element/execution-timing/077.html (#22139)
    • FAIL [expected PASS] subtest: adding several types of scripts through the DOM and removing some of them confuses scheduler

      assert_array_equals: expected property 1 to be "Script #1 ran" but got "Script #3 ran" (expected array ["Script #2 ran", "Script #1 ran", "Script #3 ran", "Script #4 ran"] got ["Script #2 ran", "Script #3 ran", "Script #4 ran", "Script #1 ran"])
      

  • TIMEOUT [expected OK] /resource-timing/nested-context-navigations-iframe.html (#24311)
    • TIMEOUT [expected PASS] subtest: Test that iframe navigations are not observable by the parent, even after history navigations by the parent

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe navigations are not observable by the parent, even after history navigations by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe navigations are not observable by the parent, even after history navigations by the parent
    • NOTRUN [expected PASS] subtest: Test that iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that iframe refreshes are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe refreshes are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe refreshes are not observable by the parent
  • OK [expected ERROR] /webxr/render_state_update.https.html (#27535)
  • OK [expected ERROR] /workers/constructors/Worker/Worker-constructor.html (#22991)
  • OK /workers/dedicated-worker-from-blob-url.window.html (#22286)
    • PASS [expected FAIL] subtest: Creating a dedicated worker from a blob URL works immediately before revoking.
Stable unexpected results that are known to be intermittent (6)
  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html (#24057)
  • TIMEOUT [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • PASS [expected FAIL] subtest: Reload domComplete &gt; Original domComplete
    • PASS [expected FAIL] subtest: Reload domContentLoadedEventEnd &gt; Original domContentLoadedEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventEnd &gt; Original loadEventEnd
    • PASS [expected FAIL] subtest: Reload loadEventStart &gt; Original loadEventStart
  • TIMEOUT [expected OK] /performance-timeline/navigation-id-detached-frame.tentative.html (#34773)
    • TIMEOUT [expected PASS] subtest: The navigation_id getter does not crash a window of detached frame

      Test timed out
      

  • OK [expected TIMEOUT] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • PASS [expected TIMEOUT] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe
Stable unexpected results (3)
  • OK /css/css-sizing/animation/width-interpolation.html
    • FAIL [expected PASS] subtest: CSS Transitions: property &lt;width&gt; from [inherit] to [40px] at (1.5) should be [10px]

      assert_equals: expected "10px " but got "9.98px "
      

    • FAIL [expected PASS] subtest: CSS Transitions with transition: all: property &lt;width&gt; from [inherit] to [40px] at (1.5) should be [10px]

      assert_equals: expected "10px " but got "9.98px "
      

    • FAIL [expected PASS] subtest: CSS Animations: property &lt;width&gt; from [inherit] to [40px] at (1.5) should be [10px]

      assert_equals: expected "10px " but got "9.98px "
      

  • FAIL [expected PASS] /css/css-transforms/transform-input-006.html
  • PASS [expected FAIL] /css/css-values/calc-rounding-003.html

Copy link

github-actions bot commented Jan 8, 2025

⚠️ Try run (#12672394680) failed.

@Loirooriol
Copy link
Contributor

What to do next?

Please investigate the failures. Interpolating from an inherited 100px to 40px at 1.5 should be 100 * -0.5 + 40 * 1.5 = -50 + 60 = 10. Everything is an integer so not sure why it's failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Percentage calculation of width is incorrect
3 participants