0

[DNR] Add isRegexSupported API call

Add the isRegexSupported extension function which provides extension
developers with a way to check that a given regular expression can be
used as a regexFilter rule condition.

Skipping presubmit since this seems to be hitting crbug.com/956368.

R=karandeepb@chromium.org, kelvinjiang@chromium.org

Bug: 1088457
No-Presubmit: True
Change-Id: Ic51df5dd096806472f25e4053a020c0a08368179
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2372465
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Reviewed-by: Karan Bhatia <karandeepb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808574}
This commit is contained in:
Dave Vandyke
2020-09-18 23:08:21 +00:00
committed by Commit Bot
parent 55e1bdbf29
commit f278c33cde
9 changed files with 167 additions and 1 deletions
AUTHORS
chrome
browser
extensions
api
declarative_net_request
test
data
extensions
api_test
declarative_net_request
is_regex_supported
extensions
tools/metrics/histograms

@ -232,7 +232,7 @@ Daniel Waxweiler <daniel.waxweiler@gmail.com>
Dániel Bátyai <dbatyai@inf.u-szeged.hu>
Dániel Vince <vinced@inf.u-szeged.hu>
Darshini KN <kn.darshini@samsung.com>
Dave Barker <kzar@kzar.co.uk>
Dave Vandyke <kzar@kzar.co.uk>
David Benjamin <davidben@mit.edu>
David Davidovic <david@davidovic.io>
David Erceg <erceg.david@gmail.com>

@ -98,4 +98,8 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, GetMatchedRules) {
ASSERT_TRUE(RunTest("get_matched_rules")) << message_;
}
IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, IsRegexSupported) {
ASSERT_TRUE(RunTest("is_regex_supported")) << message_;
}
} // namespace

@ -0,0 +1,54 @@
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function isRegexSupported(regexOptions) {
return new Promise(resolve => {
chrome.declarativeNetRequest.isRegexSupported(regexOptions, resolve);
});
}
chrome.test.runTests([
async function testSupportedRegex() {
let result = await isRegexSupported({regex: '[0-9]+'});
chrome.test.assertEq(result, {isSupported: true});
chrome.test.succeed();
},
async function testSupportedRegexWithOptions() {
let result = await isRegexSupported(
{regex: '[0-9]+', isCaseSensitive: false, requireCapturing: true});
chrome.test.assertEq(result, {isSupported: true});
chrome.test.succeed();
},
async function testInvalidRegex() {
let result = await isRegexSupported({regex: '[a-9]+'});
chrome.test.assertEq(result, {isSupported: false, reason: 'syntaxError'});
chrome.test.succeed();
},
async function testInvalidRegexWithOptions() {
let result = await isRegexSupported(
{regex: '[a-9]+', isCaseSensitive: false, requireCapturing: true});
chrome.test.assertEq(result, {isSupported: false, reason: 'syntaxError'});
chrome.test.succeed();
},
async function testMemoryError() {
let result = await isRegexSupported({regex: '[0-9]+'.repeat(1000)});
chrome.test.assertEq(
result, {isSupported: false, reason: 'memoryLimitExceeded'});
chrome.test.succeed();
},
async function testMemoryErrorWithOptions() {
let regex = '(a)'.repeat(50);
let result = await isRegexSupported({regex});
chrome.test.assertEq(result, {isSupported: true});
result = await isRegexSupported({regex, requireCapturing: true});
chrome.test.assertEq(
result, {isSupported: false, reason: 'memoryLimitExceeded'});
chrome.test.succeed();
}
]);

@ -0,0 +1,14 @@
{
"name": "Test extension",
"manifest_version": 2,
"permissions": [
"declarativeNetRequest"
],
"version": "1.0",
"background": {
"scripts": [
"background.js"
],
"persistent": false
}
}

@ -354,4 +354,42 @@ DeclarativeNetRequestSetActionCountAsBadgeTextFunction::Run() {
return RespondNow(NoArguments());
}
DeclarativeNetRequestIsRegexSupportedFunction::
DeclarativeNetRequestIsRegexSupportedFunction() = default;
DeclarativeNetRequestIsRegexSupportedFunction::
~DeclarativeNetRequestIsRegexSupportedFunction() = default;
ExtensionFunction::ResponseAction
DeclarativeNetRequestIsRegexSupportedFunction::Run() {
using Params = dnr_api::IsRegexSupported::Params;
base::string16 error;
std::unique_ptr<Params> params(Params::Create(*args_, &error));
EXTENSION_FUNCTION_VALIDATE(params);
EXTENSION_FUNCTION_VALIDATE(error.empty());
bool is_case_sensitive = params->regex_options.is_case_sensitive
? *params->regex_options.is_case_sensitive
: true;
bool require_capturing = params->regex_options.require_capturing
? *params->regex_options.require_capturing
: false;
re2::RE2 regex(params->regex_options.regex,
declarative_net_request::CreateRE2Options(is_case_sensitive,
require_capturing));
dnr_api::IsRegexSupportedResult result;
if (regex.ok()) {
result.is_supported = true;
} else {
result.is_supported = false;
result.reason = regex.error_code() == re2::RE2::ErrorPatternTooLarge
? dnr_api::UNSUPPORTED_REGEX_REASON_MEMORYLIMITEXCEEDED
: dnr_api::UNSUPPORTED_REGEX_REASON_SYNTAXERROR;
}
return RespondNow(
ArgumentList(dnr_api::IsRegexSupported::Results::Create(result)));
}
} // namespace extensions

@ -118,6 +118,19 @@ class DeclarativeNetRequestSetActionCountAsBadgeTextFunction
ExtensionFunction::ResponseAction Run() override;
};
class DeclarativeNetRequestIsRegexSupportedFunction : public ExtensionFunction {
public:
DeclarativeNetRequestIsRegexSupportedFunction();
DECLARE_EXTENSION_FUNCTION("declarativeNetRequest.isRegexSupported",
DECLARATIVENETREQUEST_ISREGEXSUPPORTED)
protected:
~DeclarativeNetRequestIsRegexSupportedFunction() override;
// ExtensionFunction override:
ExtensionFunction::ResponseAction Run() override;
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_API_DECLARATIVE_NET_REQUEST_DECLARATIVE_NET_REQUEST_API_H_

@ -1568,6 +1568,7 @@ enum HistogramValue {
INPUTMETHODPRIVATE_SETCOMPOSINGRANGE = 1505,
AUTOTESTPRIVATE_LAUNCHSYSTEMWEBAPP = 1506,
ACCESSIBILITY_PRIVATE_PERFORMACCELERATORACTION = 1507,
DECLARATIVENETREQUEST_ISREGEXSUPPORTED = 1508,
// Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY

@ -66,6 +66,16 @@ namespace declarativeNetRequest {
allowAllRequests
};
// Describes the reason why a given regular expression isn't supported.
enum UnsupportedRegexReason {
// The regular expression is syntactically incorrect, or uses features
// not available in the
// <a href = "https://github.com/google/re2/wiki/Syntax">RE2 syntax</a>.
syntaxError,
// The regular expression exceeds the memory limit.
memoryLimitExceeded
};
// Describes a single static ruleset.
dictionary Ruleset {
// A non-empty string uniquely identifying the ruleset. IDs beginning with
@ -379,11 +389,34 @@ namespace declarativeNetRequest {
DNRInfo declarative_net_request;
};
dictionary RegexOptions {
// The regular expresson to check.
DOMString regex;
// Whether the <code>regex</code> specified is case sensitive. Default is
// true.
boolean? isCaseSensitive;
// Whether the <code>regex</code> specified requires capturing. Capturing is
// only required for redirect rules which specify a
// <code>regexSubstition</code> action. The default is false.
boolean? requireCapturing;
};
dictionary IsRegexSupportedResult {
boolean isSupported;
// Specifies the reason why the regular expression is not supported. Only
// provided if <code>isSupported</code> is false.
UnsupportedRegexReason? reason;
};
callback EmptyCallback = void();
callback GetAllowedPagesCallback = void(DOMString[] result);
callback GetRulesCallback = void(Rule[] rules);
callback GetMatchedRulesCallback = void(RulesMatchedDetails details);
callback GetEnabledRulesetsCallback = void(DOMString[] rulesetIds);
callback IsRegexSupportedCallback = void(IsRegexSupportedResult result);
interface Functions {
@ -460,6 +493,14 @@ namespace declarativeNetRequest {
// action count for a tab. This preference is persisted across sessions and
// is false by default.
static void setActionCountAsBadgeText(boolean enable);
// Checks if the given regular expression will be supported as a
// <code>regexFilter</code> rule condition.
// |regexOptions|: The regular expression to check.
// |callback|: Called with details consisting of whether the regular
// expression is supported and the reason if not.
static void isRegexSupported(RegexOptions regexOptions,
IsRegexSupportedCallback callback);
};
interface Properties {

@ -24569,6 +24569,7 @@ Called by update_extension_histograms.py.-->
<int value="1505" label="INPUTMETHODPRIVATE_SETCOMPOSINGRANGE"/>
<int value="1506" label="AUTOTESTPRIVATE_LAUNCHSYSTEMWEBAPP"/>
<int value="1507" label="ACCESSIBILITY_PRIVATE_PERFORMACCELERATORACTION"/>
<int value="1508" label="DECLARATIVENETREQUEST_ISREGEXSUPPORTED"/>
</enum>
<enum name="ExtensionIconState">