diff --git a/platform/common/vapi-background.js b/platform/common/vapi-background.js index 7d6c60a1b..2a4d640a6 100644 --- a/platform/common/vapi-background.js +++ b/platform/common/vapi-background.js @@ -122,6 +122,83 @@ vAPI.browserSettings = (( ) => { if ( bp instanceof Object === false ) { return; } return { + // Whether the WebRTC-related privacy API is crashy is an open question + // only for Chromium proper (because it can be compiled without the + // WebRTC feature): hence avoid overhead of the evaluation (which uses + // an iframe) for platforms where it's a non-issue. + // https://github.com/uBlockOrigin/uBlock-issues/issues/9 + // Some Chromium builds are made to look like a Chrome build. + webRTCSupported: vAPI.webextFlavor.soup.has('chromium') === false || undefined, + + // Calling with `true` means IP address leak is not prevented. + // https://github.com/gorhill/uBlock/issues/533 + // We must first check wether this Chromium-based browser was compiled + // with WebRTC support. To do this, we use an iframe, this way the + // empty RTCPeerConnection object we create to test for support will + // be properly garbage collected. This prevents issues such as + // a computer unable to enter into sleep mode, as reported in the + // Chrome store: + // https://github.com/gorhill/uBlock/issues/533#issuecomment-167931681 + setWebrtcIPAddress: function(setting) { + // We don't know yet whether this browser supports WebRTC: find out. + if ( this.webRTCSupported === undefined ) { + // If asked to leave WebRTC setting alone at this point in the + // code, this means we never grabbed the setting in the first + // place. + if ( setting ) { return; } + this.webRTCSupported = { setting: setting }; + let iframe = document.createElement('iframe'); + const messageHandler = ev => { + if ( ev.origin !== self.location.origin ) { return; } + window.removeEventListener('message', messageHandler); + const setting = this.webRTCSupported.setting; + this.webRTCSupported = ev.data === 'webRTCSupported'; + this.setWebrtcIPAddress(setting); + iframe.parentNode.removeChild(iframe); + iframe = null; + }; + window.addEventListener('message', messageHandler); + iframe.src = 'is-webrtc-supported.html'; + document.body.appendChild(iframe); + return; + } + + // We are waiting for a response from our iframe. This makes the code + // safe to re-entrancy. + if ( typeof this.webRTCSupported === 'object' ) { + this.webRTCSupported.setting = setting; + return; + } + + // https://github.com/gorhill/uBlock/issues/533 + // WebRTC not supported: `webRTCMultipleRoutesEnabled` can NOT be + // safely accessed. Accessing the property will cause full browser + // crash. + if ( this.webRTCSupported !== true ) { return; } + + const bpn = bp.network; + + if ( setting ) { + bpn.webRTCIPHandlingPolicy.clear({ + scope: 'regular', + }); + } else { + // https://github.com/uBlockOrigin/uAssets/issues/333#issuecomment-289426678 + // Leverage virtuous side-effect of strictest setting. + // https://github.com/gorhill/uBlock/issues/3009 + // Firefox currently works differently, use + // `default_public_interface_only` for now. + // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/privacy/network#Browser_compatibility + // Firefox 70+ supports `disable_non_proxied_udp` + const value = + vAPI.webextFlavor.soup.has('firefox') === false || + vAPI.webextFlavor.major < 70 + ? 'default_public_interface_only' + : 'disable_non_proxied_udp'; + bpn.webRTCIPHandlingPolicy.set({ value, scope: 'regular' }); + } + }, + set: function(details) { for ( const setting in details ) { if ( details.hasOwnProperty(setting) === false ) { continue; } @@ -156,6 +233,10 @@ vAPI.browserSettings = (( ) => { } break; + case 'webrtcIPAddress': + this.setWebrtcIPAddress(!!details[setting]); + break; + default: break; } diff --git a/src/js/background.js b/src/js/background.js index 7d5bd166d..5bb86f021 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -110,6 +110,7 @@ const userSettingsDefault = { requestLogMaxEntries: 1000, showIconBadge: true, tooltipsDisabled: false, + webrtcIPAddressHidden: false, }; const µBlock = { // jshint ignore:line diff --git a/src/js/settings.js b/src/js/settings.js index 2bbc38cc5..8dba98ddb 100644 --- a/src/js/settings.js +++ b/src/js/settings.js @@ -176,6 +176,7 @@ const onLocalDataReceived = function(details) { if ( details.privacySettingsSupported === false ) { uDom('[data-setting-name="prefetchingDisabled"]').attr('disabled', ''); uDom('[data-setting-name="hyperlinkAuditingDisabled"]').attr('disabled', ''); + uDom('[data-setting-name="webrtcIPAddressHidden"]').attr('disabled', ''); } }; diff --git a/src/js/start.js b/src/js/start.js index 6a02e32a1..1a7398536 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -212,6 +212,7 @@ const onUserSettingsReady = function(fetched) { vAPI.browserSettings.set({ 'hyperlinkAuditing': !µb.userSettings.hyperlinkAuditingDisabled, 'prefetching': !µb.userSettings.prefetchingDisabled, + 'webrtcIPAddress': !µb.userSettings.webrtcIPAddressHidden }); } diff --git a/src/js/ublock.js b/src/js/ublock.js index 6343e7c02..f9db09e2b 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -394,6 +394,11 @@ const matchBucket = function(url, hostname, bucket, start) { vAPI.browserSettings.set({ 'prefetching': !value }); } break; + case 'webrtcIPAddressHidden': + if ( this.privacySettingsSupported ) { + vAPI.browserSettings.set({ 'webrtcIPAddress': !value }); + } + break; default: break; } diff --git a/src/settings.html b/src/settings.html index a3213e22d..a3739a794 100644 --- a/src/settings.html +++ b/src/settings.html @@ -26,6 +26,7 @@
+