Skip to content

Instantly share code, notes, and snippets.

@gorhill
Created February 16, 2019 18:36
Show Gist options
  • Save gorhill/67d7b4e75e268fb36658823968427fb5 to your computer and use it in GitHub Desktop.
Save gorhill/67d7b4e75e268fb36658823968427fb5 to your computer and use it in GitHub Desktop.
This is a patch I applied to ABP dev build 3.4.3.2253 in order to be able to benchmark from within the extension

Install ABP dev build from Chrome store: https://chrome.google.com/webstore/detail/adblock-plus-development/ldcecbkkoecffmfljeihcmifjjdoepkn

Copy ABP dev build extension from the Chromium profile into working directory for modification.

Apply the patch below (manually probably).

Remove _metadata folder (because probably Chromium will complain of corrupted extension).

Side load the resulting extension.

Be sure that only EasyList is enabled -- disable "Acceptable ads", we just want to test EasyList.

Open the extension dev console, enter benchmark();, wait for result. Repeat the benchmark a few times to confirm results are stable.

Notice I use new URL() in order to be able to call the matching function. This is what ABP does itself as seen above in its onBeforeRequest handler.

--- ./original/manifest.json
+++ ./modified/manifest.json
@@ -37,10 +37,8 @@
"48": "icons/detailed/abp-48.png",
"64": "icons/detailed/abp-64.png"
},
- "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7lnjJxU5gDGir3Brx0nCly36/bvi3xiCZBnU5P/d6dDfVxLdk2jn8SI03EggHvKavUmCsjhd1rfVqNnpLqxykwPQSIlsnx7rl1MNyas1TPxpD3l8BiO/Eq+iUVWmZQLjWM0yyO4Nj1W0LMP2hg4hzNX8m/vOp5NOrQwfJ8A4ouQIDAQAB",
"manifest_version": 2,
"minimum_chrome_version": "49.0",
- "minimum_opera_version": "36.0",
"name": "__MSG_name_devbuild__",
"options_ui": {
"open_in_tab": true,
@@ -51,6 +49,5 @@
"storage": {
"managed_schema": "managed-storage-schema.json"
},
- "update_url": "https://clients2.google.com/service/update2/crx",
"version": "3.4.3.2253"
}
--- ./original/lib/adblockplus.js
+++ ./modified/lib/adblockplus.js
@@ -8153,6 +8153,187 @@
return result;
}, {urls: ["<all_urls>"]}, ["blocking"]);
+
+
+self.benchmark = function() {
+ // This maps puppeteer types to Adblock Plus types
+ const TYPE_MAP = {
+ // Consider document requests as sub_document. This is because the request
+ // dataset does not contain sub_frame or main_frame but only 'document' and
+ // different blockers have different behaviours.
+ document: RegExpFilter.typeMap.SUBDOCUMENT,
+ stylesheet: RegExpFilter.typeMap.STYLESHEET,
+ image: RegExpFilter.typeMap.IMAGE,
+ media: RegExpFilter.typeMap.MEDIA,
+ font: RegExpFilter.typeMap.FONT,
+ script: RegExpFilter.typeMap.SCRIPT,
+ xhr: RegExpFilter.typeMap.XMLHTTPREQUEST,
+ websocket: RegExpFilter.typeMap.WEBSOCKET,
+
+ // other
+ fetch: RegExpFilter.typeMap.OTHER,
+ other: RegExpFilter.typeMap.OTHER,
+ eventsource: RegExpFilter.typeMap.OTHER,
+ manifest: RegExpFilter.typeMap.OTHER,
+ texttrack: RegExpFilter.typeMap.OTHER,
+ };
+
+ const fetchText = function(url, onLoad, onError) {
+ if ( typeof onError !== 'function' ) {
+ onError = onLoad;
+ }
+
+ const timeoutAfter = 30000;
+ const xhr = new XMLHttpRequest();
+ let contentLoaded = 0;
+ let timeoutTimer;
+
+ const cleanup = function() {
+ xhr.removeEventListener('load', onLoadEvent);
+ xhr.removeEventListener('error', onErrorEvent);
+ xhr.removeEventListener('abort', onErrorEvent);
+ xhr.removeEventListener('progress', onProgressEvent);
+ if ( timeoutTimer !== undefined ) {
+ clearTimeout(timeoutTimer);
+ timeoutTimer = undefined;
+ }
+ };
+
+ // https://github.com/gorhill/uMatrix/issues/15
+ const onLoadEvent = function() {
+ cleanup();
+ // xhr for local files gives status 0, but actually succeeds
+ const details = {
+ url,
+ content: '',
+ statusCode: this.status || 200,
+ statusText: this.statusText || ''
+ };
+ if ( details.statusCode < 200 || details.statusCode >= 300 ) {
+ return onError.call(null, details);
+ }
+ // we never download anything else than plain text: discard if response
+ // appears to be a HTML document: could happen when server serves
+ // some kind of error page I suppose
+ const text = this.responseText.trim();
+ if ( text.startsWith('<') && text.endsWith('>') ) {
+ return onError.call(null, details);
+ }
+ details.content = this.responseText;
+ onLoad(details);
+ };
+
+ const onErrorEvent = function() {
+ cleanup();
+ onError({ url, content: '' });
+ };
+
+ const onTimeout = function() {
+ xhr.abort();
+ };
+
+ // https://github.com/gorhill/uBlock/issues/2526
+ // - Timeout only when there is no progress.
+ const onProgressEvent = function(ev) {
+ if ( ev.loaded === contentLoaded ) { return; }
+ contentLoaded = ev.loaded;
+ if ( timeoutTimer !== undefined ) {
+ clearTimeout(timeoutTimer);
+ }
+ timeoutTimer = setTimeout(onTimeout, timeoutAfter);
+ };
+
+ // Be ready for thrown exceptions:
+ // I am pretty sure it used to work, but now using a URL such as
+ // `file:///` on Chromium 40 results in an exception being thrown.
+ try {
+ xhr.open('get', url, true);
+ xhr.addEventListener('load', onLoadEvent);
+ xhr.addEventListener('error', onErrorEvent);
+ xhr.addEventListener('abort', onErrorEvent);
+ xhr.addEventListener('progress', onProgressEvent);
+ xhr.responseType = 'text';
+ xhr.send();
+ timeoutTimer = setTimeout(onTimeout, timeoutAfter);
+ } catch (e) {
+ onErrorEvent.call(xhr);
+ }
+ };
+ const LineIterator = function(text, offset) {
+ this.text = text;
+ this.textLen = this.text.length;
+ this.offset = offset || 0;
+ };
+
+ LineIterator.prototype.next = function(offset) {
+ if ( offset !== undefined ) {
+ this.offset += offset;
+ }
+ var lineEnd = this.text.indexOf('\n', this.offset);
+ if ( lineEnd === -1 ) {
+ lineEnd = this.text.indexOf('\r', this.offset);
+ if ( lineEnd === -1 ) {
+ lineEnd = this.textLen;
+ }
+ }
+ var line = this.text.slice(this.offset, lineEnd);
+ this.offset = lineEnd + 1;
+ return line;
+ };
+
+ LineIterator.prototype.charCodeAt = function(offset) {
+ return this.text.charCodeAt(this.offset + offset);
+ };
+
+ LineIterator.prototype.eot = function() {
+ return this.offset >= this.textLen;
+ };
+
+ new Promise(resolve => {
+ const url = chrome.runtime.getURL('/requests.json');
+ fetchText(url, details => {
+ if ( details.error !== undefined ) {
+ console.info(`Not found: ${url}`);
+ resolve();
+ return;
+ }
+ const requests = [];
+ const lineIter = new LineIterator(details.content);
+ while ( lineIter.eot() === false ) {
+ let request;
+ try {
+ request = JSON.parse(lineIter.next());
+ } catch(ex) {
+ }
+ if ( request instanceof Object === false ) { continue; }
+ if ( !request.frameUrl || !request.url ) { continue; }
+ requests.push(request);
+ }
+ resolve(requests);
+ });
+ }).then(requests => {
+ if ( Array.isArray(requests) === false || requests.length === 0 ) {
+ console.info('No requests found to benchmark');
+ return;
+ }
+ const t0 = self.performance.now();
+ for ( const request of requests ) {
+ const url = new URL(request.url);
+ const sourceURL = new URL(request.frameUrl);
+ matchRequest(
+ url,
+ resourceTypes.get(request.cpt) || "OTHER",
+ sourceURL.hostname,
+ null, false
+ );
+ }
+ const t1 = self.performance.now();
+ const dur = t1 - t0;
+ console.info(`Evaluated ${requests.length} requests in ${dur.toFixed(0)} ms`);
+ console.info(`\tAverage: ${(dur / requests.length).toFixed(3)} ms per request`);
+ });
+}
+
port.on("filters.collapse", (message, sender) =>
{
@@ -13571,4 +13752,4 @@
/***/ })
/******/ ]);
-//# sourceMappingURL=adblockplus.js.map+//# sourceMappingURL=adblockplus.js.map
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment