1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-09 12:22:33 +01:00

Improve interactivity for blocked large media elements

Related issues:
- https://github.com/gorhill/uBlock/issues/1390
- https://github.com/gorhill/uBlock/issues/2334

The deadline to interactively load a specific media
element has been extended from 2sec to 5sec.

Clicking over a blocked large media element will cause
uBO to lookup and handle all potentially blocked large
elements at the cursor position. This should take care
of being able to unblock media elements hidden under
other DOM object.

The CSS style applied to blocked large media elements
has been fine tuned to improve interactivity.

uBO will now remember the specific media elements which
were unblocked and keep them exempted from being
further blocked. This would be an issue when unblocking
a video and then a bit later seeking to another point
in the video, in which case uBO would again block
network requests for that video.
This commit is contained in:
Raymond Hill 2020-10-18 10:07:46 -04:00
parent 9947fcf4d5
commit 53dd339d78
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
5 changed files with 113 additions and 76 deletions

View File

@ -1641,7 +1641,7 @@ const onMessage = function(request, sender, callback) {
case 'temporarilyAllowLargeMediaElement': case 'temporarilyAllowLargeMediaElement':
if ( pageStore !== null ) { if ( pageStore !== null ) {
pageStore.allowLargeMediaElementsUntil = Date.now() + 2000; pageStore.allowLargeMediaElementsUntil = Date.now() + 5000;
} }
break; break;

View File

@ -244,7 +244,7 @@ const PageStore = class {
typeof this.allowLargeMediaElementsUntil !== 'number' || typeof this.allowLargeMediaElementsUntil !== 'number' ||
tabContext.rootHostname !== this.tabHostname tabContext.rootHostname !== this.tabHostname
) { ) {
this.allowLargeMediaElementsUntil = 0; this.allowLargeMediaElementsUntil = Date.now();
} }
this.tabHostname = tabContext.rootHostname; this.tabHostname = tabContext.rootHostname;
@ -260,6 +260,7 @@ const PageStore = class {
this.largeMediaCount = 0; this.largeMediaCount = 0;
this.largeMediaTimer = null; this.largeMediaTimer = null;
this.internalRedirectionCount = 0; this.internalRedirectionCount = 0;
this.allowLargeMediaElementsRegex = undefined;
this.extraData.clear(); this.extraData.clear();
this.frameAddCount = 0; this.frameAddCount = 0;
@ -339,7 +340,8 @@ const PageStore = class {
this.rawURL = ''; this.rawURL = '';
this.hostnameToCountMap = null; this.hostnameToCountMap = null;
this.netFilteringCache.empty(); this.netFilteringCache.empty();
this.allowLargeMediaElementsUntil = 0; this.allowLargeMediaElementsUntil = Date.now();
this.allowLargeMediaElementsRegex = undefined;
if ( this.largeMediaTimer !== null ) { if ( this.largeMediaTimer !== null ) {
clearTimeout(this.largeMediaTimer); clearTimeout(this.largeMediaTimer);
this.largeMediaTimer = null; this.largeMediaTimer = null;
@ -438,7 +440,12 @@ const PageStore = class {
temporarilyAllowLargeMediaElements(state) { temporarilyAllowLargeMediaElements(state) {
this.largeMediaCount = 0; this.largeMediaCount = 0;
µb.contextMenu.update(this.tabId); µb.contextMenu.update(this.tabId);
this.allowLargeMediaElementsUntil = state ? Date.now() + 86400000 : 0; if ( state ) {
this.allowLargeMediaElementsUntil = 0;
this.allowLargeMediaElementsRegex = undefined;
} else {
this.allowLargeMediaElementsUntil = Date.now();
}
µb.scriptlets.injectDeep(this.tabId, 'load-large-media-all'); µb.scriptlets.injectDeep(this.tabId, 'load-large-media-all');
} }
@ -704,7 +711,23 @@ const PageStore = class {
filterLargeMediaElement(fctxt, size) { filterLargeMediaElement(fctxt, size) {
fctxt.filter = undefined; fctxt.filter = undefined;
if ( this.allowLargeMediaElementsUntil === 0 ) {
return 0;
}
// Disregard large media elements previously allowed: for example, to
// seek inside a previously allowed audio/video.
if (
this.allowLargeMediaElementsRegex instanceof RegExp &&
this.allowLargeMediaElementsRegex.test(fctxt.url)
) {
return 0;
}
if ( Date.now() < this.allowLargeMediaElementsUntil ) { if ( Date.now() < this.allowLargeMediaElementsUntil ) {
const sources = this.allowLargeMediaElementsRegex instanceof RegExp
? [ this.allowLargeMediaElementsRegex.source ]
: [];
sources.push('^' + µb.escapeRegex(fctxt.url));
this.allowLargeMediaElementsRegex = new RegExp(sources.join('|'));
return 0; return 0;
} }
if ( if (
@ -713,6 +736,7 @@ const PageStore = class {
fctxt.getTabHostname() fctxt.getTabHostname()
) !== true ) !== true
) { ) {
this.allowLargeMediaElementsUntil = 0;
return 0; return 0;
} }
if ( (size >>> 10) < µb.userSettings.largeMediaSize ) { if ( (size >>> 10) < µb.userSettings.largeMediaSize ) {

View File

@ -19,48 +19,36 @@
Home: https://github.com/gorhill/uBlock Home: https://github.com/gorhill/uBlock
*/ */
'use strict';
/******************************************************************************/ /******************************************************************************/
(function() { (( ) => {
'use strict';
/******************************************************************************/ /******************************************************************************/
// For all media resources which have failed to load, trigger a reload. // For all media resources which have failed to load, trigger a reload.
var elems, i, elem, src;
// <audio> and <video> elements. // <audio> and <video> elements.
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
elems = document.querySelectorAll('audio,video'); for ( const elem of document.querySelectorAll('audio,video') ) {
i = elems.length; if ( elem.error === null ) { continue; }
while ( i-- ) { elem.load();
elem = elems[i];
if ( elem.error !== null ) {
elem.load();
}
} }
// <img> elements. // <img> elements.
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
elems = document.querySelectorAll('img'); for ( const elem of document.querySelectorAll('img') ) {
i = elems.length; if ( elem.naturalWidth !== 0 && elem.naturalHeight !== 0 ) { continue; }
while ( i-- ) {
elem = elems[i];
if ( elem.naturalWidth !== 0 && elem.naturalHeight !== 0 ) {
continue;
}
if ( window.getComputedStyle(elem).getPropertyValue('display') === 'none' ) { if ( window.getComputedStyle(elem).getPropertyValue('display') === 'none' ) {
continue; continue;
} }
src = elem.getAttribute('src'); const src = elem.getAttribute('src') || '';
if ( src ) { if ( src === '' ) { continue; }
elem.removeAttribute('src'); elem.removeAttribute('src');
elem.setAttribute('src', src); elem.setAttribute('src', src);
}
} }
/******************************************************************************/ /******************************************************************************/

View File

@ -36,9 +36,10 @@ if ( typeof vAPI !== 'object' || vAPI.loadLargeMediaInteractive === true ) {
const largeMediaElementAttribute = 'data-' + vAPI.sessionId; const largeMediaElementAttribute = 'data-' + vAPI.sessionId;
const largeMediaElementSelector = const largeMediaElementSelector =
':root audio[' + largeMediaElementAttribute + '],\n' + ':root audio[' + largeMediaElementAttribute + '],\n' +
':root img[' + largeMediaElementAttribute + '],\n' + ':root img[' + largeMediaElementAttribute + '],\n' +
':root video[' + largeMediaElementAttribute + ']'; ':root picture[' + largeMediaElementAttribute + '],\n' +
':root video[' + largeMediaElementAttribute + ']';
/******************************************************************************/ /******************************************************************************/
@ -51,9 +52,7 @@ const mediaNotLoaded = function(elem) {
case 'video': case 'video':
return elem.error !== null; return elem.error !== null;
case 'img': case 'img':
if ( elem.naturalWidth !== 0 || elem.naturalHeight !== 0 ) { if ( elem.naturalWidth !== 0 || elem.naturalHeight !== 0 ) { break; }
break;
}
const style = window.getComputedStyle(elem); const style = window.getComputedStyle(elem);
// For some reason, style can be null with Pale Moon. // For some reason, style can be null with Pale Moon.
return style !== null ? return style !== null ?
@ -74,50 +73,57 @@ const mediaNotLoaded = function(elem) {
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
const surveyMissingMediaElements = function() { const surveyMissingMediaElements = function() {
var largeMediaElementCount = 0; let largeMediaElementCount = 0;
var elems = document.querySelectorAll('audio,img,video'); for ( const elem of document.querySelectorAll('audio,img,video') ) {
var i = elems.length, elem; if ( mediaNotLoaded(elem) === false ) { continue; }
while ( i-- ) { elem.setAttribute(largeMediaElementAttribute, '');
elem = elems[i]; largeMediaElementCount += 1;
if ( mediaNotLoaded(elem) ) { switch ( elem.localName ) {
elem.setAttribute(largeMediaElementAttribute, ''); case 'img': {
largeMediaElementCount += 1; const picture = elem.closest('picture');
if ( picture !== null ) {
picture.setAttribute(largeMediaElementAttribute, '');
}
} break;
default:
break;
} }
} }
return largeMediaElementCount; return largeMediaElementCount;
}; };
if ( surveyMissingMediaElements() === 0 ) { if ( surveyMissingMediaElements() === 0 ) { return; }
return;
}
vAPI.loadLargeMediaInteractive = true; vAPI.loadLargeMediaInteractive = true;
// Insert custom style tag. // Insert CSS to highlight blocked media elements.
let styleTag = document.createElement('style'); if ( vAPI.largeMediaElementStyleSheet === undefined ) {
styleTag.setAttribute('type', 'text/css'); vAPI.largeMediaElementStyleSheet = [
styleTag.textContent = [ largeMediaElementSelector + ' {',
largeMediaElementSelector + ' {', 'border: 2px dotted red !important;',
'border: 1px dotted red !important;', 'box-sizing: border-box !important;',
'box-sizing: border-box !important;', 'cursor: zoom-in !important;',
'cursor: zoom-in !important;', 'display: inline-block;',
'display: inline-block;', 'font-size: 1rem !important;',
'font-size: 1em !important;', 'min-height: 1em !important;',
'min-height: 1em !important;', 'min-width: 1em !important;',
'min-width: 1em !important;', 'opacity: 1 !important;',
'opacity: 1 !important;', 'outline: none !important;',
'outline: none !important;', 'visibility: visible !important;',
'}' 'z-index: 2147483647',
].join('\n'); '}',
document.head.appendChild(styleTag); ].join('\n');
vAPI.userStylesheet.add(vAPI.largeMediaElementStyleSheet);
vAPI.userStylesheet.apply();
}
/******************************************************************************/ /******************************************************************************/
const stayOrLeave = (( ) => { const stayOrLeave = (( ) => {
let timer = null; let timer;
const timeoutHandler = function(leaveNow) { const timeoutHandler = function(leaveNow) {
timer = null; timer = undefined;
if ( leaveNow !== true ) { if ( leaveNow !== true ) {
if ( if (
document.querySelector(largeMediaElementSelector) !== null || document.querySelector(largeMediaElementSelector) !== null ||
@ -127,9 +133,8 @@ const stayOrLeave = (( ) => {
} }
} }
// Leave // Leave
if ( styleTag !== null ) { for ( const elem of document.querySelectorAll(largeMediaElementSelector) ) {
styleTag.parentNode.removeChild(styleTag); elem.removeAttribute(largeMediaElementAttribute);
styleTag = null;
} }
vAPI.loadLargeMediaInteractive = false; vAPI.loadLargeMediaInteractive = false;
document.removeEventListener('error', onLoadError, true); document.removeEventListener('error', onLoadError, true);
@ -137,7 +142,7 @@ const stayOrLeave = (( ) => {
}; };
return function(leaveNow) { return function(leaveNow) {
if ( timer !== null ) { if ( timer !== undefined ) {
clearTimeout(timer); clearTimeout(timer);
} }
if ( leaveNow ) { if ( leaveNow ) {
@ -160,24 +165,44 @@ const loadImage = async function(elem) {
elem.setAttribute('src', src); elem.setAttribute('src', src);
elem.removeAttribute(largeMediaElementAttribute); elem.removeAttribute(largeMediaElementAttribute);
switch ( elem.localName ) {
case 'img': {
const picture = elem.closest('picture');
if ( picture !== null ) {
picture.removeAttribute(largeMediaElementAttribute);
}
} break;
default:
break;
}
stayOrLeave(); stayOrLeave();
}; };
/******************************************************************************/ /******************************************************************************/
const onMouseClick = function(ev) { const onMouseClick = function(ev) {
if ( ev.button !== 0 ) { return; } if ( ev.button !== 0 || ev.isTrusted === false ) { return; }
const elem = ev.target; const toLoad = [];
if ( elem.matches(largeMediaElementSelector) === false ) { return; } const elems = document.elementsFromPoint instanceof Function
? document.elementsFromPoint(ev.clientX, ev.clientY)
: [ ev.target ];
for ( const elem of elems ) {
if ( elem.matches(largeMediaElementSelector) && mediaNotLoaded(elem) ) {
toLoad.push(elem);
}
}
if ( mediaNotLoaded(elem) === false ) { if ( toLoad.length === 0 ) {
elem.removeAttribute(largeMediaElementAttribute);
stayOrLeave(); stayOrLeave();
return; return;
} }
loadImage(elem); for ( const elem of toLoad ) {
loadImage(elem);
}
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
@ -210,7 +235,7 @@ document.addEventListener('error', onLoadError, true);
/******************************************************************************/ /******************************************************************************/
vAPI.shutdown.add(function() { vAPI.shutdown.add(( ) => {
stayOrLeave(true); stayOrLeave(true);
}); });

View File

@ -469,7 +469,7 @@ const onHeadersReceived = function(details) {
if ( isRootDoc ) { if ( isRootDoc ) {
const contentType = headerValueFromName('content-type', responseHeaders); const contentType = headerValueFromName('content-type', responseHeaders);
if ( reMediaContentTypes.test(contentType) ) { if ( reMediaContentTypes.test(contentType) ) {
pageStore.allowLargeMediaElementsUntil = Date.now() + 86400000; pageStore.allowLargeMediaElementsUntil = 0;
return; return;
} }
} }