From a7c4ae7a0a341266f75be871ea999d07ebfd16fb Mon Sep 17 00:00:00 2001 From: crisbeto Date: Mon, 3 Aug 2020 21:03:41 +0200 Subject: [PATCH] fix(platform): detect and ignore scrollBehavior polyfills We use the `supportsScrollBehavior` function to check whether to try and pass in the scroll options object to `Scrollable.scrollTo` since passing it in on a browser that doesn't support it won't do anything. The problem is that the only way to detect if scroll behavior is supported is to look for the `scrollBehavior` property on a DOM element's CSS styles, however this will cause scroll behavior polyfills to be ignored since they only modify `Element.prototype.scrollTo`. These changes try to detect whether `scrollTo` has been polyfilled, and if it has, we consider it as being supported. Fixes #17847. --- src/cdk/platform/features/scrolling.ts | 31 +++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/src/cdk/platform/features/scrolling.ts b/src/cdk/platform/features/scrolling.ts index a0acbd2ece61..e3e24e2f84d7 100644 --- a/src/cdk/platform/features/scrolling.ts +++ b/src/cdk/platform/features/scrolling.ts @@ -28,9 +28,38 @@ export const enum RtlScrollAxisType { /** Cached result of the way the browser handles the horizontal scroll axis in RTL mode. */ let rtlScrollAxisType: RtlScrollAxisType|undefined; +/** Cached result of the check that indicates whether the browser supports scroll behaviors. */ +let scrollBehaviorSupported: boolean|undefined; + /** Check whether the browser supports scroll behaviors. */ export function supportsScrollBehavior(): boolean { - return !!(typeof document == 'object' && 'scrollBehavior' in document.documentElement!.style); + if (scrollBehaviorSupported == null) { + // If we're not in the browser, it can't be supported. + if (typeof document !== 'object' || !document) { + scrollBehaviorSupported = false; + } + + // If the element can have a `scrollBehavior` style, we can be sure that it's supported. + if ('scrollBehavior' in document.documentElement!.style) { + scrollBehaviorSupported = true; + } else { + // At this point we have 3 possibilities: `scrollTo` isn't supported at all, it's + // supported but it doesn't handle scroll behavior, or it has been polyfilled. + const scrollToFunction: Function|undefined = Element.prototype.scrollTo; + + if (scrollToFunction) { + // We can detect if the function has been polyfilled by calling `toString` on it. Native + // functions are obfuscated using `[native code]`, whereas if it was overwritten we'd get + // the actual function source. Via https://davidwalsh.name/detect-native-function. Consider + // polyfilled functions as supporting scroll behavior. + scrollBehaviorSupported = !/\{\s*\[native code\]\s*\}/.test(scrollToFunction.toString()); + } else { + scrollBehaviorSupported = false; + } + } + } + + return scrollBehaviorSupported; } /**