a11y-canvas: Add heuristics to detect inaccessible canvases. [chromium/src : main]

0 views
Skip to first unread message

Ramin Halavati (Gerrit)

unread,
May 27, 2026, 8:02:39 AM (5 days ago) May 27
to Ramin Halavati, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
Attention needed from Justin Novosad and Lucas Radaelli

New activity on the change

Open in Gerrit

Related details

Attention is currently required from:
  • Justin Novosad
  • Lucas Radaelli
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
Gerrit-Change-Number: 7852260
Gerrit-PatchSet: 8
Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
Gerrit-Attention: Justin Novosad <ju...@chromium.org>
Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
Gerrit-Comment-Date: Wed, 27 May 2026 12:02:11 +0000
Gerrit-HasComments: No
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

AI Code Reviewer (Gerrit)

unread,
May 27, 2026, 8:05:02 AM (5 days ago) May 27
to Ramin Halavati, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
Attention needed from Justin Novosad and Lucas Radaelli

AI Code Reviewer added 1 comment

File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
Line 164, Patchset 8 (Latest): TaskRunnerTimer<AccessibilityManager> uma_timer_;
AI Code Reviewer . unresolved

nit: Blink Style Guide: Naming - Precede boolean values with words like “is” and “did”. Consider renaming `uma_recorded_` to `is_uma_recorded_` or `did_record_uma_`.

To keep this interaction as brief and non-intrusive as possible, please consider responding with one of following options:
**Done** | **OK But Won't Fix**: reason | **Later**: b/<bug_id> | **Invalid:** reason


_This comment was generated by [Experimental Blink C++ Code Review Agent](http://go/blink-c++-code-review-agent)._
_AI reviews can sometimes be inaccurate; We appreciate your 🙏 feedback 🙏 to help us improve._
_[File a bug](http://go/blink-c++-code-review-agent-feedback) | [Provide feedback on chat](https://chat.google.com/room/AAQA0zhQHe0?cls=4) | [Opt-out](https://ganpati2.corp.google.com/group/peep-genai-blink-agent-optout.prod)_

Open in Gerrit

Related details

Attention is currently required from:
  • Justin Novosad
  • Lucas Radaelli
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
    Gerrit-Change-Number: 7852260
    Gerrit-PatchSet: 8
    Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
    Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
    Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
    Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
    Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
    Gerrit-Attention: Justin Novosad <ju...@chromium.org>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Wed, 27 May 2026 12:04:59 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Justin Novosad (Gerrit)

    unread,
    May 27, 2026, 11:00:57 AM (5 days ago) May 27
    to Ramin Halavati, AI Code Reviewer, Lucas Radaelli, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Lucas Radaelli and Ramin Halavati

    Justin Novosad added 8 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
    Line 94, Patchset 8 (Latest): // LINT.IfChange(CanvasHeuristicResult)
    Justin Novosad . unresolved

    nit: The label `CanvasHeuristicResult` should probably be changed to match the name of the enum.

    Line 86, Patchset 8 (Latest):class CORE_EXPORT AccessibilityManager {
    Justin Novosad . unresolved

    This class is big enough to be put in its own file. Also, you should probably give it a name that makes it clear that it is canvas-specific. Suggestion: HTMLCanvasAccessibilityManager

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
    Line 1652, Patchset 8 (Latest): accessibility_manager_->SetVisible();
    Justin Novosad . unresolved
    How come we never reset AccessibilityManager::is_visible_ to false when the canvas is not longer visible?  I wouldhave expected this code to be 
    ```
    if (accessibility_manager_) {
    accessibility_manager_->SetVisible(displayed_);
    }
    ```
    If there is a reason not to do this, it should be explained in a comment, because right now it looks like a bug.
    Line 1763, Patchset 8 (Latest): if (accessibility_manager_) {
    Justin Novosad . unresolved

    I don't think ContextDestroyed is the right place to put this code. The purpose of ContextDestroyed is to manage the mutual reference between the canvas element and the context because the object garbage collection order is non-deterministic. The accessibility manager does not interact with this reference cycle and is not impacted by the destruction order between canvas and context. Calling `FlushUmaIfNeeded` in the `Dispose` method (as you're already doing) should be sufficient.

    Also, Why do we even need to call `FlushUmaIfNeeded` given that the destructor of AccesibilityManager already does it. If it's just to make unit tests work, you could instead teardown the document and trigger GC manually in your tests.

    Line 1850, Patchset 8 (Latest): bool first_child_is_element = firstElementChild() != nullptr;
    Justin Novosad . unresolved

    The name of this variable does not correctly express what you're checking. Consider `has_child_element` or `has_fallback_element`

    Line 2112, Patchset 8 (Latest): ElementTraversal::FirstChild(*canvas_element) != nullptr;
    Justin Novosad . unresolved

    This replicates logic you added in `HTMLCanvasElement::ChildrenChanged`. Consider adding a `HasFallbackElementContent()` method to HTMLCanvasElement. That way if we ever need to modify how this is determined, we won't need to worry about tracking down all the places in the code that need updating.

    Line 2124, Patchset 8 (Latest): if (canvas_element->IsDisplayed()) {
    SetVisible();
    }
    Justin Novosad . unresolved

    Why not `SetVisible(canvas_element->IsDisplayed());`? Is there a reason _is_visible should never be reset to false?

    Line 2199, Patchset 8 (Latest): uma_timer_.StartOneShot(base::Seconds(1), FROM_HERE);
    Justin Novosad . unresolved

    Why is this delay necessary? and why one second?

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Lucas Radaelli
    • Ramin Halavati
    Gerrit-Attention: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Wed, 27 May 2026 15:00:51 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Ramin Halavati (Gerrit)

    unread,
    May 28, 2026, 7:43:45 AM (4 days ago) May 28
    to Ramin Halavati, AI Code Reviewer, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad and Lucas Radaelli

    Ramin Halavati added 9 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
    Line 164, Patchset 8: TaskRunnerTimer<AccessibilityManager> uma_timer_;
    AI Code Reviewer . resolved

    nit: Blink Style Guide: Naming - Precede boolean values with words like “is” and “did”. Consider renaming `uma_recorded_` to `is_uma_recorded_` or `did_record_uma_`.

    To keep this interaction as brief and non-intrusive as possible, please consider responding with one of following options:
    **Done** | **OK But Won't Fix**: reason | **Later**: b/<bug_id> | **Invalid:** reason


    _This comment was generated by [Experimental Blink C++ Code Review Agent](http://go/blink-c++-code-review-agent)._
    _AI reviews can sometimes be inaccurate; We appreciate your 🙏 feedback 🙏 to help us improve._
    _[File a bug](http://go/blink-c++-code-review-agent-feedback) | [Provide feedback on chat](https://chat.google.com/room/AAQA0zhQHe0?cls=4) | [Opt-out](https://ganpati2.corp.google.com/group/peep-genai-blink-agent-optout.prod)_

    Ramin Halavati

    Done

    Line 94, Patchset 8: // LINT.IfChange(CanvasHeuristicResult)
    Justin Novosad . resolved

    nit: The label `CanvasHeuristicResult` should probably be changed to match the name of the enum.

    Ramin Halavati

    Done

    Line 86, Patchset 8:class CORE_EXPORT AccessibilityManager {
    Justin Novosad . resolved

    This class is big enough to be put in its own file. Also, you should probably give it a name that makes it clear that it is canvas-specific. Suggestion: HTMLCanvasAccessibilityManager

    Ramin Halavati

    Done

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
    Line 1652, Patchset 8: accessibility_manager_->SetVisible();
    Justin Novosad . unresolved
    How come we never reset AccessibilityManager::is_visible_ to false when the canvas is not longer visible?  I wouldhave expected this code to be 
    ```
    if (accessibility_manager_) {
    accessibility_manager_->SetVisible(displayed_);
    }
    ```
    If there is a reason not to do this, it should be explained in a comment, because right now it looks like a bug.
    Ramin Halavati

    Done,
    The actual reason was (leaked from my subconscious) that I had not made up my mind about whether we want to switch back from "needs a11y support" to "doesn't need" or not. But now that I thought more about it, if we concluded (right or wrong) that it doesn't need a11y support, it's better to avoid unnecessary computations.

    Line 1763, Patchset 8: if (accessibility_manager_) {
    Justin Novosad . unresolved

    I don't think ContextDestroyed is the right place to put this code. The purpose of ContextDestroyed is to manage the mutual reference between the canvas element and the context because the object garbage collection order is non-deterministic. The accessibility manager does not interact with this reference cycle and is not impacted by the destruction order between canvas and context. Calling `FlushUmaIfNeeded` in the `Dispose` method (as you're already doing) should be sufficient.

    Also, Why do we even need to call `FlushUmaIfNeeded` given that the destructor of AccesibilityManager already does it. If it's just to make unit tests work, you could instead teardown the document and trigger GC manually in your tests.

    Ramin Halavati

    Part 1: You are correct, removed it.
    Part 2: As far as I can see, the destructor isn't reliably called. I added logs in the constructor and descructor and the later is almost never seen. I don't know what garbage collection is doing behind the scenes, but I am even wondering if it's useful to be in the destructor or I would just remove it from there.

    Line 1850, Patchset 8: bool first_child_is_element = firstElementChild() != nullptr;
    Justin Novosad . resolved

    The name of this variable does not correctly express what you're checking. Consider `has_child_element` or `has_fallback_element`

    Ramin Halavati

    Done

    Line 2112, Patchset 8: ElementTraversal::FirstChild(*canvas_element) != nullptr;
    Justin Novosad . resolved

    This replicates logic you added in `HTMLCanvasElement::ChildrenChanged`. Consider adding a `HasFallbackElementContent()` method to HTMLCanvasElement. That way if we ever need to modify how this is determined, we won't need to worry about tracking down all the places in the code that need updating.

    Ramin Halavati

    Done

    Line 2124, Patchset 8: if (canvas_element->IsDisplayed()) {
    SetVisible();
    }
    Justin Novosad . resolved

    Why not `SetVisible(canvas_element->IsDisplayed());`? Is there a reason _is_visible should never be reset to false?

    Ramin Halavati

    Done

    Line 2199, Patchset 8: uma_timer_.StartOneShot(base::Seconds(1), FROM_HERE);
    Justin Novosad . unresolved

    Why is this delay necessary? and why one second?

    Ramin Halavati

    Added a constant and a comment on top of the new file. One second is chosen arbitrarily. I have no idea about how to choose it based on knowledge.
    I expect that most probably after the initial load of the page (and a11y is/gets enabled), the canvas is already either accessible or not, don't you think so?

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Lucas Radaelli
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
    Gerrit-Change-Number: 7852260
    Gerrit-PatchSet: 10
    Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
    Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
    Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
    Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
    Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
    Gerrit-Attention: Justin Novosad <ju...@chromium.org>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Thu, 28 May 2026 11:43:23 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Justin Novosad <ju...@chromium.org>
    Comment-In-Reply-To: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Lucas Radaelli (Gerrit)

    unread,
    May 28, 2026, 12:48:54 PM (4 days ago) May 28
    to Ramin Halavati, AI Code Reviewer, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad and Ramin Halavati

    Lucas Radaelli added 10 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_accessibility_manager.h
    Line 120, Patchset 10 (Latest): void OnUpdate();
    Lucas Radaelli . unresolved

    nit: style guide defines methods before variables, so please move this up.

    Line 80, Patchset 10 (Latest): // its accessibility related information.
    Lucas Radaelli . unresolved

    nit: consider moving all this logic to the .cc

    Line 64, Patchset 10 (Latest): if (has_layoutsubtree_ == has_layoutsubtree) {
    Lucas Radaelli . unresolved

    nit: prefer to move these to the .cc

    Line 53, Patchset 10 (Latest): void ReadAriaAttributes(const HTMLCanvasElement* element);
    Lucas Radaelli . unresolved

    why do we need to pass by argument the html canvas element if it is received in the constructor?

    Line 28, Patchset 10 (Latest): HTMLCanvasElement* canvas_element);
    Lucas Radaelli . unresolved

    nit: please document lifecycle of this pointer. The html element needs to outlive this manager?

    Line 21, Patchset 10 (Latest):// canvas element, determines if the canvas element needs accessibility support,
    Lucas Radaelli . unresolved

    nit: is it one manager per canvas html element or a manager can hold multiple elements?

    File third_party/blink/renderer/core/html/canvas/html_canvas_accessibility_manager.cc
    Line 17, Patchset 10 (Latest):// Delay to ensure the canvas element has had a chance to update its
    Lucas Radaelli . unresolved

    nit: add blank line between namespace and first comment.

    Line 23, Patchset 10 (Latest):
    Lucas Radaelli . unresolved

    nit: add the anonymousnamespace inside the blink one. namespace blink {namespace { } // namespace ...... ... } // namespace blink.

    Line 55, Patchset 10 (Latest): bool has_fallback_element_content =
    Lucas Radaelli . unresolved

    Now I understand why you are passing the element on methods, just document in the constructor (.h), that you are not holding a pointer to the canvas.

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
    Line 452, Patchset 10 (Latest): std::unique_ptr<HTMLCanvasAccessibilityManager> accessibility_manager_;
    Lucas Radaelli . unresolved

    nit: can we make the AXObject that represents the canvas hold the object, or even the AXObjectCache? Here, every canvas element will have a pointer / variable to this object, even when it is not used, and that will increase memory usage on the blink side.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Ramin Halavati
    Gerrit-Attention: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Comment-Date: Thu, 28 May 2026 16:48:42 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Ramin Halavati (Gerrit)

    unread,
    May 29, 2026, 3:59:38 AM (3 days ago) May 29
    to Ramin Halavati, AI Code Reviewer, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad and Lucas Radaelli

    Ramin Halavati added 10 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_accessibility_manager.h
    Line 120, Patchset 10: void OnUpdate();
    Lucas Radaelli . resolved

    nit: style guide defines methods before variables, so please move this up.

    Ramin Halavati

    Done

    Line 80, Patchset 10: // its accessibility related information.
    Lucas Radaelli . resolved

    nit: consider moving all this logic to the .cc

    Ramin Halavati

    Done

    Line 64, Patchset 10: if (has_layoutsubtree_ == has_layoutsubtree) {
    Lucas Radaelli . resolved

    nit: prefer to move these to the .cc

    Ramin Halavati

    Done

    Line 53, Patchset 10: void ReadAriaAttributes(const HTMLCanvasElement* element);
    Lucas Radaelli . resolved

    why do we need to pass by argument the html canvas element if it is received in the constructor?

    Ramin Halavati

    I changed the code to keep the owner canvas elements, so the argument is not needed anymore.

    Line 28, Patchset 10: HTMLCanvasElement* canvas_element);
    Lucas Radaelli . resolved

    nit: please document lifecycle of this pointer. The html element needs to outlive this manager?

    Ramin Halavati

    Done

    Line 21, Patchset 10:// canvas element, determines if the canvas element needs accessibility support,
    Lucas Radaelli . resolved

    nit: is it one manager per canvas html element or a manager can hold multiple elements?

    Ramin Halavati

    Just one, updated the comment.

    File third_party/blink/renderer/core/html/canvas/html_canvas_accessibility_manager.cc
    Line 17, Patchset 10:// Delay to ensure the canvas element has had a chance to update its
    Lucas Radaelli . resolved

    nit: add blank line between namespace and first comment.

    Ramin Halavati

    Done

    Line 23, Patchset 10:
    Lucas Radaelli . resolved

    nit: add the anonymousnamespace inside the blink one. namespace blink {namespace { } // namespace ...... ... } // namespace blink.

    Ramin Halavati

    Done

    Line 55, Patchset 10: bool has_fallback_element_content =
    Lucas Radaelli . resolved

    Now I understand why you are passing the element on methods, just document in the constructor (.h), that you are not holding a pointer to the canvas.

    Ramin Halavati

    Actually I changed it to the other way! I noted that I need the pointer to the canvas later at least for UKM collections, and it looks neater if the owner link is kept.

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
    Line 452, Patchset 10: std::unique_ptr<HTMLCanvasAccessibilityManager> accessibility_manager_;
    Lucas Radaelli . unresolved

    nit: can we make the AXObject that represents the canvas hold the object, or even the AXObjectCache? Here, every canvas element will have a pointer / variable to this object, even when it is not used, and that will increase memory usage on the blink side.

    Ramin Halavati

    `accessibility_manager_` exists per each canvas element, and is created only when AXObject is created and therefore as long as a11y is not enabled, it does not exist.
    Keeping it at this level gives us easier access from Canvas element (for updates regarding rendering text, etc.), and to Canvas element for reading feature and sending requests like storing UKM.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Lucas Radaelli
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
    Gerrit-Change-Number: 7852260
    Gerrit-PatchSet: 12
    Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
    Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
    Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
    Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
    Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
    Gerrit-Attention: Justin Novosad <ju...@chromium.org>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Fri, 29 May 2026 07:59:16 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Lucas Radaelli <lucasr...@google.com>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Justin Novosad (Gerrit)

    unread,
    May 29, 2026, 9:54:13 AM (3 days ago) May 29
    to Ramin Halavati, AI Code Reviewer, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad, Lucas Radaelli and Ramin Halavati

    Justin Novosad added 3 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
    Line 1652, Patchset 8: accessibility_manager_->SetVisible();
    Justin Novosad . resolved
    How come we never reset AccessibilityManager::is_visible_ to false when the canvas is not longer visible?  I wouldhave expected this code to be 
    ```
    if (accessibility_manager_) {
    accessibility_manager_->SetVisible(displayed_);
    }
    ```
    If there is a reason not to do this, it should be explained in a comment, because right now it looks like a bug.
    Ramin Halavati

    Done,
    The actual reason was (leaked from my subconscious) that I had not made up my mind about whether we want to switch back from "needs a11y support" to "doesn't need" or not. But now that I thought more about it, if we concluded (right or wrong) that it doesn't need a11y support, it's better to avoid unnecessary computations.

    Justin Novosad

    Acknowledged

    Line 1763, Patchset 8: if (accessibility_manager_) {
    Justin Novosad . unresolved

    I don't think ContextDestroyed is the right place to put this code. The purpose of ContextDestroyed is to manage the mutual reference between the canvas element and the context because the object garbage collection order is non-deterministic. The accessibility manager does not interact with this reference cycle and is not impacted by the destruction order between canvas and context. Calling `FlushUmaIfNeeded` in the `Dispose` method (as you're already doing) should be sufficient.

    Also, Why do we even need to call `FlushUmaIfNeeded` given that the destructor of AccesibilityManager already does it. If it's just to make unit tests work, you could instead teardown the document and trigger GC manually in your tests.

    Ramin Halavati

    Part 1: You are correct, removed it.
    Part 2: As far as I can see, the destructor isn't reliably called. I added logs in the constructor and descructor and the later is almost never seen. I don't know what garbage collection is doing behind the scenes, but I am even wondering if it's useful to be in the destructor or I would just remove it from there.

    Justin Novosad

    The destructor does not get called because this is a garbage collected object. This class does however register a prefinalizer (i.e. the `Dispose` method), which oilpan will call prior to de-allocation. This `Dispose` method is where you'd want to call `FlushUmaIfNeeded`. To trigger immediate finalization consistently in a test, you need to remove all references to the object, for example either tear down the document or remove the canvas to the DOM and set all variables that point the canvas to `nullptr`. Then you can manually force an immediate GC by calling `ThreadState::Current()->CollectAllGarbageForTesting()`. This should consistently result in HTMLCanvasElement::Dispose() being called.

    Line 2199, Patchset 8: uma_timer_.StartOneShot(base::Seconds(1), FROM_HERE);
    Justin Novosad . unresolved

    Why is this delay necessary? and why one second?

    Ramin Halavati

    Added a constant and a comment on top of the new file. One second is chosen arbitrarily. I have no idea about how to choose it based on knowledge.
    I expect that most probably after the initial load of the page (and a11y is/gets enabled), the canvas is already either accessible or not, don't you think so?

    Justin Novosad

    I still don't understand why the delay is necessary. Is it because SetHeuristicResult gets called multiple times and you only want to capture the last value it is set to? If that is the case, I think this way of doing thing adds unnecessary complexity. Perhaps you could just record a histogram sample in `Dispose`.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Lucas Radaelli
    • Ramin Halavati
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
    Gerrit-Change-Number: 7852260
    Gerrit-PatchSet: 12
    Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
    Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
    Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
    Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Justin Novosad <ju...@google.com>
    Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
    Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
    Gerrit-Attention: Justin Novosad <ju...@chromium.org>
    Gerrit-Attention: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Fri, 29 May 2026 13:53:59 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Justin Novosad <ju...@chromium.org>
    Comment-In-Reply-To: Ramin Halavati <rhal...@chromium.org>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Lucas Radaelli (Gerrit)

    unread,
    May 29, 2026, 12:49:01 PM (3 days ago) May 29
    to Ramin Halavati, Justin Novosad, AI Code Reviewer, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad and Ramin Halavati

    Lucas Radaelli added 1 comment

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
    Line 452, Patchset 10: std::unique_ptr<HTMLCanvasAccessibilityManager> accessibility_manager_;
    Lucas Radaelli . unresolved

    nit: can we make the AXObject that represents the canvas hold the object, or even the AXObjectCache? Here, every canvas element will have a pointer / variable to this object, even when it is not used, and that will increase memory usage on the blink side.

    Ramin Halavati

    `accessibility_manager_` exists per each canvas element, and is created only when AXObject is created and therefore as long as a11y is not enabled, it does not exist.
    Keeping it at this level gives us easier access from Canvas element (for updates regarding rendering text, etc.), and to Canvas element for reading feature and sending requests like storing UKM.

    Lucas Radaelli

    but doesn't that mean that every time a canvas element is initiated on a page, even though a11y is not on, the object still holds a pointer to it, wasting memory?

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Ramin Halavati
    Gerrit-Comment-Date: Fri, 29 May 2026 16:48:49 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Ramin Halavati <rhal...@chromium.org>
    Comment-In-Reply-To: Lucas Radaelli <lucasr...@google.com>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Ramin Halavati (Gerrit)

    unread,
    7:39 AM (8 hours ago) 7:39 AM
    to Ramin Halavati, Justin Novosad, AI Code Reviewer, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad, Justin Novosad and Lucas Radaelli

    Ramin Halavati added 3 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.h
    Line 452, Patchset 10: std::unique_ptr<HTMLCanvasAccessibilityManager> accessibility_manager_;
    Lucas Radaelli . unresolved

    nit: can we make the AXObject that represents the canvas hold the object, or even the AXObjectCache? Here, every canvas element will have a pointer / variable to this object, even when it is not used, and that will increase memory usage on the blink side.

    Ramin Halavati

    `accessibility_manager_` exists per each canvas element, and is created only when AXObject is created and therefore as long as a11y is not enabled, it does not exist.
    Keeping it at this level gives us easier access from Canvas element (for updates regarding rendering text, etc.), and to Canvas element for reading feature and sending requests like storing UKM.

    Lucas Radaelli

    but doesn't that mean that every time a canvas element is initiated on a page, even though a11y is not on, the object still holds a pointer to it, wasting memory?

    Ramin Halavati

    You are right, each canvas will have an uninitialized `Member<HTMLCanvasAccessibilityManager>` object which takes 4-8 bytes.
    However moving ownership of this object to AXObject would require a lot of boilerplate code to get it when it is needed in the canvas object (to pass data and events). Also note that due to the layering of Blink, it would be quite expensive (in terms of code) to move the class to `modules/accessibility` and make it available by an object in `core`.
    I can create a CL to showcase it if you insist, but I think considering all these required boilerplate, the current solution is easier to read and maybe even more compact in memory.
    We can reconsider this decision later when the code is in a stable state and optimizations can be done with fewer considerations.

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
    Line 1763, Patchset 8: if (accessibility_manager_) {
    Justin Novosad . unresolved

    I don't think ContextDestroyed is the right place to put this code. The purpose of ContextDestroyed is to manage the mutual reference between the canvas element and the context because the object garbage collection order is non-deterministic. The accessibility manager does not interact with this reference cycle and is not impacted by the destruction order between canvas and context. Calling `FlushUmaIfNeeded` in the `Dispose` method (as you're already doing) should be sufficient.

    Also, Why do we even need to call `FlushUmaIfNeeded` given that the destructor of AccesibilityManager already does it. If it's just to make unit tests work, you could instead teardown the document and trigger GC manually in your tests.

    Ramin Halavati

    Part 1: You are correct, removed it.
    Part 2: As far as I can see, the destructor isn't reliably called. I added logs in the constructor and descructor and the later is almost never seen. I don't know what garbage collection is doing behind the scenes, but I am even wondering if it's useful to be in the destructor or I would just remove it from there.

    Justin Novosad

    The destructor does not get called because this is a garbage collected object. This class does however register a prefinalizer (i.e. the `Dispose` method), which oilpan will call prior to de-allocation. This `Dispose` method is where you'd want to call `FlushUmaIfNeeded`. To trigger immediate finalization consistently in a test, you need to remove all references to the object, for example either tear down the document or remove the canvas to the DOM and set all variables that point the canvas to `nullptr`. Then you can manually force an immediate GC by calling `ThreadState::Current()->CollectAllGarbageForTesting()`. This should consistently result in HTMLCanvasElement::Dispose() being called.

    Ramin Halavati

    Please let me know to schedule a quick call if this is becoming frustrating or I am not getting something here.
    A few points before the actual reply:
    1- I have not added any of the mechanism here to make tests pass, it's just based on my observation on how things run (on the surface).
    2- Frankly I am not sure how often the heuristic states change on a canvas and this UMA is just to give us a rough estimate of how much different cases happen. I have added a timer based on the assumption that if it doesn't change for 1s, that is good enough to collect.

    Gemini believes (and it matches what I observe) that using garbage collection pre-finalizers is an anti-pattern for important tasks because GC timing is non-deterministic and skipped on shutdown. It says if there would be no memory pressure, GC might be delayed until actual browser shutdown and at that time Chromium performs a fast shutdown which will result in renderer process termination and skipping final garbage collection.
    It's recommended preferred solution (implied in the class comment of ExecutionContextLifecycleObserver) is to do it in `ContextDestroyed`.

    My personal observations (based on LOGs) also show that the only reliable call is in `HTMLCanvasElement::ContextDestroyed()` (which is only skipped if browser shuts down while the tab is still on. So my preference is still `ContextDestroyed` + timer and if timer looks bad, just `ContextDestroyed`.

    WDYT?

    Line 2199, Patchset 8: uma_timer_.StartOneShot(base::Seconds(1), FROM_HERE);
    Justin Novosad . unresolved

    Why is this delay necessary? and why one second?

    Ramin Halavati

    Added a constant and a comment on top of the new file. One second is chosen arbitrarily. I have no idea about how to choose it based on knowledge.
    I expect that most probably after the initial load of the page (and a11y is/gets enabled), the canvas is already either accessible or not, don't you think so?

    Justin Novosad

    I still don't understand why the delay is necessary. Is it because SetHeuristicResult gets called multiple times and you only want to capture the last value it is set to? If that is the case, I think this way of doing thing adds unnecessary complexity. Perhaps you could just record a histogram sample in `Dispose`.

    Ramin Halavati

    Replied in more details on the other comment. This metric collected just as a best-effort, so it doesn't matter that much if it is not really the last value, and a stable value is good enough. (I will update metric's description if we proceed with the timer.)

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Justin Novosad
    • Lucas Radaelli
    Gerrit-Attention: Justin Novosad <ju...@google.com>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Mon, 01 Jun 2026 11:39:26 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Justin Novosad <ju...@chromium.org>
    Comment-In-Reply-To: Ramin Halavati <rhal...@chromium.org>
    Comment-In-Reply-To: Justin Novosad <ju...@google.com>
    Comment-In-Reply-To: Lucas Radaelli <lucasr...@google.com>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Justin Novosad (Gerrit)

    unread,
    9:35 AM (6 hours ago) 9:35 AM
    to Ramin Halavati, AI Code Reviewer, Lucas Radaelli, Justin Novosad, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Justin Novosad, Lucas Radaelli and Ramin Halavati

    Justin Novosad added 2 comments

    File third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
    Line 1763, Patchset 8: if (accessibility_manager_) {
    Justin Novosad . unresolved

    I don't think ContextDestroyed is the right place to put this code. The purpose of ContextDestroyed is to manage the mutual reference between the canvas element and the context because the object garbage collection order is non-deterministic. The accessibility manager does not interact with this reference cycle and is not impacted by the destruction order between canvas and context. Calling `FlushUmaIfNeeded` in the `Dispose` method (as you're already doing) should be sufficient.

    Also, Why do we even need to call `FlushUmaIfNeeded` given that the destructor of AccesibilityManager already does it. If it's just to make unit tests work, you could instead teardown the document and trigger GC manually in your tests.

    Ramin Halavati

    Part 1: You are correct, removed it.
    Part 2: As far as I can see, the destructor isn't reliably called. I added logs in the constructor and descructor and the later is almost never seen. I don't know what garbage collection is doing behind the scenes, but I am even wondering if it's useful to be in the destructor or I would just remove it from there.

    Justin Novosad

    The destructor does not get called because this is a garbage collected object. This class does however register a prefinalizer (i.e. the `Dispose` method), which oilpan will call prior to de-allocation. This `Dispose` method is where you'd want to call `FlushUmaIfNeeded`. To trigger immediate finalization consistently in a test, you need to remove all references to the object, for example either tear down the document or remove the canvas to the DOM and set all variables that point the canvas to `nullptr`. Then you can manually force an immediate GC by calling `ThreadState::Current()->CollectAllGarbageForTesting()`. This should consistently result in HTMLCanvasElement::Dispose() being called.

    Ramin Halavati

    Please let me know to schedule a quick call if this is becoming frustrating or I am not getting something here.
    A few points before the actual reply:
    1- I have not added any of the mechanism here to make tests pass, it's just based on my observation on how things run (on the surface).
    2- Frankly I am not sure how often the heuristic states change on a canvas and this UMA is just to give us a rough estimate of how much different cases happen. I have added a timer based on the assumption that if it doesn't change for 1s, that is good enough to collect.

    Gemini believes (and it matches what I observe) that using garbage collection pre-finalizers is an anti-pattern for important tasks because GC timing is non-deterministic and skipped on shutdown. It says if there would be no memory pressure, GC might be delayed until actual browser shutdown and at that time Chromium performs a fast shutdown which will result in renderer process termination and skipping final garbage collection.
    It's recommended preferred solution (implied in the class comment of ExecutionContextLifecycleObserver) is to do it in `ContextDestroyed`.

    My personal observations (based on LOGs) also show that the only reliable call is in `HTMLCanvasElement::ContextDestroyed()` (which is only skipped if browser shuts down while the tab is still on. So my preference is still `ContextDestroyed` + timer and if timer looks bad, just `ContextDestroyed`.

    WDYT?

    Justin Novosad

    Thanks for the explanations. This makes sense. Please add a few more comments in the code to explain theses things.

    Line 2199, Patchset 8: uma_timer_.StartOneShot(base::Seconds(1), FROM_HERE);
    Justin Novosad . resolved

    Why is this delay necessary? and why one second?

    Ramin Halavati

    Added a constant and a comment on top of the new file. One second is chosen arbitrarily. I have no idea about how to choose it based on knowledge.
    I expect that most probably after the initial load of the page (and a11y is/gets enabled), the canvas is already either accessible or not, don't you think so?

    Justin Novosad

    I still don't understand why the delay is necessary. Is it because SetHeuristicResult gets called multiple times and you only want to capture the last value it is set to? If that is the case, I think this way of doing thing adds unnecessary complexity. Perhaps you could just record a histogram sample in `Dispose`.

    Ramin Halavati

    Replied in more details on the other comment. This metric collected just as a best-effort, so it doesn't matter that much if it is not really the last value, and a stable value is good enough. (I will update metric's description if we proceed with the timer.)

    Justin Novosad

    Acknowledged

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Justin Novosad
    • Lucas Radaelli
    • Ramin Halavati
    Gerrit-Attention: Ramin Halavati <rhal...@chromium.org>
    Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
    Gerrit-Comment-Date: Mon, 01 Jun 2026 13:35:21 +0000
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Justin Novosad (Gerrit)

    unread,
    9:36 AM (6 hours ago) 9:36 AM
    to Ramin Halavati, Justin Novosad, AI Code Reviewer, Lucas Radaelli, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
    Attention needed from Lucas Radaelli and Ramin Halavati

    Justin Novosad voted Code-Review+1

    Code-Review+1
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Lucas Radaelli
    • Ramin Halavati
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
      Gerrit-Change-Number: 7852260
      Gerrit-PatchSet: 12
      Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
      Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
      Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
      Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
      Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
      Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
      Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
      Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
      Gerrit-CC: Justin Novosad <ju...@google.com>
      Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
      Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
      Gerrit-Attention: Ramin Halavati <rhal...@chromium.org>
      Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
      Gerrit-Comment-Date: Mon, 01 Jun 2026 13:35:56 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Ramin Halavati (Gerrit)

      unread,
      11:48 AM (4 hours ago) 11:48 AM
      to Ramin Halavati, Evan Liu, Justin Novosad, Justin Novosad, AI Code Reviewer, Lucas Radaelli, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
      Attention needed from Evan Liu and Lucas Radaelli

      Ramin Halavati added 1 comment

      File third_party/blink/renderer/core/html/canvas/html_canvas_element.cc
      Line 1763, Patchset 8: if (accessibility_manager_) {
      Justin Novosad . resolved

      I don't think ContextDestroyed is the right place to put this code. The purpose of ContextDestroyed is to manage the mutual reference between the canvas element and the context because the object garbage collection order is non-deterministic. The accessibility manager does not interact with this reference cycle and is not impacted by the destruction order between canvas and context. Calling `FlushUmaIfNeeded` in the `Dispose` method (as you're already doing) should be sufficient.

      Also, Why do we even need to call `FlushUmaIfNeeded` given that the destructor of AccesibilityManager already does it. If it's just to make unit tests work, you could instead teardown the document and trigger GC manually in your tests.

      Ramin Halavati

      Part 1: You are correct, removed it.
      Part 2: As far as I can see, the destructor isn't reliably called. I added logs in the constructor and descructor and the later is almost never seen. I don't know what garbage collection is doing behind the scenes, but I am even wondering if it's useful to be in the destructor or I would just remove it from there.

      Justin Novosad

      The destructor does not get called because this is a garbage collected object. This class does however register a prefinalizer (i.e. the `Dispose` method), which oilpan will call prior to de-allocation. This `Dispose` method is where you'd want to call `FlushUmaIfNeeded`. To trigger immediate finalization consistently in a test, you need to remove all references to the object, for example either tear down the document or remove the canvas to the DOM and set all variables that point the canvas to `nullptr`. Then you can manually force an immediate GC by calling `ThreadState::Current()->CollectAllGarbageForTesting()`. This should consistently result in HTMLCanvasElement::Dispose() being called.

      Ramin Halavati

      Please let me know to schedule a quick call if this is becoming frustrating or I am not getting something here.
      A few points before the actual reply:
      1- I have not added any of the mechanism here to make tests pass, it's just based on my observation on how things run (on the surface).
      2- Frankly I am not sure how often the heuristic states change on a canvas and this UMA is just to give us a rough estimate of how much different cases happen. I have added a timer based on the assumption that if it doesn't change for 1s, that is good enough to collect.

      Gemini believes (and it matches what I observe) that using garbage collection pre-finalizers is an anti-pattern for important tasks because GC timing is non-deterministic and skipped on shutdown. It says if there would be no memory pressure, GC might be delayed until actual browser shutdown and at that time Chromium performs a fast shutdown which will result in renderer process termination and skipping final garbage collection.
      It's recommended preferred solution (implied in the class comment of ExecutionContextLifecycleObserver) is to do it in `ContextDestroyed`.

      My personal observations (based on LOGs) also show that the only reliable call is in `HTMLCanvasElement::ContextDestroyed()` (which is only skipped if browser shuts down while the tab is still on. So my preference is still `ContextDestroyed` + timer and if timer looks bad, just `ContextDestroyed`.

      WDYT?

      Justin Novosad

      Thanks for the explanations. This makes sense. Please add a few more comments in the code to explain theses things.

      Ramin Halavati

      Done

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Evan Liu
      • Lucas Radaelli
      Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement satisfiedCode-Review
      • requirement is not satisfiedNo-Unresolved-Comments
      • requirement satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I932faf3a08f1b34a2f5e681e5c2c61808bf9d011
      Gerrit-Change-Number: 7852260
      Gerrit-PatchSet: 14
      Gerrit-Owner: Ramin Halavati <rhal...@chromium.org>
      Gerrit-Reviewer: Evan Liu <ev...@google.com>
      Gerrit-Reviewer: Justin Novosad <ju...@chromium.org>
      Gerrit-Reviewer: Lucas Radaelli <lucasr...@google.com>
      Gerrit-Reviewer: Ramin Halavati <rhal...@chromium.org>
      Gerrit-CC: (Julie)Jeongeun Kim <je_jul...@chromium.org>
      Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
      Gerrit-CC: Akihiro Ota <akihi...@chromium.org>
      Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
      Gerrit-CC: Justin Novosad <ju...@google.com>
      Gerrit-CC: Kevin Babbitt <kbab...@microsoft.com>
      Gerrit-CC: Nektarios Paisios <nek...@chromium.org>
      Gerrit-Attention: Evan Liu <ev...@google.com>
      Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
      Gerrit-Comment-Date: Mon, 01 Jun 2026 15:48:27 +0000
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Evan Liu (Gerrit)

      unread,
      1:06 PM (2 hours ago) 1:06 PM
      to Ramin Halavati, Justin Novosad, Justin Novosad, AI Code Reviewer, Lucas Radaelli, Chromium Metrics Reviews, android-bu...@system.gserviceaccount.com, Chromium LUCI CQ, chromium...@chromium.org, (Julie)Jeongeun Kim, Kevin Babbitt, asvitkine...@chromium.org, abigailbk...@google.com, blink-rev...@chromium.org, blink-rev...@chromium.org, blink-...@chromium.org, dtseng...@chromium.org, francisjp...@google.com, josiah...@chromium.org, kyungjunle...@google.com, lucasrada...@google.com, nektar...@chromium.org, yuzo+...@chromium.org
      Attention needed from Lucas Radaelli and Ramin Halavati

      Evan Liu voted and added 1 comment

      Votes added by Evan Liu

      Code-Review+1

      1 comment

      File third_party/blink/renderer/core/html/canvas/html_canvas_accessibility_manager.h
      Line 40, Patchset 14 (Latest): enum class HeuristicResult {
      Evan Liu . unresolved

      nit: this should be prefixed with the comment
      // These values are persisted to logs. Entries should not be renumbered and
      // numeric values should never be reused.

      https://chromium.googlesource.com/chromium/src/tools/+/HEAD/metrics/histograms/README.md

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Lucas Radaelli
      • Ramin Halavati
      Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement satisfiedCode-Owners
      Gerrit-Attention: Ramin Halavati <rhal...@chromium.org>
      Gerrit-Attention: Lucas Radaelli <lucasr...@google.com>
      Gerrit-Comment-Date: Mon, 01 Jun 2026 17:06:39 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy
      Reply all
      Reply to author
      Forward
      0 new messages