mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 17:17:57 +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:
parent
9947fcf4d5
commit
53dd339d78
5 changed files with 113 additions and 76 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue