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':
if ( pageStore !== null ) {
pageStore.allowLargeMediaElementsUntil = Date.now() + 2000;
pageStore.allowLargeMediaElementsUntil = Date.now() + 5000;
}
break;

View file

@ -244,7 +244,7 @@ const PageStore = class {
typeof this.allowLargeMediaElementsUntil !== 'number' ||
tabContext.rootHostname !== this.tabHostname
) {
this.allowLargeMediaElementsUntil = 0;
this.allowLargeMediaElementsUntil = Date.now();
}
this.tabHostname = tabContext.rootHostname;
@ -260,6 +260,7 @@ const PageStore = class {
this.largeMediaCount = 0;
this.largeMediaTimer = null;
this.internalRedirectionCount = 0;
this.allowLargeMediaElementsRegex = undefined;
this.extraData.clear();
this.frameAddCount = 0;
@ -339,7 +340,8 @@ const PageStore = class {
this.rawURL = '';
this.hostnameToCountMap = null;
this.netFilteringCache.empty();
this.allowLargeMediaElementsUntil = 0;
this.allowLargeMediaElementsUntil = Date.now();
this.allowLargeMediaElementsRegex = undefined;
if ( this.largeMediaTimer !== null ) {
clearTimeout(this.largeMediaTimer);
this.largeMediaTimer = null;
@ -438,7 +440,12 @@ const PageStore = class {
temporarilyAllowLargeMediaElements(state) {
this.largeMediaCount = 0;
µ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');
}
@ -704,7 +711,23 @@ const PageStore = class {
filterLargeMediaElement(fctxt, size) {
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 ) {
const sources = this.allowLargeMediaElementsRegex instanceof RegExp
? [ this.allowLargeMediaElementsRegex.source ]
: [];
sources.push('^' + µb.escapeRegex(fctxt.url));
this.allowLargeMediaElementsRegex = new RegExp(sources.join('|'));
return 0;
}
if (
@ -713,6 +736,7 @@ const PageStore = class {
fctxt.getTabHostname()
) !== true
) {
this.allowLargeMediaElementsUntil = 0;
return 0;
}
if ( (size >>> 10) < µb.userSettings.largeMediaSize ) {

View file

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

View file

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

View file

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