1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-17 16:02:33 +01:00
uBlock/src/js/scriptlets/load-large-media-interactive.js

278 lines
8.1 KiB
JavaScript
Raw Normal View History

/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2015-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
*/
'use strict';
/******************************************************************************/
(( ) => {
/******************************************************************************/
// This can happen
if ( typeof vAPI !== 'object' || vAPI.loadAllLargeMedia instanceof Function ) {
return;
}
/******************************************************************************/
const largeMediaElementAttribute = 'data-' + vAPI.sessionId;
const largeMediaElementSelector =
':root audio[' + largeMediaElementAttribute + '],\n' +
':root img[' + largeMediaElementAttribute + '],\n' +
':root picture[' + largeMediaElementAttribute + '],\n' +
':root video[' + largeMediaElementAttribute + ']';
/******************************************************************************/
const mediaNotLoaded = function(elem) {
switch ( elem.localName ) {
case 'audio':
case 'video': {
const src = elem.src || '';
if ( src.startsWith('blob:') ) {
elem.autoplay = false;
elem.pause();
}
return elem.readyState === 0 || elem.error !== null;
}
case 'img': {
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 ?
style.getPropertyValue('display') !== 'none' :
elem.offsetHeight !== 0 && elem.offsetWidth !== 0;
}
default:
break;
}
return false;
};
/******************************************************************************/
// For all media resources which have failed to load, trigger a reload.
// <audio> and <video> elements.
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
const surveyMissingMediaElements = function() {
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; }
// 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;',
'filter: none !important;',
'font-size: 1rem !important;',
'min-height: 1em !important;',
'min-width: 1em !important;',
'opacity: 1 !important;',
'outline: none !important;',
'transform: none !important;',
'visibility: visible !important;',
'z-index: 2147483647',
'}',
].join('\n');
vAPI.userStylesheet.add(vAPI.largeMediaElementStyleSheet);
vAPI.userStylesheet.apply();
}
/******************************************************************************/
const loadMedia = async function(elem) {
const src = elem.getAttribute('src') || '';
elem.removeAttribute('src');
await vAPI.messaging.send('scriptlets', {
what: 'temporarilyAllowLargeMediaElement',
});
if ( src !== '' ) {
elem.setAttribute('src', src);
}
elem.load();
};
/******************************************************************************/
const loadImage = async function(elem) {
const src = elem.getAttribute('src') || '';
elem.removeAttribute('src');
await vAPI.messaging.send('scriptlets', {
what: 'temporarilyAllowLargeMediaElement',
});
if ( src !== '' ) {
elem.setAttribute('src', src);
}
};
/******************************************************************************/
const loadMany = function(elems) {
for ( const elem of elems ) {
switch ( elem.localName ) {
case 'audio':
case 'video':
loadMedia(elem);
break;
case 'img':
loadImage(elem);
break;
default:
break;
}
}
};
/******************************************************************************/
const onMouseClick = function(ev) {
if ( ev.button !== 0 || ev.isTrusted === 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) === false ) { continue; }
elem.removeAttribute(largeMediaElementAttribute);
if ( mediaNotLoaded(elem) ) {
toLoad.push(elem);
}
}
if ( toLoad.length === 0 ) { return; }
loadMany(toLoad);
ev.preventDefault();
ev.stopPropagation();
};
document.addEventListener('click', onMouseClick, true);
/******************************************************************************/
const onLoadedData = function(ev) {
const media = ev.target;
if ( media.localName !== 'audio' && media.localName !== 'video' ) {
return;
}
const src = media.src;
if ( typeof src === 'string' && src.startsWith('blob:') === false ) {
return;
}
media.autoplay = false;
media.pause();
};
document.addEventListener('loadeddata', onLoadedData);
/******************************************************************************/
const onLoad = function(ev) {
ev.target.removeAttribute(largeMediaElementAttribute);
};
document.addEventListener('load', onLoad, true);
/******************************************************************************/
const onLoadError = function(ev) {
const elem = ev.target;
if ( mediaNotLoaded(elem) ) {
elem.setAttribute(largeMediaElementAttribute, '');
}
};
document.addEventListener('error', onLoadError, true);
/******************************************************************************/
vAPI.loadAllLargeMedia = function() {
document.removeEventListener('click', onMouseClick, true);
document.removeEventListener('loadeddata', onLoadedData, true);
document.removeEventListener('load', onLoad, true);
document.removeEventListener('error', onLoadError, true);
const toLoad = [];
for ( const elem of document.querySelectorAll(largeMediaElementSelector) ) {
elem.removeAttribute(largeMediaElementAttribute);
if ( mediaNotLoaded(elem) ) {
toLoad.push(elem);
}
}
loadMany(toLoad);
};
/******************************************************************************/
})();
/*******************************************************************************
DO NOT:
- Remove the following code
- Add code beyond the following code
Reason:
- https://github.com/gorhill/uBlock/pull/3721
- uBO never uses the return value from injected content scripts
**/
void 0;