"use strict"; // This file expects tabTracker to be defined in the global scope (e.g. // by ext-utils.js). /* global tabTracker */ ChromeUtils.defineModuleGetter(this, "WebRequest", "resource://gre/modules/WebRequest.jsm"); // The guts of a WebRequest event handler. Takes care of converting // |details| parameter when invoking listeners. function registerEvent(extension, eventName, fire, filter, info, tabParent = null) { let listener = async data => { let browserData = {tabId: -1, windowId: -1}; if (data.browser) { browserData = tabTracker.getBrowserData(data.browser); } if (filter.tabId != null && browserData.tabId != filter.tabId) { return; } if (filter.windowId != null && browserData.windowId != filter.windowId) { return; } let event = data.serialize(eventName); event.tabId = browserData.tabId; if (data.registerTraceableChannel) { if (fire.wakeup) { await fire.wakeup(); } data.registerTraceableChannel(extension.policy, tabParent); } return fire.sync(event); }; let filter2 = {}; if (filter.urls) { let perms = new MatchPatternSet([...extension.whiteListedHosts.patterns, ...extension.optionalOrigins.patterns]); filter2.urls = new MatchPatternSet(filter.urls); if (!perms.overlapsAll(filter2.urls)) { Cu.reportError("The webRequest.addListener filter doesn't overlap with host permissions."); } } if (filter.types) { filter2.types = filter.types; } if (filter.tabId) { filter2.tabId = filter.tabId; } if (filter.windowId) { filter2.windowId = filter.windowId; } let blockingAllowed = extension.hasPermission("webRequestBlocking"); let info2 = []; if (info) { for (let desc of info) { if (desc == "blocking" && !blockingAllowed) { Cu.reportError("Using webRequest.addListener with the blocking option " + "requires the 'webRequestBlocking' permission."); } else { info2.push(desc); } } } let listenerDetails = { addonId: extension.id, extension: extension.policy, blockingAllowed, }; WebRequest[eventName].addListener( listener, filter2, info2, listenerDetails); return { unregister: () => { WebRequest[eventName].removeListener(listener); }, convert(_fire, context) { fire = _fire; tabParent = context.xulBrowser.frameLoader.tabParent; }, }; } function makeWebRequestEvent(context, name) { return new EventManager({ context, name: `webRequest.${name}`, persistent: { module: "webRequest", event: name, }, register: (fire, filter, info) => { return registerEvent(context.extension, name, fire, filter, info, context.xulBrowser.frameLoader.tabParent).unregister; }, }).api(); } this.webRequest = class extends ExtensionAPI { primeListener(extension, event, fire, params) { return registerEvent(extension, event, fire, ...params); } getAPI(context) { return { webRequest: { onBeforeRequest: makeWebRequestEvent(context, "onBeforeRequest"), onBeforeSendHeaders: makeWebRequestEvent(context, "onBeforeSendHeaders"), onSendHeaders: makeWebRequestEvent(context, "onSendHeaders"), onHeadersReceived: makeWebRequestEvent(context, "onHeadersReceived"), onAuthRequired: makeWebRequestEvent(context, "onAuthRequired"), onBeforeRedirect: makeWebRequestEvent(context, "onBeforeRedirect"), onResponseStarted: makeWebRequestEvent(context, "onResponseStarted"), onErrorOccurred: makeWebRequestEvent(context, "onErrorOccurred"), onCompleted: makeWebRequestEvent(context, "onCompleted"), getSecurityInfo: function(requestId, options = {}) { return WebRequest.getSecurityInfo({ id: requestId, extension: context.extension.policy, tabParent: context.xulBrowser.frameLoader.tabParent, options, }); }, handlerBehaviorChanged: function() { // TODO: Flush all caches. }, }, }; } };