fix(common): escape query selector used when anchor scrolling (#29577)
When an anchor scroll happens, we run document.querySelector. This value can be taken directly from the user. Therefore it's possible to throw an error on scrolling, which can cause the application to fail. This PR escapes the selector before using it. Related to #28193 [Internal discussion](https://groups.google.com/a/google.com/forum/#!topic/angular-users/d82GHfmRKLc) PR Close #29577
This commit is contained in:
@ -6,10 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {defineInjectable, inject} from '@angular/core';
|
||||
import {ErrorHandler, defineInjectable, inject} from '@angular/core';
|
||||
|
||||
import {DOCUMENT} from './dom_tokens';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Defines a scroll position manager. Implemented by `BrowserViewportScroller`.
|
||||
*
|
||||
@ -19,8 +21,10 @@ export abstract class ViewportScroller {
|
||||
// De-sugared tree-shakable injection
|
||||
// See #23917
|
||||
/** @nocollapse */
|
||||
static ngInjectableDef = defineInjectable(
|
||||
{providedIn: 'root', factory: () => new BrowserViewportScroller(inject(DOCUMENT), window)});
|
||||
static ngInjectableDef = defineInjectable({
|
||||
providedIn: 'root',
|
||||
factory: () => new BrowserViewportScroller(inject(DOCUMENT), window, inject(ErrorHandler))
|
||||
});
|
||||
|
||||
/**
|
||||
* Configures the top offset used when scrolling to an anchor.
|
||||
@ -62,7 +66,7 @@ export abstract class ViewportScroller {
|
||||
export class BrowserViewportScroller implements ViewportScroller {
|
||||
private offset: () => [number, number] = () => [0, 0];
|
||||
|
||||
constructor(private document: any, private window: any) {}
|
||||
constructor(private document: any, private window: any, private errorHandler: ErrorHandler) {}
|
||||
|
||||
/**
|
||||
* Configures the top offset used when scrolling to an anchor.
|
||||
@ -106,15 +110,26 @@ export class BrowserViewportScroller implements ViewportScroller {
|
||||
*/
|
||||
scrollToAnchor(anchor: string): void {
|
||||
if (this.supportScrollRestoration()) {
|
||||
const elSelectedById = this.document.querySelector(`#${anchor}`);
|
||||
if (elSelectedById) {
|
||||
this.scrollToElement(elSelectedById);
|
||||
return;
|
||||
// Escape anything passed to `querySelector` as it can throw errors and stop the application
|
||||
// from working if invalid values are passed.
|
||||
if (this.window.CSS && this.window.CSS.escape) {
|
||||
anchor = this.window.CSS.escape(anchor);
|
||||
} else {
|
||||
anchor = anchor.replace(/(\"|\'\ |:|\.|\[|\]|,|=)/g, '\\$1');
|
||||
}
|
||||
const elSelectedByName = this.document.querySelector(`[name='${anchor}']`);
|
||||
if (elSelectedByName) {
|
||||
this.scrollToElement(elSelectedByName);
|
||||
return;
|
||||
try {
|
||||
const elSelectedById = this.document.querySelector(`#${anchor}`);
|
||||
if (elSelectedById) {
|
||||
this.scrollToElement(elSelectedById);
|
||||
return;
|
||||
}
|
||||
const elSelectedByName = this.document.querySelector(`[name='${anchor}']`);
|
||||
if (elSelectedByName) {
|
||||
this.scrollToElement(elSelectedByName);
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
this.errorHandler.handleError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user