/******************************************************************************* uBlock Origin - a browser extension to block requests. Copyright (C) 2020-present Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. Home: https://github.com/gorhill/uBlock */ 'use strict'; /******************************************************************************/ (( ) => { // >>>>> start of local scope /******************************************************************************/ // Worker context if ( self.WorkerGlobalScope instanceof Object && self instanceof self.WorkerGlobalScope ) { let content = ''; const doSearch = function(details) { const reEOLs = /\n\r|\r\n|\n|\r/g; const t1 = Date.now() + 750; let reSearch; try { reSearch = new RegExp(details.pattern, details.flags); } catch(ex) { return; } const response = []; const maxOffset = content.length; let iLine = 0; let iOffset = 0; let size = 0; while ( iOffset < maxOffset ) { // Find next match const match = reSearch.exec(content); if ( match === null ) { break; } // Find number of line breaks between last and current match. reEOLs.lastIndex = 0; const eols = content.slice(iOffset, match.index).match(reEOLs); if ( Array.isArray(eols) ) { iLine += eols.length; } // Store line response.push(iLine); size += 1; // Find next line break. reEOLs.lastIndex = reSearch.lastIndex; const eol = reEOLs.exec(content); iOffset = eol !== null ? reEOLs.lastIndex : content.length; reSearch.lastIndex = iOffset; iLine += 1; // Quit if this takes too long if ( (size & 0x3FF) === 0 && Date.now() >= t1 ) { break; } } return response; }; self.onmessage = function(e) { const msg = e.data; switch ( msg.what ) { case 'setHaystack': content = msg.content; break; case 'doSearch': const response = doSearch(msg); self.postMessage({ id: msg.id, response }); break; } }; return; } /******************************************************************************/ // Main context { const workerTTL = 5 * 60 * 1000; const pendingResponses = new Map(); let worker; let workerTTLTimer; let messageId = 1; const onWorkerMessage = function(e) { const msg = e.data; const resolver = pendingResponses.get(msg.id); if ( resolver === undefined ) { return; } pendingResponses.delete(msg.id); resolver(msg.response); }; const cancelPendingTasks = function() { for ( const resolver of pendingResponses.values() ) { resolver(); } pendingResponses.clear(); }; const destroy = function() { shutdown(); self.searchThread = undefined; }; const shutdown = function() { if ( workerTTLTimer !== undefined ) { clearTimeout(workerTTLTimer); workerTTLTimer = undefined; } if ( worker === undefined ) { return; } worker.terminate(); worker.onmessage = undefined; worker = undefined; cancelPendingTasks(); }; const init = function() { if ( self.searchThread instanceof Object === false ) { return; } if ( worker === undefined ) { worker = new Worker('js/codemirror/search-thread.js'); worker.onmessage = onWorkerMessage; } if ( workerTTLTimer !== undefined ) { clearTimeout(workerTTLTimer); } workerTTLTimer = vAPI.setTimeout(shutdown, workerTTL); self.addEventListener('beforeunload', ( ) => { destroy(); }, { once: true }); }; const needHaystack = function() { return worker instanceof Object === false; }; const setHaystack = function(content) { init(); worker.postMessage({ what: 'setHaystack', content }); }; const search = function(query, overwrite = true) { init(); if ( worker instanceof Object === false ) { return Promise.resolve(); } if ( overwrite ) { cancelPendingTasks(); } const id = messageId++; worker.postMessage({ what: 'doSearch', id, pattern: query.source, flags: query.flags, isRE: query instanceof RegExp }); return new Promise(resolve => { pendingResponses.set(id, resolve); }); }; self.searchThread = { needHaystack, setHaystack, search, shutdown }; } /******************************************************************************/ // <<<<< end of local scope })(); /******************************************************************************/ void 0;