diff --git a/packages/adblocker-webextension/adblocker.ts b/packages/adblocker-webextension/adblocker.ts index 067e521be8..7f0095e3c1 100644 --- a/packages/adblocker-webextension/adblocker.ts +++ b/packages/adblocker-webextension/adblocker.ts @@ -227,48 +227,20 @@ export class WebExtensionBlocker extends FiltersEngine { } } - /** - * Deal with request cancellation (`{ cancel: true }`) and redirection (`{ redirectUrl: '...' }`). - */ - private onBeforeRequest = ( - details: WebRequestBeforeRequestDetails, - ): chrome.webRequest.BlockingResponse => { - const request = fromWebRequestDetails(details); - if (request.isMainFrame()) { - this.performHTMLFiltering(request); - return {}; + public handleRuntimeMessage = async ( + msg: IBackgroundCallback & { action?: string }, + sender: chrome.runtime.MessageSender, + ): Promise => { + if (sender.tab === undefined) { + throw new Error('required "sender.tab" information is not available'); } - const { redirect, match } = this.match(request); - - if (redirect !== undefined) { - return { redirectUrl: redirect.dataUrl }; - } else if (match === true) { - return { cancel: true }; + if (sender.tab.id === undefined) { + throw new Error('required "sender.tab.id" information is not available'); } - return {}; - }; - - /** - * - */ - private onHeadersReceived = ( - details: WebRequestHeadersReceivedDetails, - ): chrome.webRequest.BlockingResponse => { - return updateResponseHeadersWithCSP( - details, - this.getCSPDirectives(fromWebRequestDetails(details)), - ); - }; - - private onRuntimeMessage = ( - msg: IBackgroundCallback & { action?: string }, - sender: chrome.runtime.MessageSender, - sendResponse: (response?: any) => void, - ): void => { - if (sender.tab === undefined || sender.tab.id === undefined || sender.frameId === undefined) { - return; + if (sender.frameId === undefined) { + throw new Error('required "sender.frameId" information is not available'); } // Make sure we only listen to messages coming from our content-script @@ -306,7 +278,7 @@ export class WebExtensionBlocker extends FiltersEngine { return; } - this.injectStylesWebExtension(styles, { tabId: sender.tab.id, allFrames: true }); + await this.injectStylesWebExtension(styles, { tabId: sender.tab.id, allFrames: true }); } // Separately, requests cosmetics which depend on the page it self @@ -337,7 +309,7 @@ export class WebExtensionBlocker extends FiltersEngine { return; } - this.injectStylesWebExtension(styles, { tabId: sender.tab.id, frameId }); + await this.injectStylesWebExtension(styles, { tabId: sender.tab.id, frameId }); // Inject scripts from content script const responseFromBackground: IMessageFromBackground = { @@ -346,12 +318,56 @@ export class WebExtensionBlocker extends FiltersEngine { scripts, styles: '', }; - sendResponse(responseFromBackground); + return responseFromBackground; } } }; - private injectStylesWebExtension( + /** + * Deal with request cancellation (`{ cancel: true }`) and redirection (`{ redirectUrl: '...' }`). + */ + private onBeforeRequest = ( + details: WebRequestBeforeRequestDetails, + ): chrome.webRequest.BlockingResponse => { + const request = fromWebRequestDetails(details); + if (request.isMainFrame()) { + this.performHTMLFiltering(request); + return {}; + } + + const { redirect, match } = this.match(request); + + if (redirect !== undefined) { + return { redirectUrl: redirect.dataUrl }; + } else if (match === true) { + return { cancel: true }; + } + + return {}; + }; + + private onHeadersReceived = ( + details: WebRequestHeadersReceivedDetails, + ): chrome.webRequest.BlockingResponse => { + return updateResponseHeadersWithCSP( + details, + this.getCSPDirectives(fromWebRequestDetails(details)), + ); + }; + + private onRuntimeMessage = ( + msg: IBackgroundCallback & { action?: string }, + sender: chrome.runtime.MessageSender, + sendResponse: (response?: any) => void, + ): void => { + this.handleRuntimeMessage(msg, sender) + .then(sendResponse) + .catch((ex) => { + console.error('Error while handling runtime message:', ex); + }); + }; + + private async injectStylesWebExtension( styles: string, { tabId, @@ -362,13 +378,24 @@ export class WebExtensionBlocker extends FiltersEngine { frameId?: number; allFrames?: boolean; }, - ): void { - if ( - styles.length > 0 && - typeof chrome !== 'undefined' && - chrome.tabs && - chrome.tabs.insertCSS - ) { + ): Promise { + // Abort if stylesheet is empty. + if (styles.length === 0) { + return; + } + + // Abort if `chrome` global is not accessible. + if (typeof chrome === 'undefined') { + throw new Error('required "chrome" global object is not accessible'); + } + + // Abort if `chrome.tabs.insertCSS` is not available. + if (chrome.tabs === undefined || chrome.tabs.insertCSS === undefined) { + throw new Error('required "chrome.tabs.insertCSS" is not available'); + } + + // Proceed with stylesheet injection. + return new Promise((resolve, reject) => { chrome.tabs.insertCSS( tabId, { @@ -381,11 +408,13 @@ export class WebExtensionBlocker extends FiltersEngine { }, () => { if (chrome.runtime.lastError) { - console.error('Error while injecting CSS', chrome.runtime.lastError.message); + reject(chrome.runtime.lastError.message); + } else { + resolve(); } }, ); - } + }); } }