/******************************************************************************* uBlock Origin - a browser extension to block requests. Copyright (C) 2014-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 */ /* globals browser */ 'use strict'; /******************************************************************************/ import logger from './logger.js'; import { FilteringContext } from './filtering-context.js'; import { domainFromHostname, hostnameFromURI, originFromURI, } from './uri-utils.js'; /******************************************************************************/ // Not all platforms may have properly declared vAPI.webextFlavor. if ( vAPI.webextFlavor === undefined ) { vAPI.webextFlavor = { major: 0, soup: new Set([ 'ublock' ]) }; } /******************************************************************************/ const hiddenSettingsDefault = { allowGenericProceduralFilters: false, assetFetchTimeout: 30, autoCommentFilterTemplate: '{{date}} {{origin}}', autoUpdateAssetFetchPeriod: 60, autoUpdateDelayAfterLaunch: 105, autoUpdatePeriod: 2, benchmarkDatasetURL: 'unset', blockingProfiles: '11111/#F00 11010/#C0F 11001/#00F 00001', cacheStorageAPI: 'unset', cacheStorageCompression: true, cacheControlForFirefox1376932: 'no-cache, no-store, must-revalidate', cloudStorageCompression: true, cnameIgnoreList: 'unset', cnameIgnore1stParty: true, cnameIgnoreExceptions: true, cnameIgnoreRootDocument: true, cnameMaxTTL: 120, cnameReplayFullURL: false, cnameUncloakProxied: false, consoleLogLevel: 'unset', debugAssetsJson: false, debugScriptlets: false, debugScriptletInjector: false, disableWebAssembly: false, extensionUpdateForceReload: false, filterAuthorMode: false, loggerPopupType: 'popup', manualUpdateAssetFetchPeriod: 500, modifyWebextFlavor: 'unset', popupFontSize: 'unset', popupPanelDisabledSections: 0, popupPanelLockedSections: 0, popupPanelHeightMode: 0, requestJournalProcessPeriod: 1000, selfieAfter: 2, strictBlockingBypassDuration: 120, toolbarWarningTimeout: 60, trustedListPrefixes: 'ublock-', uiPopupConfig: 'unset', uiStyles: 'unset', updateAssetBypassBrowserCache: false, userResourcesLocation: 'unset', }; if ( vAPI.webextFlavor.soup.has('devbuild') ) { hiddenSettingsDefault.trustedListPrefixes += ' user-'; } const userSettingsDefault = { advancedUserEnabled: false, alwaysDetachLogger: true, autoUpdate: true, cloudStorageEnabled: false, cnameUncloakEnabled: true, collapseBlocked: true, colorBlindFriendly: false, contextMenuEnabled: true, uiAccentCustom: false, uiAccentCustom0: '#aca0f7', uiTheme: 'auto', externalLists: '', firewallPaneMinimized: true, hyperlinkAuditingDisabled: true, ignoreGenericCosmeticFilters: vAPI.webextFlavor.soup.has('mobile'), importedLists: [], largeMediaSize: 50, parseAllABPHideFilters: true, popupPanelSections: 0b111, prefetchingDisabled: true, requestLogMaxEntries: 1000, showIconBadge: true, suspendUntilListsAreLoaded: vAPI.Net.canSuspend(), tooltipsDisabled: false, webrtcIPAddressHidden: false, }; const dynamicFilteringDefault = [ 'behind-the-scene * * noop', 'behind-the-scene * image noop', 'behind-the-scene * 3p noop', 'behind-the-scene * inline-script noop', 'behind-the-scene * 1p-script noop', 'behind-the-scene * 3p-script noop', 'behind-the-scene * 3p-frame noop', ]; const hostnameSwitchesDefault = [ 'no-large-media: behind-the-scene false', ]; // https://github.com/LiCybora/NanoDefenderFirefox/issues/196 if ( vAPI.webextFlavor.soup.has('firefox') ) { hostnameSwitchesDefault.push('no-csp-reports: * true'); } const µBlock = { // jshint ignore:line userSettingsDefault: userSettingsDefault, userSettings: Object.assign({}, userSettingsDefault), hiddenSettingsDefault: hiddenSettingsDefault, hiddenSettingsAdmin: {}, hiddenSettings: Object.assign({}, hiddenSettingsDefault), dynamicFilteringDefault, hostnameSwitchesDefault, noDashboard: false, // Features detection. privacySettingsSupported: vAPI.browserSettings instanceof Object, cloudStorageSupported: vAPI.cloud instanceof Object, canFilterResponseData: typeof browser.webRequest.filterResponseData === 'function', // https://github.com/chrisaljoudi/uBlock/issues/180 // Whitelist directives need to be loaded once the PSL is available netWhitelist: new Map(), netWhitelistModifyTime: 0, netWhitelistDefault: [ 'about-scheme', 'chrome-extension-scheme', 'chrome-scheme', 'edge-scheme', 'moz-extension-scheme', 'opera-scheme', 'vivaldi-scheme', 'wyciwyg-scheme', // Firefox's "What-You-Cache-Is-What-You-Get" ], localSettings: { blockedRequestCount: 0, allowedRequestCount: 0, }, localSettingsLastModified: 0, // Read-only systemSettings: { compiledMagic: 56, // Increase when compiled format changes selfieMagic: 56, // Increase when selfie format changes }, // https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501 // The assumption is that cache storage state reflects whether // compiled or selfie assets are available or not. The properties // below is to no longer rely on this assumption -- though it's still // not clear how the assumption could be wrong, and it's still not // clear whether relying on those properties will really solve the // issue. It's just an attempt at hardening. compiledFormatChanged: false, selfieIsInvalid: false, restoreBackupSettings: { lastRestoreFile: '', lastRestoreTime: 0, lastBackupFile: '', lastBackupTime: 0, }, commandShortcuts: new Map(), // Allows to fully customize uBO's assets, typically set through admin // settings. The content of 'assets.json' will also tell which filter // lists to enable by default when uBO is first installed. assetsBootstrapLocation: undefined, assetsJsonPath: vAPI.webextFlavor.soup.has('devbuild') ? '/assets/assets.dev.json' : '/assets/assets.json', userFiltersPath: 'user-filters', pslAssetKey: 'public_suffix_list.dat', selectedFilterLists: [], availableFilterLists: {}, badLists: new Map(), inMemoryFilters: [], inMemoryFiltersCompiled: '', // https://github.com/uBlockOrigin/uBlock-issues/issues/974 // This can be used to defer filtering decision-making. readyToFilter: false, supportStats: { allReadyAfter: '?', maxAssetCacheWait: '?', }, pageStores: new Map(), pageStoresToken: 0, storageQuota: vAPI.storage.QUOTA_BYTES, storageUsed: 0, noopFunc: function(){}, apiErrorCount: 0, maybeGoodPopup: { tabId: 0, url: '', }, epickerArgs: { eprom: null, mouse: false, target: '', zap: false, }, scriptlets: {}, cspNoInlineScript: "script-src 'unsafe-eval' * blob: data:", cspNoScripting: 'script-src http: https:', cspNoInlineFont: 'font-src *', liveBlockingProfiles: [], blockingProfileColorCache: new Map(), parsedTrustedListPrefixes: [], uiAccentStylesheet: '', }; µBlock.domainFromHostname = domainFromHostname; µBlock.hostnameFromURI = hostnameFromURI; µBlock.FilteringContext = class extends FilteringContext { duplicate() { return (new µBlock.FilteringContext(this)); } fromTabId(tabId) { const tabContext = µBlock.tabContextManager.mustLookup(tabId); this.tabOrigin = tabContext.origin; this.tabHostname = tabContext.rootHostname; this.tabDomain = tabContext.rootDomain; this.tabId = tabContext.tabId; return this; } maybeFromDocumentURL(documentUrl) { if ( documentUrl === undefined ) { return; } if ( documentUrl.startsWith(this.tabOrigin) ) { return; } this.tabOrigin = originFromURI(µBlock.normalizeTabURL(0, documentUrl)); this.tabHostname = hostnameFromURI(this.tabOrigin); this.tabDomain = domainFromHostname(this.tabHostname); } // https://github.com/uBlockOrigin/uBlock-issues/issues/459 // In case of a request for frame and if ever no context is specified, // assume the origin of the context is the same as the request itself. fromWebrequestDetails(details) { const tabId = details.tabId; this.type = details.type; const isMainFrame = this.itype === this.MAIN_FRAME; if ( isMainFrame && tabId > 0 ) { µBlock.tabContextManager.push(tabId, details.url); } this.fromTabId(tabId); // Must be called AFTER tab context management this.realm = ''; this.id = details.requestId; this.setMethod(details.method); this.setURL(details.url); this.aliasURL = details.aliasURL || undefined; this.redirectURL = undefined; this.filter = undefined; if ( this.itype !== this.SUB_FRAME ) { this.docId = details.frameId; this.frameId = -1; } else { this.docId = details.parentFrameId; this.frameId = details.frameId; } if ( this.tabId > 0 ) { if ( this.docId === 0 ) { if ( isMainFrame === false ) { this.maybeFromDocumentURL(details.documentUrl); } this.docOrigin = this.tabOrigin; this.docHostname = this.tabHostname; this.docDomain = this.tabDomain; return this; } if ( details.documentUrl !== undefined ) { this.setDocOriginFromURL(details.documentUrl); return this; } const pageStore = µBlock.pageStoreFromTabId(this.tabId); const docStore = pageStore && pageStore.getFrameStore(this.docId); if ( docStore ) { this.setDocOriginFromURL(docStore.rawURL); } else { this.setDocOrigin(this.tabOrigin); } return this; } if ( details.documentUrl !== undefined ) { const origin = originFromURI( µBlock.normalizeTabURL(0, details.documentUrl) ); this.setDocOrigin(origin).setTabOrigin(origin); return this; } const origin = (this.itype & this.FRAME_ANY) !== 0 ? originFromURI(this.url) : this.tabOrigin; this.setDocOrigin(origin).setTabOrigin(origin); return this; } getTabOrigin() { if ( this.tabOrigin === undefined ) { const tabContext = µBlock.tabContextManager.mustLookup(this.tabId); this.tabOrigin = tabContext.origin; this.tabHostname = tabContext.rootHostname; this.tabDomain = tabContext.rootDomain; } return super.getTabOrigin(); } toLogger() { const details = { id: this.id, tstamp: Date.now(), realm: this.realm, method: this.getMethodName(), type: this.stype, tabId: this.tabId, tabDomain: this.getTabDomain(), tabHostname: this.getTabHostname(), docDomain: this.getDocDomain(), docHostname: this.getDocHostname(), domain: this.getDomain(), hostname: this.getHostname(), url: this.url, aliasURL: this.aliasURL, filter: undefined, }; // Many filters may have been applied to the current context if ( Array.isArray(this.filter) === false ) { details.filter = this.filter; return logger.writeOne(details); } for ( const filter of this.filter ) { details.filter = filter; logger.writeOne(details); } } }; µBlock.filteringContext = new µBlock.FilteringContext(); self.µBlock = µBlock; /******************************************************************************/ export default µBlock;