diff --git a/front_end/bindings/IgnoreListManager.js b/front_end/bindings/IgnoreListManager.js index 7ccc3e8b46..034bcd99c8 100644 --- a/front_end/bindings/IgnoreListManager.js +++ b/front_end/bindings/IgnoreListManager.js @@ -3,6 +3,7 @@ // found in the LICENSE file. import * as Common from '../common/common.js'; +import * as Platform from '../platform/platform.js'; import * as SDK from '../sdk/sdk.js'; import * as Workspace from '../workspace/workspace.js'; @@ -366,7 +367,7 @@ export class IgnoreListManager { return ''; } if (!parsedURL.isValid) { - return '^' + url.escapeForRegExp() + '$'; + return '^' + Platform.StringUtilities.escapeForRegExp(url) + '$'; } let name = parsedURL.lastPathComponent; if (name) { @@ -389,7 +390,7 @@ export class IgnoreListManager { } prefix += '.*'; } - return prefix + name.escapeForRegExp() + (url.endsWith(name) ? '$' : '\\b'); + return prefix + Platform.StringUtilities.escapeForRegExp(name) + (url.endsWith(name) ? '$' : '\\b'); } } diff --git a/front_end/elements/StylesSidebarPane.js b/front_end/elements/StylesSidebarPane.js index d46344716d..9f00319184 100644 --- a/front_end/elements/StylesSidebarPane.js +++ b/front_end/elements/StylesSidebarPane.js @@ -32,6 +32,7 @@ import * as Common from '../common/common.js'; import * as Components from '../components/components.js'; import * as Host from '../host/host.js'; import * as InlineEditor from '../inline_editor/inline_editor.js'; +import * as Platform from '../platform/platform.js'; import {ls} from '../platform/platform.js'; import * as Root from '../root/root.js'; import * as SDK from '../sdk/sdk.js'; @@ -250,7 +251,7 @@ export class StylesSidebarPane extends ElementsSidebarPane { input.placeholder = placeholder; function searchHandler() { - const regex = input.value ? new RegExp(input.value.escapeForRegExp(), 'i') : null; + const regex = input.value ? new RegExp(Platform.StringUtilities.escapeForRegExp(input.value), 'i') : null; filterCallback(regex); } input.addEventListener('input', searchHandler, false); diff --git a/front_end/legacy/legacy-defs.d.ts b/front_end/legacy/legacy-defs.d.ts index 356e5e9168..5e08202693 100644 --- a/front_end/legacy/legacy-defs.d.ts +++ b/front_end/legacy/legacy-defs.d.ts @@ -27,10 +27,6 @@ interface Uint32Array { upperBound(value: number, comparator?: {(a: number, b: number): number}, left?: number, right?: number): number; } -interface String { - escapeForRegExp(): string; -} - interface RegExp { __fromRegExpQuery: boolean; } diff --git a/front_end/network/NetworkLogView.js b/front_end/network/NetworkLogView.js index 6110595611..bc8583aeb5 100644 --- a/front_end/network/NetworkLogView.js +++ b/front_end/network/NetworkLogView.js @@ -282,14 +282,7 @@ export class NetworkLogView extends UI.Widget.VBox { * @return {!Filter} */ static _createRequestDomainFilter(value) { - /** - * @param {string} string - * @return {string} - */ - function escapeForRegExp(string) { - return string.escapeForRegExp(); - } - const escapedPattern = value.split('*').map(escapeForRegExp).join('.*'); + const escapedPattern = value.split('*').map(Platform.StringUtilities.escapeForRegExp).join('.*'); return NetworkLogView._requestDomainFilter.bind(null, new RegExp('^' + escapedPattern + '$', 'i')); } @@ -506,7 +499,7 @@ export class NetworkLogView extends UI.Widget.VBox { * @return {boolean} */ static _requestUrlFilter(value, request) { - const regex = new RegExp(value.escapeForRegExp(), 'i'); + const regex = new RegExp(Platform.StringUtilities.escapeForRegExp(value), 'i'); return regex.test(request.url()); } @@ -1713,13 +1706,14 @@ export class NetworkLogView extends UI.Widget.VBox { const regex = descriptor.regex; let filter; if (key) { - const defaultText = (key + ':' + text).escapeForRegExp(); + const defaultText = Platform.StringUtilities.escapeForRegExp(key + ':' + text); filter = this._createSpecialFilter(/** @type {!FilterType} */ (key), text) || NetworkLogView._requestPathFilter.bind(null, new RegExp(defaultText, 'i')); } else if (descriptor.regex) { filter = NetworkLogView._requestPathFilter.bind(null, /** @type {!RegExp} */ (regex)); } else { - filter = NetworkLogView._requestPathFilter.bind(null, new RegExp(text.escapeForRegExp(), 'i')); + filter = NetworkLogView._requestPathFilter.bind( + null, new RegExp(Platform.StringUtilities.escapeForRegExp(text), 'i')); } return descriptor.negative ? NetworkLogView._negativeFilter.bind(null, filter) : filter; }); diff --git a/front_end/platform/string-utilities.ts b/front_end/platform/string-utilities.ts index db1c70881e..4faed41cdf 100644 --- a/front_end/platform/string-utilities.ts +++ b/front_end/platform/string-utilities.ts @@ -357,16 +357,17 @@ export const removeURLFragment = (inputStr: string): string => { return url.toString(); }; +const SPECIAL_REGEX_CHARACTERS = '^[]{}()\\.^$*+?|-,'; + export const regexSpecialCharacters = function(): string { - return '^[]{}()\\.^$*+?|-,'; + return SPECIAL_REGEX_CHARACTERS; }; export const filterRegex = function(query: string): RegExp { - const toEscape = regexSpecialCharacters(); let regexString = ''; for (let i = 0; i < query.length; ++i) { let c = query.charAt(i); - if (toEscape.indexOf(c) !== -1) { + if (SPECIAL_REGEX_CHARACTERS.indexOf(c) !== -1) { c = '\\' + c; } if (i) { @@ -458,3 +459,7 @@ export const trimEndWithMaxLength = (str: string, maxLength: number): string => } return str.substr(0, maxLength - 1) + '…'; }; + +export const escapeForRegExp = (str: string): string => { + return escapeCharacters(str, SPECIAL_REGEX_CHARACTERS); +}; diff --git a/front_end/platform/utilities.js b/front_end/platform/utilities.js index 13fcd10b19..1b99ec8d88 100644 --- a/front_end/platform/utilities.js +++ b/front_end/platform/utilities.js @@ -33,7 +33,7 @@ * that has been migrated, it will need to be imported. */ import {inverse} from './map-utilities.js'; -import {caseInsensetiveComparator, escapeCharacters, regexSpecialCharacters, sprintf} from './string-utilities.js'; +import {caseInsensetiveComparator, regexSpecialCharacters, sprintf} from './string-utilities.js'; // Still used in the test runners that can't use ES modules :( String.sprintf = sprintf; @@ -43,14 +43,6 @@ String.regexSpecialCharacters = regexSpecialCharacters; // @ts-ignore https://crbug.com/1050549 String.caseInsensetiveComparator = caseInsensetiveComparator; -/** - * @this {string} - * @return {string} - */ -String.prototype.escapeForRegExp = function() { - return escapeCharacters(this, regexSpecialCharacters()); -}; - /** * @param {string} a * @param {string} b diff --git a/front_end/recorder/RecordingFileSystem.ts b/front_end/recorder/RecordingFileSystem.ts index eed4c497db..71697ba424 100644 --- a/front_end/recorder/RecordingFileSystem.ts +++ b/front_end/recorder/RecordingFileSystem.ts @@ -7,6 +7,7 @@ import * as Common from '../common/common.js'; import * as i18n from '../i18n/i18n.js'; import * as Persistence from '../persistence/persistence.js'; +import * as Platform from '../platform/platform.js'; import * as TextUtils from '../text_utils/text_utils.js'; // eslint-disable-line no-unused-vars import * as Workspace from '../workspace/workspace.js'; // eslint-disable-line no-unused-vars @@ -109,7 +110,7 @@ export class RecordingFileSystem extends Persistence.PlatformFileSystem.Platform } async searchInPath(query: string, _progress: Common.Progress.Progress): Promise { - const re = new RegExp(query.escapeForRegExp(), 'i'); + const re = new RegExp(Platform.StringUtilities.escapeForRegExp(query), 'i'); const allRecordings: Recording[] = this._recordingsSetting.get(); const matchedRecordings = allRecordings.filter(recording => recording.content.match(re)); return matchedRecordings.map(recording => `recording:///${escapeRecordingName(recording.name)}`); diff --git a/front_end/resources/StorageItemsView.js b/front_end/resources/StorageItemsView.js index 4f20170cdb..7e63423bde 100644 --- a/front_end/resources/StorageItemsView.js +++ b/front_end/resources/StorageItemsView.js @@ -3,6 +3,7 @@ // found in the LICENSE file. import * as Common from '../common/common.js'; +import * as Platform from '../platform/platform.js'; import * as UI from '../ui/ui.js'; export class StorageItemsView extends UI.Widget.VBox { @@ -74,7 +75,7 @@ export class StorageItemsView extends UI.Widget.VBox { */ _filterChanged(event) { const text = /** @type {?string} */ (event.data); - this._filterRegex = text ? new RegExp(text.escapeForRegExp(), 'i') : null; + this._filterRegex = text ? new RegExp(Platform.StringUtilities.escapeForRegExp(text), 'i') : null; this.refreshItems(); } diff --git a/front_end/sdk/DebuggerModel.js b/front_end/sdk/DebuggerModel.js index f1f51922c9..e889e5a6ce 100644 --- a/front_end/sdk/DebuggerModel.js +++ b/front_end/sdk/DebuggerModel.js @@ -30,6 +30,7 @@ import * as Common from '../common/common.js'; import * as Host from '../host/host.js'; +import * as Platform from '../platform/platform.js'; import * as ProtocolClient from '../protocol_client/protocol_client.js'; // eslint-disable-line no-unused-vars import * as Root from '../root/root.js'; @@ -432,7 +433,8 @@ export class DebuggerModel extends SDKModel { let urlRegex; if (this.target().type() === Type.Node) { const platformPath = Common.ParsedURL.ParsedURL.urlToPlatformPath(url, Host.Platform.isWin()); - urlRegex = `${platformPath.escapeForRegExp()}|${url.escapeForRegExp()}`; + urlRegex = + `${Platform.StringUtilities.escapeForRegExp(platformPath)}|${Platform.StringUtilities.escapeForRegExp(url)}`; } // Adjust column if needed. let minColumnNumber = 0; diff --git a/front_end/sdk/NetworkRequest.js b/front_end/sdk/NetworkRequest.js index 9d1b06d721..9df4375b77 100644 --- a/front_end/sdk/NetworkRequest.js +++ b/front_end/sdk/NetworkRequest.js @@ -1234,7 +1234,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper { * @return {!Array.} */ _parseMultipartFormDataParameters(data, boundary) { - const sanitizedBoundary = boundary.escapeForRegExp(); + const sanitizedBoundary = Platform.StringUtilities.escapeForRegExp(boundary); const keyValuePattern = new RegExp( // Header with an optional file name. '^\\r\\ncontent-disposition\\s*:\\s*form-data\\s*;\\s*name="([^"]*)"(?:\\s*;\\s*filename="([^"]*)")?' + diff --git a/front_end/snippets/ScriptSnippetFileSystem.ts b/front_end/snippets/ScriptSnippetFileSystem.ts index 297e80653c..8560ceaf3b 100644 --- a/front_end/snippets/ScriptSnippetFileSystem.ts +++ b/front_end/snippets/ScriptSnippetFileSystem.ts @@ -7,6 +7,7 @@ import * as Common from '../common/common.js'; import * as i18n from '../i18n/i18n.js'; import * as Persistence from '../persistence/persistence.js'; +import * as Platform from '../platform/platform.js'; import * as SDK from '../sdk/sdk.js'; import * as TextUtils from '../text_utils/text_utils.js'; // eslint-disable-line no-unused-vars import * as UI from '../ui/ui.js'; @@ -110,7 +111,7 @@ export class SnippetFileSystem extends Persistence.PlatformFileSystem.PlatformFi } async searchInPath(query: string, _progress: Common.Progress.Progress): Promise { - const re = new RegExp(query.escapeForRegExp(), 'i'); + const re = new RegExp(Platform.StringUtilities.escapeForRegExp(query), 'i'); const allSnippets: Snippet[] = this._snippetsSetting.get(); const matchedSnippets = allSnippets.filter(snippet => snippet.content.match(re)); return matchedSnippets.map(snippet => `snippet:///${escapeSnippetName(snippet.name)}`); diff --git a/front_end/text_editor/CodeMirrorTextEditor.js b/front_end/text_editor/CodeMirrorTextEditor.js index 6a965c6bad..b102c4e5d7 100644 --- a/front_end/text_editor/CodeMirrorTextEditor.js +++ b/front_end/text_editor/CodeMirrorTextEditor.js @@ -1862,7 +1862,7 @@ export class SelectNextOccurrenceController { return undefined; } - let regexSource = textToFind.escapeForRegExp(); + let regexSource = Platform.StringUtilities.escapeForRegExp(textToFind); if (fullWord) { regexSource = '\\b' + regexSource + '\\b'; } diff --git a/front_end/ui/FilterSuggestionBuilder.js b/front_end/ui/FilterSuggestionBuilder.js index ae18668745..271b3da9b6 100644 --- a/front_end/ui/FilterSuggestionBuilder.js +++ b/front_end/ui/FilterSuggestionBuilder.js @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import * as Platform from '../platform/platform.js'; + import {Suggestion, Suggestions} from './SuggestBox.js'; // eslint-disable-line no-unused-vars export class FilterSuggestionBuilder { @@ -37,7 +39,7 @@ export class FilterSuggestionBuilder { /** @type {!Suggestions} */ const suggestions = []; if (valueDelimiterIndex === -1) { - const matcher = new RegExp('^' + prefix.escapeForRegExp(), 'i'); + const matcher = new RegExp('^' + Platform.StringUtilities.escapeForRegExp(prefix), 'i'); for (const key of this._keys) { if (matcher.test(key)) { suggestions.push(/** @type {!Suggestion} */ ({text: modifier + key + ':'})); @@ -46,7 +48,7 @@ export class FilterSuggestionBuilder { } else { const key = prefix.substring(0, valueDelimiterIndex).toLowerCase(); const value = prefix.substring(valueDelimiterIndex + 1); - const matcher = new RegExp('^' + value.escapeForRegExp(), 'i'); + const matcher = new RegExp('^' + Platform.StringUtilities.escapeForRegExp(value), 'i'); const values = Array.from(this._valuesMap.get(key) || new Set()); this._valueSorter(key, values); for (const item of values) { diff --git a/test/unittests/front_end/platform/StringUtilities_test.ts b/test/unittests/front_end/platform/StringUtilities_test.ts index 7f13c2492c..cc604462f5 100644 --- a/test/unittests/front_end/platform/StringUtilities_test.ts +++ b/test/unittests/front_end/platform/StringUtilities_test.ts @@ -622,4 +622,11 @@ describe('StringUtilities', () => { } }); + describe('escapeForRegExp', () => { + it('escapes regex characters', () => { + const inputString = '^[]{}()\\.^$*+?|-'; + const outputString = Platform.StringUtilities.escapeForRegExp(inputString); + assert.strictEqual(outputString, '\\^\\[\\]\\{\\}\\(\\)\\\\\\.\\^\\$\\*\\+\\?\\|\\-'); + }); + }); }); diff --git a/test/unittests/front_end/platform/Utilities_test.ts b/test/unittests/front_end/platform/Utilities_test.ts index 9b10c8aa3a..a7ade59317 100644 --- a/test/unittests/front_end/platform/Utilities_test.ts +++ b/test/unittests/front_end/platform/Utilities_test.ts @@ -108,10 +108,4 @@ describe('Utilities', () => { } } }); - - it('escapes regex characters', () => { - const inputString = '^[]{}()\\.^$*+?|-'; - const outputString = inputString.escapeForRegExp(); - assert.strictEqual(outputString, '\\^\\[\\]\\{\\}\\(\\)\\\\\\.\\^\\$\\*\\+\\?\\|\\-'); - }); });