Skip to content

Attach user popovers to DOM nodes added after the initial DOM query

What does this MR do?

For #296621 (closed)

Problem

When the user popovers are added, every node must be present in the DOM.

It's not always the case that every user name link is in the DOM tree, so some links that are added later don't trigger the user popover.

Solution

Add a DOM Mutation Observer that rechecks and applies the user popover when the DOM tree is modified.

Notes

  1. The mutation observer only begins observing the DOM tree after the popovers have been applied the first time. It should be okay to have the mutation observer start working before anyone asks for it, but it's safer to make it inert until some code actually initializes the popovers.
  2. A mutation observer cannot observe the same node more than once, and it manages its own deduplication. This isn't a problem right now, but it is a good safety net in case duplicate observations are triggered.
  3. The popover initialization code already checks for nodes that have been bound and won't rebind the popover, so we don't need to worry about accidental duplicate popovers.

Additional Concerns

  1. In my testing, I found that there was enough of a race condition (the body is ready, but the popover initialization simply scans the DOM for certain nodes. If nothing has loaded yet, no popovers will be assigned) that even top level username links didn't get the popover bound (see the before video). This fix also resolves that if any DOM mutations are observed after the popovers are initialized (this is usually the case, even when there aren't any threads, but there may be exceedingly rare situations where the mutation observer is applied, but no DOM mutations ever occur).
    • A fix for this would be to avoid binding directly to the nodes and instead have a single listener that observes events at a very high level and manages the user popover for all pertinent hover events. This way, any arbitrary new DOM nodes will bubble events to the existing handler, rather than requiring a component to be bound directly to each node.
  2. I've disabled one ESLint rule for use before definition. It isn't really true that the function to add the popovers is used before it's defined. The mutation observer needs to be defined to be used to observe the DOM in the function. This could be resolved by constructing a brand new MutationObserver every time the function is called, but that could potentially bind multiple observers. Having a single observer avoids duplicate observers.

Screenshots (strongly suggested)

Before After
Video beforeMaster afterBranch
Notes Note here that even a top-level user name link doesn't show the popover

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • [-] Label as security and @ mention @gitlab-com/gl-security/appsec
  • [-] The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • [-] Security reports checked/validated by a reviewer from the AppSec team
Edited by Thomas Randolph

Merge request reports

Loading