Skip to content

Performance Leak: Unbound addHook registration in _sanitizeLinks causes main-thread blocking and input lag #10988

Description

@BlueShiftNova

Target SharePoint environment

SharePoint Online

What SharePoint development model, framework, SDK or API is this about?

💥 SharePoint Framework

Developer environment

Windows

What browser(s) / client(s) have you tested

  • 💥 Internet Explorer
  • 💥 Microsoft Edge
  • 💥 Google Chrome
  • 💥 FireFox
  • 💥 Safari
  • mobile (iOS/iPadOS)
  • mobile (Android)
  • not applicable
  • other (enter in the "Additional environment details" area below)

Additional environment details

  • Microsoft Windows 11

  • Microsoft Edge Version 151.0.4129.79

  • Google Chrome Version 151.0.7922.72

  • Firefox Version 153.0.3

  • Occurs when accessing Viva Home / SharePoint integrated pages via Microsoft Teams on the Web via Microsoft Edge and Google Chrome

  • Firefox does not appear to be impacted

Describe the bug / error

Over extended usage sessions (typically a few hours), accessing SharePoint-integrated pages via Microsoft Teams on the Web results in severe main-thread blocking and keyboard input lag (up to ~1800ms per keystroke/timer execution).

Profiling in Chromium DevTools reveals a long task originating from Timer fired events that eventually call into _sanitizeLinks() inside sp-pages-assembly_en-us.js.

Image

Every time _sanitizeLinks() is called during component lifecycle updates or tab navigation, it executes:

static _sanitizeLinks() {
	ab.addHook("afterSanitizeAttributes", a => {
		let b = document.createElement("a")
		  , c = a.getAttribute("href");
		c && (b.href = c,
		b.protocol && !this.isSafeLinkProtocol(b.protocol) && a.removeAttribute("href"));
		let d = a.getAttribute("action");
		d && (b.href = d,
		b.protocol && !this.isSafeLinkProtocol(b.protocol) && a.removeAttribute("action"));
		let e = a.getAttribute("xlink:href");
		e && (b.href = e,
		b.protocol && !this.isSafeLinkProtocol(b.protocol) && a.removeAttribute("xlink:href"));
	}
	);
}

Because the ab sanitizer instance persists globally across component mounts, calling addHook repeatedly appends identical callbacks without removing old ones. Over time, thousands of hooks accumulate in the handler array. When sanitization triggers, Chromium executes document.createElement("a") up to three times per hook, causing catastrophic main-thread starvation.

Adding the following lines to the function via a local file override in Chrome resolves the issue:

if (this._hasAttachedHook) return;
this._hasAttachedHook = true;

Steps to reproduce

  1. Open Microsoft Teams on the Web using Google Chrome or Microsoft Edge.
  2. Keep the tab open and actively navigate and participate between different group chats over a few hours to allow lifecycle re-renders to accumulate hooks
  3. Record a Performance Trace in Chrome/Edge DevTools while typing in an input field to capture input lag
  4. Inspect the long task in the Performance flame graph under "Timer Fired"

Expected behavior

_sanitizeLinks() should only attach its afterSanitizeAttributes hook once per sanitizer instance, or detach the hook on component unmount, ensuring single-digit millisecond sanitization performance regardless of session duration.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions