mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-22 18:32:45 +01:00
#1163: this implements "block elements by size"
This commit is contained in:
parent
08d7ce96aa
commit
89148351e8
@ -245,10 +245,6 @@ vAPI.tabs.registerListeners = function() {
|
||||
}
|
||||
};
|
||||
|
||||
var onUpdated = function(tabId, changeInfo, tab) {
|
||||
onUpdatedClient(tabId, changeInfo, tab);
|
||||
};
|
||||
|
||||
var onCommitted = function(details) {
|
||||
if ( details.frameId !== 0 ) {
|
||||
return;
|
||||
@ -256,9 +252,18 @@ vAPI.tabs.registerListeners = function() {
|
||||
onNavigationClient(details);
|
||||
};
|
||||
|
||||
chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget);
|
||||
var onActivated = function(details) {
|
||||
vAPI.contextMenu.onMustUpdate(details.tabId);
|
||||
};
|
||||
|
||||
var onUpdated = function(tabId, changeInfo, tab) {
|
||||
onUpdatedClient(tabId, changeInfo, tab);
|
||||
};
|
||||
|
||||
chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate);
|
||||
chrome.webNavigation.onCommitted.addListener(onCommitted);
|
||||
chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget);
|
||||
chrome.tabs.onActivated.addListener(onActivated);
|
||||
chrome.tabs.onUpdated.addListener(onUpdated);
|
||||
|
||||
if ( typeof this.onClosed === 'function' ) {
|
||||
@ -291,9 +296,7 @@ vAPI.tabs.get = function(tabId, callback) {
|
||||
|
||||
var onTabReceived = function(tabs) {
|
||||
// https://code.google.com/p/chromium/issues/detail?id=410868#c8
|
||||
if ( chrome.runtime.lastError ) {
|
||||
/* noop */
|
||||
}
|
||||
void chrome.runtime.lastError;
|
||||
callback(tabs[0]);
|
||||
};
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, onTabReceived);
|
||||
@ -553,6 +556,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
|
||||
{ '19': 'img/browsericons/icon19-off.png', '38': 'img/browsericons/icon38-off.png' };
|
||||
|
||||
chrome.browserAction.setIcon({ tabId: tabId, path: iconPaths }, onIconReady);
|
||||
vAPI.contextMenu.onMustUpdate(tabId);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -784,6 +788,51 @@ vAPI.net.registerListeners = function() {
|
||||
var µb = µBlock;
|
||||
var µburi = µb.URI;
|
||||
|
||||
// Chromium-based browsers understand only these network request types.
|
||||
var validTypes = {
|
||||
'main_frame': true,
|
||||
'sub_frame': true,
|
||||
'stylesheet': true,
|
||||
'script': true,
|
||||
'image': true,
|
||||
'object': true,
|
||||
'xmlhttprequest': true,
|
||||
'other': true
|
||||
};
|
||||
|
||||
var denormalizeTypes = function(aa) {
|
||||
if ( aa.length === 0 ) {
|
||||
return Object.keys(validTypes);
|
||||
}
|
||||
var out = [];
|
||||
var i = aa.length,
|
||||
type,
|
||||
needOther = true;
|
||||
while ( i-- ) {
|
||||
type = aa[i];
|
||||
if ( validTypes.hasOwnProperty(type) ) {
|
||||
out.push(type);
|
||||
}
|
||||
if ( type === 'other' ) {
|
||||
needOther = false;
|
||||
}
|
||||
}
|
||||
if ( needOther ) {
|
||||
out.push('other');
|
||||
}
|
||||
return out;
|
||||
};
|
||||
|
||||
var headerValue = function(headers, name) {
|
||||
var i = headers.length;
|
||||
while ( i-- ) {
|
||||
if ( headers[i].name.toLowerCase() === name ) {
|
||||
return headers[i].value.trim();
|
||||
}
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
var normalizeRequestDetails = function(details) {
|
||||
µburi.set(details.url);
|
||||
|
||||
@ -801,34 +850,45 @@ vAPI.net.registerListeners = function() {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/862
|
||||
// If no transposition possible, transpose to `object` as per
|
||||
// Chromium bug 410382 (see below)
|
||||
if ( pos === -1 ) {
|
||||
details.type = 'object';
|
||||
return;
|
||||
}
|
||||
if ( pos !== -1 ) {
|
||||
var ext = tail.slice(pos) + '.';
|
||||
if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(ext) !== -1 ) {
|
||||
details.type = 'font';
|
||||
return;
|
||||
}
|
||||
|
||||
var ext = tail.slice(pos) + '.';
|
||||
if ( '.eot.ttf.otf.svg.woff.woff2.'.indexOf(ext) !== -1 ) {
|
||||
details.type = 'font';
|
||||
return;
|
||||
}
|
||||
// Still need this because often behind-the-scene requests are wrongly
|
||||
// categorized as 'other'
|
||||
if ( '.ico.png.gif.jpg.jpeg.webp.'.indexOf(ext) !== -1 ) {
|
||||
details.type = 'image';
|
||||
return;
|
||||
}
|
||||
// https://code.google.com/p/chromium/issues/detail?id=410382
|
||||
details.type = 'object';
|
||||
};
|
||||
if ( '.mp3.mp4.webm.'.indexOf(ext) !== -1 ) {
|
||||
details.type = 'media';
|
||||
return;
|
||||
}
|
||||
|
||||
var headerValue = function(headers, name) {
|
||||
var i = headers.length;
|
||||
while ( i-- ) {
|
||||
if ( headers[i].name.toLowerCase() === name ) {
|
||||
return headers[i].value.trim();
|
||||
// Still need this because often behind-the-scene requests are wrongly
|
||||
// categorized as 'other'
|
||||
if ( '.ico.png.gif.jpg.jpeg.webp.'.indexOf(ext) !== -1 ) {
|
||||
details.type = 'image';
|
||||
return;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
|
||||
// Try to extract type from response headers if present.
|
||||
if ( details.responseHeaders ) {
|
||||
var contentType = headerValue(details.responseHeaders, 'content-type');
|
||||
if ( contentType.startsWith('font/') ) {
|
||||
details.type = 'font';
|
||||
return;
|
||||
}
|
||||
if ( contentType.startsWith('image/') ) {
|
||||
details.type = 'image';
|
||||
return;
|
||||
}
|
||||
if ( contentType.startsWith('audio/') || contentType.startsWith('video/') ) {
|
||||
details.type = 'media';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// https://code.google.com/p/chromium/issues/detail?id=410382
|
||||
details.type = 'object';
|
||||
};
|
||||
|
||||
var onBeforeRequestClient = this.onBeforeRequest.callback;
|
||||
@ -839,13 +899,7 @@ vAPI.net.registerListeners = function() {
|
||||
|
||||
var onHeadersReceivedClient = this.onHeadersReceived.callback;
|
||||
var onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0);
|
||||
var onHeadersReceivedTypes = onHeadersReceivedClientTypes.slice(0);
|
||||
if (
|
||||
onHeadersReceivedTypes.length !== 0 &&
|
||||
onHeadersReceivedTypes.indexOf('other') === -1
|
||||
) {
|
||||
onHeadersReceivedTypes.push('other');
|
||||
}
|
||||
var onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
|
||||
var onHeadersReceived = function(details) {
|
||||
normalizeRequestDetails(details);
|
||||
// Hack to work around Chromium API limitations, where requests of
|
||||
@ -857,21 +911,18 @@ vAPI.net.registerListeners = function() {
|
||||
// `other` always becomes `object` when it can't be normalized into
|
||||
// something else. Test case for "unfriendly" font URLs:
|
||||
// https://www.google.com/fonts
|
||||
if ( details.type === 'object' ) {
|
||||
if ( headerValue(details.responseHeaders, 'content-type').startsWith('font/') ) {
|
||||
details.type = 'font';
|
||||
var r = onBeforeRequestClient(details);
|
||||
if ( typeof r === 'object' && r.cancel === true ) {
|
||||
return { cancel: true };
|
||||
}
|
||||
}
|
||||
if (
|
||||
onHeadersReceivedClientTypes.length !== 0 &&
|
||||
onHeadersReceivedClientTypes.indexOf(details.type) === -1
|
||||
) {
|
||||
return;
|
||||
if ( details.type === 'font' ) {
|
||||
var r = onBeforeRequestClient(details);
|
||||
if ( typeof r === 'object' && r.cancel === true ) {
|
||||
return { cancel: true };
|
||||
}
|
||||
}
|
||||
if (
|
||||
onHeadersReceivedClientTypes.length !== 0 &&
|
||||
onHeadersReceivedClientTypes.indexOf(details.type) === -1
|
||||
) {
|
||||
return;
|
||||
}
|
||||
return onHeadersReceivedClient(details);
|
||||
};
|
||||
|
||||
@ -921,15 +972,46 @@ vAPI.net.registerListeners = function() {
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.contextMenu = {
|
||||
create: function(details, callback) {
|
||||
this.menuId = details.id;
|
||||
this.callback = callback;
|
||||
chrome.contextMenus.create(details);
|
||||
chrome.contextMenus.onClicked.addListener(this.callback);
|
||||
_callback: null,
|
||||
_entries: [],
|
||||
_createEntry: function(entry) {
|
||||
chrome.contextMenus.create(JSON.parse(JSON.stringify(entry)), function() {
|
||||
void chrome.runtime.lastError;
|
||||
});
|
||||
},
|
||||
remove: function() {
|
||||
chrome.contextMenus.onClicked.removeListener(this.callback);
|
||||
chrome.contextMenus.remove(this.menuId);
|
||||
onMustUpdate: function() {},
|
||||
setEntries: function(entries, callback) {
|
||||
entries = entries || [];
|
||||
var n = Math.max(this._entries.length, entries.length),
|
||||
oldEntryId, newEntry;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
oldEntryId = this._entries[i];
|
||||
newEntry = entries[i];
|
||||
if ( oldEntryId && newEntry ) {
|
||||
if ( newEntry.id !== oldEntryId ) {
|
||||
chrome.contextMenus.remove(oldEntryId);
|
||||
this._createEntry(newEntry);
|
||||
this._entries[i] = newEntry.id;
|
||||
}
|
||||
} else if ( oldEntryId && !newEntry ) {
|
||||
chrome.contextMenus.remove(oldEntryId);
|
||||
} else if ( !oldEntryId && newEntry ) {
|
||||
this._createEntry(newEntry);
|
||||
this._entries[i] = newEntry.id;
|
||||
}
|
||||
}
|
||||
n = this._entries.length = entries.length;
|
||||
callback = callback || null;
|
||||
if ( callback === this._callback ) {
|
||||
return;
|
||||
}
|
||||
if ( n !== 0 && callback !== null ) {
|
||||
chrome.contextMenus.onClicked.addListener(callback);
|
||||
this._callback = callback;
|
||||
} else if ( n === 0 && this._callback !== null ) {
|
||||
chrome.contextMenus.onClicked.removeListener(this._callback);
|
||||
this._callback = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global HTMLDocument, XMLDocument */
|
||||
|
||||
// For non background pages
|
||||
|
||||
/******************************************************************************/
|
||||
@ -29,6 +31,18 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/1124
|
||||
// Looks like `contentType` is on track to be standardized:
|
||||
// https://dom.spec.whatwg.org/#concept-document-content-type
|
||||
@ -43,14 +57,12 @@ var chrome = self.chrome;
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/456
|
||||
// Already injected?
|
||||
if ( vAPI.vapiClientInjected ) {
|
||||
//console.debug('vapi-client.js already injected: skipping.');
|
||||
if ( vAPI.sessionId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
vAPI.vapiClientInjected = true;
|
||||
vAPI.sessionId = String.fromCharCode(Date.now() % 26 + 97) +
|
||||
Math.random().toString(36).slice(2);
|
||||
Math.random().toString(36).slice(2);
|
||||
vAPI.chrome = true;
|
||||
|
||||
/******************************************************************************/
|
||||
@ -67,7 +79,6 @@ vAPI.shutdown = (function() {
|
||||
};
|
||||
|
||||
var exec = function() {
|
||||
//console.debug('Shutting down...');
|
||||
var job;
|
||||
while ( (job = jobs.pop()) ) {
|
||||
job();
|
||||
@ -89,28 +100,34 @@ vAPI.messaging = {
|
||||
pendingCount: 0,
|
||||
auxProcessId: 1,
|
||||
|
||||
onDisconnect: function() {
|
||||
this.port = null;
|
||||
this.close();
|
||||
vAPI.shutdown.exec();
|
||||
},
|
||||
|
||||
setup: function() {
|
||||
try {
|
||||
this.port = chrome.runtime.connect({name: vAPI.sessionId});
|
||||
this.port = chrome.runtime.connect({name: vAPI.sessionId}) || null;
|
||||
} catch (ex) {
|
||||
}
|
||||
if ( this.port === null ) {
|
||||
//console.error("uBlock> Can't patch things up. It's over.");
|
||||
vAPI.shutdown.exec();
|
||||
return false;
|
||||
}
|
||||
this.port.onMessage.addListener(messagingConnector);
|
||||
this.port.onDisconnect.addListener(this.onDisconnect.bind(this));
|
||||
return true;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
var port = this.port;
|
||||
if ( port === null ) {
|
||||
return;
|
||||
if ( port !== null ) {
|
||||
port.disconnect();
|
||||
port.onMessage.removeListener(messagingConnector);
|
||||
port.onDisconnect.removeListener(this.onDisconnect);
|
||||
this.port = null;
|
||||
}
|
||||
this.port = null;
|
||||
port.disconnect();
|
||||
port.onMessage.removeListener(messagingConnector);
|
||||
this.channels = {};
|
||||
// service pending callbacks
|
||||
var pending = this.pending, listener;
|
||||
|
@ -1440,6 +1440,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
|
||||
|
||||
if ( tabId === curTabId ) {
|
||||
tb.updateState(win, tabId);
|
||||
vAPI.contextMenu.onMustUpdate(tabId);
|
||||
}
|
||||
};
|
||||
|
||||
@ -2064,17 +2065,23 @@ var httpObserver = {
|
||||
},
|
||||
|
||||
handleResponseHeaders: function(channel, URI, channelData) {
|
||||
var type = this.typeMap[channelData[3]] || 'other';
|
||||
if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(type) === false ) {
|
||||
var requestType = this.typeMap[channelData[3]] || 'other';
|
||||
if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(requestType) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 'Content-Security-Policy' MUST come last in the array. Need to
|
||||
// revised this eventually.
|
||||
var responseHeaders = [];
|
||||
var value = this.getResponseHeader(channel, 'Content-Security-Policy');
|
||||
if ( value !== undefined ) {
|
||||
responseHeaders.push({ name: 'Content-Security-Policy', value: value });
|
||||
var value = channel.contentLength;
|
||||
if ( value !== -1 ) {
|
||||
responseHeaders.push({ name: 'Content-Length', value: value });
|
||||
}
|
||||
if ( requestType.endsWith('_frame') ) {
|
||||
value = this.getResponseHeader(channel, 'Content-Security-Policy');
|
||||
if ( value !== undefined ) {
|
||||
responseHeaders.push({ name: 'Content-Security-Policy', value: value });
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/966
|
||||
@ -2088,7 +2095,7 @@ var httpObserver = {
|
||||
parentFrameId: channelData[1],
|
||||
responseHeaders: responseHeaders,
|
||||
tabId: channelData[2],
|
||||
type: this.typeMap[channelData[3]] || 'other',
|
||||
type: requestType,
|
||||
url: URI.asciiSpec
|
||||
});
|
||||
|
||||
@ -2179,7 +2186,7 @@ var httpObserver = {
|
||||
|
||||
// Carry data for behind-the-scene redirects
|
||||
if ( channel instanceof Ci.nsIWritablePropertyBag ) {
|
||||
channel.setProperty( this.REQDATAKEY, [0, -1, null, vAPI.noTabId, rawtype]);
|
||||
channel.setProperty(this.REQDATAKEY, [0, -1, vAPI.noTabId, rawtype]);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -3171,173 +3178,20 @@ if ( vAPI.toolbarButton.init !== null ) {
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.contextMenu = {
|
||||
contextMap: {
|
||||
vAPI.contextMenu = (function() {
|
||||
var clientCallback = null;
|
||||
var clientEntries = [];
|
||||
|
||||
var contextMap = {
|
||||
frame: 'inFrame',
|
||||
link: 'onLink',
|
||||
image: 'onImage',
|
||||
audio: 'onAudio',
|
||||
video: 'onVideo',
|
||||
editable: 'onEditableArea'
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.contextMenu.displayMenuItem = function({target}) {
|
||||
var doc = target.ownerDocument;
|
||||
var gContextMenu = doc.defaultView.gContextMenu;
|
||||
if ( !gContextMenu.browser ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var menuitem = doc.getElementById(vAPI.contextMenu.menuItemId);
|
||||
var currentURI = gContextMenu.browser.currentURI;
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/105
|
||||
// TODO: Should the element picker works on any kind of pages?
|
||||
if ( !currentURI.schemeIs('http') && !currentURI.schemeIs('https') ) {
|
||||
menuitem.setAttribute('hidden', true);
|
||||
return;
|
||||
}
|
||||
|
||||
var ctx = vAPI.contextMenu.contexts;
|
||||
|
||||
if ( !ctx ) {
|
||||
menuitem.setAttribute('hidden', false);
|
||||
return;
|
||||
}
|
||||
|
||||
var ctxMap = vAPI.contextMenu.contextMap;
|
||||
|
||||
for ( var context of ctx ) {
|
||||
if (
|
||||
context === 'page' &&
|
||||
!gContextMenu.onLink &&
|
||||
!gContextMenu.onImage &&
|
||||
!gContextMenu.onEditableArea &&
|
||||
!gContextMenu.inFrame &&
|
||||
!gContextMenu.onVideo &&
|
||||
!gContextMenu.onAudio
|
||||
) {
|
||||
menuitem.setAttribute('hidden', false);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
ctxMap.hasOwnProperty(context) &&
|
||||
gContextMenu[ctxMap[context]]
|
||||
) {
|
||||
menuitem.setAttribute('hidden', false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
menuitem.setAttribute('hidden', true);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.contextMenu.register = (function() {
|
||||
var register = function(window) {
|
||||
if ( canRegister(window) !== true ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !this.menuItemId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( vAPI.fennec ) {
|
||||
// TODO https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/NativeWindow/contextmenus/add
|
||||
/*var nativeWindow = doc.defaultView.NativeWindow;
|
||||
contextId = nativeWindow.contextmenus.add(
|
||||
this.menuLabel,
|
||||
nativeWindow.contextmenus.linkOpenableContext,
|
||||
this.onCommand
|
||||
);*/
|
||||
return;
|
||||
}
|
||||
|
||||
// Already installed?
|
||||
var doc = window.document;
|
||||
if ( doc.getElementById(this.menuItemId) !== null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var contextMenu = doc.getElementById('contentAreaContextMenu');
|
||||
|
||||
// This can happen (Thunderbird).
|
||||
if ( contextMenu === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var menuitem = doc.createElement('menuitem');
|
||||
menuitem.setAttribute('id', this.menuItemId);
|
||||
menuitem.setAttribute('label', this.menuLabel);
|
||||
menuitem.setAttribute('image', vAPI.getURL('img/browsericons/icon16.svg'));
|
||||
menuitem.setAttribute('class', 'menuitem-iconic');
|
||||
menuitem.addEventListener('command', this.onCommand);
|
||||
contextMenu.addEventListener('popupshowing', this.displayMenuItem);
|
||||
contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator'));
|
||||
};
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/906
|
||||
// Be sure document.readyState is 'complete': it could happen at launch
|
||||
// time that we are called by vAPI.contextMenu.create() directly before
|
||||
// the environment is properly initialized.
|
||||
var canRegister = function(win) {
|
||||
return win && win.document.readyState === 'complete';
|
||||
};
|
||||
|
||||
return function(win) {
|
||||
deferUntil(
|
||||
canRegister.bind(null, win),
|
||||
register.bind(this, win)
|
||||
);
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.contextMenu.unregister = function(win) {
|
||||
if ( !this.menuItemId ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( vAPI.fennec ) {
|
||||
// TODO
|
||||
return;
|
||||
}
|
||||
|
||||
var doc = win.document;
|
||||
var menuitem = doc.getElementById(this.menuItemId);
|
||||
|
||||
// Not guarantee the menu item was actually registered.
|
||||
if ( menuitem === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var contextMenu = menuitem.parentNode;
|
||||
menuitem.removeEventListener('command', this.onCommand);
|
||||
contextMenu.removeEventListener('popupshowing', this.displayMenuItem);
|
||||
contextMenu.removeChild(menuitem);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.contextMenu.create = function(details, callback) {
|
||||
this.menuItemId = details.id;
|
||||
this.menuLabel = details.title;
|
||||
this.contexts = details.contexts;
|
||||
|
||||
if ( Array.isArray(this.contexts) && this.contexts.length ) {
|
||||
this.contexts = this.contexts.indexOf('all') === -1 ? this.contexts : null;
|
||||
} else {
|
||||
// default in Chrome
|
||||
this.contexts = ['page'];
|
||||
}
|
||||
|
||||
this.onCommand = function() {
|
||||
var onCommand = function() {
|
||||
var gContextMenu = getOwnerWindow(this).gContextMenu;
|
||||
var details = {
|
||||
menuItemId: this.id
|
||||
@ -3361,29 +3215,185 @@ vAPI.contextMenu.create = function(details, callback) {
|
||||
details.linkUrl = gContextMenu.linkURL;
|
||||
}
|
||||
|
||||
callback(details, {
|
||||
clientCallback(details, {
|
||||
id: tabWatcher.tabIdFromTarget(gContextMenu.browser),
|
||||
url: gContextMenu.browser.currentURI.asciiSpec
|
||||
});
|
||||
};
|
||||
|
||||
for ( var win of winWatcher.getWindows() ) {
|
||||
this.register(win);
|
||||
}
|
||||
};
|
||||
var menuItemMatchesContext = function(contextMenu, clientEntry) {
|
||||
if ( !clientEntry.contexts ) {
|
||||
return false;
|
||||
}
|
||||
for ( var context of clientEntry.contexts ) {
|
||||
if ( context === 'all' ) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
contextMap.hasOwnProperty(context) &&
|
||||
contextMenu[contextMap[context]]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
var onMenuShowing = function({target}) {
|
||||
var doc = target.ownerDocument;
|
||||
var gContextMenu = doc.defaultView.gContextMenu;
|
||||
if ( !gContextMenu.browser ) {
|
||||
return;
|
||||
}
|
||||
|
||||
vAPI.contextMenu.remove = function() {
|
||||
for ( var win of winWatcher.getWindows() ) {
|
||||
this.unregister(win);
|
||||
}
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/105
|
||||
// TODO: Should the element picker works on any kind of pages?
|
||||
var currentURI = gContextMenu.browser.currentURI,
|
||||
isHTTP = currentURI.schemeIs('http') || currentURI.schemeIs('https'),
|
||||
layoutChanged = false,
|
||||
contextMenu = doc.getElementById('contentAreaContextMenu'),
|
||||
newEntries = clientEntries,
|
||||
oldMenuitems = contextMenu.querySelectorAll('[data-uBlock0="menuitem"]'),
|
||||
newMenuitems = [],
|
||||
n = Math.max(clientEntries.length, oldMenuitems.length),
|
||||
menuitem, newEntry;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
menuitem = oldMenuitems[i];
|
||||
newEntry = newEntries[i];
|
||||
if ( menuitem && !newEntry ) {
|
||||
menuitem.parentNode.removeChild(menuitem);
|
||||
menuitem.removeEventListener('command', onCommand);
|
||||
menuitem = null;
|
||||
layoutChanged = true;
|
||||
} else if ( !menuitem && newEntry ) {
|
||||
menuitem = doc.createElement('menuitem');
|
||||
menuitem.setAttribute('data-uBlock0', 'menuitem');
|
||||
menuitem.addEventListener('command', onCommand);
|
||||
}
|
||||
if ( !menuitem ) {
|
||||
continue;
|
||||
}
|
||||
if ( menuitem.id !== newEntry.id ) {
|
||||
menuitem.setAttribute('id', newEntry.id);
|
||||
menuitem.setAttribute('label', newEntry.title);
|
||||
layoutChanged = true;
|
||||
}
|
||||
menuitem.setAttribute('hidden', !isHTTP || !menuItemMatchesContext(gContextMenu, newEntry));
|
||||
newMenuitems.push(menuitem);
|
||||
}
|
||||
// No changes?
|
||||
if ( layoutChanged === false ) {
|
||||
return;
|
||||
}
|
||||
// No entry: remove submenu if present.
|
||||
var menu = contextMenu.querySelector('[data-uBlock0="menu"]');
|
||||
if ( newMenuitems.length === 0 ) {
|
||||
if ( menu !== null ) {
|
||||
menu.parentNode.removeChild(menuitem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Only one entry: no need for a submenu.
|
||||
if ( newMenuitems.length === 1 ) {
|
||||
if ( menu !== null ) {
|
||||
menu.parentNode.removeChild(menu);
|
||||
}
|
||||
menuitem = newMenuitems[0];
|
||||
menuitem.setAttribute('class', 'menuitem-iconic');
|
||||
menuitem.setAttribute('image', vAPI.getURL('img/browsericons/icon16.svg'));
|
||||
contextMenu.insertBefore(menuitem, doc.getElementById('inspect-separator'));
|
||||
return;
|
||||
}
|
||||
// More than one entry: we need a submenu.
|
||||
if ( menu === null ) {
|
||||
menu = doc.createElement('menu');
|
||||
menu.setAttribute('label', vAPI.app.name);
|
||||
menu.setAttribute('data-uBlock0', 'menu');
|
||||
menu.setAttribute('class', 'menu-iconic');
|
||||
menu.setAttribute('image', vAPI.getURL('img/browsericons/icon16.svg'));
|
||||
contextMenu.insertBefore(menu, doc.getElementById('inspect-separator'));
|
||||
}
|
||||
var menupopup = contextMenu.querySelector('[data-uBlock0="menupopup"]');
|
||||
if ( menupopup === null ) {
|
||||
menupopup = doc.createElement('menupopup');
|
||||
menupopup.setAttribute('data-uBlock0', 'menupopup');
|
||||
menu.appendChild(menupopup);
|
||||
}
|
||||
for ( i = 0; i < newMenuitems.length; i++ ) {
|
||||
menuitem = newMenuitems[i];
|
||||
menuitem.setAttribute('class', 'menuitem-non-iconic');
|
||||
menuitem.removeAttribute('image');
|
||||
menupopup.appendChild(menuitem);
|
||||
}
|
||||
};
|
||||
|
||||
this.menuItemId = null;
|
||||
this.menuLabel = null;
|
||||
this.contexts = null;
|
||||
this.onCommand = null;
|
||||
};
|
||||
// https://github.com/gorhill/uBlock/issues/906
|
||||
// Be sure document.readyState is 'complete': it could happen at launch
|
||||
// time that we are called by vAPI.contextMenu.create() directly before
|
||||
// the environment is properly initialized.
|
||||
var canRegister = function(win) {
|
||||
return win && win.document.readyState === 'complete';
|
||||
};
|
||||
|
||||
var register = function(window) {
|
||||
if ( canRegister(window) !== true ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var contextMenu = window.document.getElementById('contentAreaContextMenu');
|
||||
if ( contextMenu === null ) {
|
||||
return;
|
||||
}
|
||||
contextMenu.addEventListener('popupshowing', onMenuShowing);
|
||||
};
|
||||
|
||||
var registerAsync = function(win) {
|
||||
// TODO https://developer.mozilla.org/en-US/Add-ons/Firefox_for_Android/API/NativeWindow/contextmenus/add
|
||||
// var nativeWindow = doc.defaultView.NativeWindow;
|
||||
// contextId = nativeWindow.contextmenus.add(
|
||||
// this.menuLabel,
|
||||
// nativeWindow.contextmenus.linkOpenableContext,
|
||||
// this.onCommand
|
||||
// );
|
||||
if ( vAPI.fennec ) {
|
||||
return;
|
||||
}
|
||||
deferUntil(
|
||||
canRegister.bind(null, win),
|
||||
register.bind(null, win)
|
||||
);
|
||||
};
|
||||
|
||||
var unregister = function(win) {
|
||||
// TODO
|
||||
if ( vAPI.fennec ) {
|
||||
return;
|
||||
}
|
||||
var contextMenu = win.document.getElementById('contentAreaContextMenu');
|
||||
if ( contextMenu !== null ) {
|
||||
contextMenu.removeEventListener('popupshowing', onMenuShowing);
|
||||
}
|
||||
var menuitems = win.document.querySelectorAll('[data-uBlock0]'),
|
||||
menuitem;
|
||||
for ( var i = 0; i < menuitems.length; i++ ) {
|
||||
menuitem = menuitems[i];
|
||||
menuitem.parentNode.removeChild(menuitem);
|
||||
menuitem.removeEventListener('command', onCommand);
|
||||
}
|
||||
};
|
||||
|
||||
var setEntries = function(entries, callback) {
|
||||
clientEntries = entries || [];
|
||||
clientCallback = callback || null;
|
||||
};
|
||||
|
||||
return {
|
||||
onMustUpdate: function() {},
|
||||
register: registerAsync,
|
||||
unregister: unregister,
|
||||
setEntries: setEntries
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
@ -19,7 +19,9 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global addMessageListener, removeMessageListener, sendAsyncMessage, outerShutdown */
|
||||
/* global HTMLDocument, XMLDocument,
|
||||
addMessageListener, removeMessageListener, sendAsyncMessage, outerShutdown
|
||||
*/
|
||||
|
||||
// For non background pages
|
||||
|
||||
@ -29,6 +31,18 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Not all sandboxes are given an rpc function, so assign a dummy one if it is
|
||||
|
@ -77,19 +77,19 @@
|
||||
},
|
||||
"popupTipNoPopups":{
|
||||
"message":"Toggle the blocking of all popups for this site",
|
||||
"description":"English: Toggle the blocking of all popups for this site"
|
||||
"description":"Tooltip for the no-popups per-site switch"
|
||||
},
|
||||
"popupTipNoStrictBlocking":{
|
||||
"message":"Toggle strict blocking for this site",
|
||||
"description":"English: Toggle strict blocking for this site"
|
||||
"popupTipNoLargeMedia":{
|
||||
"message":"Toggle the blocking of large media elements for this site",
|
||||
"description":"Tooltip for the no-large-media per-site switch"
|
||||
},
|
||||
"popupTipNoCosmeticFiltering":{
|
||||
"message":"Toggle cosmetic filtering for this site",
|
||||
"description":"English: Toggle cosmetic filtering for this site"
|
||||
"description":"Tooltip for the no-cosmetic-filtering per-site switch"
|
||||
},
|
||||
"popupTipNoRemoteFonts":{
|
||||
"message":"Toggle the blocking of remote fonts for this site",
|
||||
"description":"English: Toggle the blocking of remote fonts for this site"
|
||||
"description":"Tooltip for the no-remote-fonts per-site switch"
|
||||
},
|
||||
"popupTipGlobalRules":{
|
||||
"message":"Global rules: this column is for rules which apply to all sites.",
|
||||
@ -215,9 +215,25 @@
|
||||
"message":"Prevent WebRTC from leaking local IP addresses",
|
||||
"description":"English: "
|
||||
},
|
||||
"settingsExperimentalPrompt":{
|
||||
"message":"Enable experimental features (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Experimental-features'>About<\/a>)",
|
||||
"description":"English: Enable experimental features"
|
||||
"settingPerSiteSwitchGroup":{
|
||||
"message":"Default behavior",
|
||||
"description": ""
|
||||
},
|
||||
"settingPerSiteSwitchGroupSynopsis":{
|
||||
"message":"These default behaviors can be overriden on a per-site basis",
|
||||
"description": ""
|
||||
},
|
||||
"settingsNoCosmeticFilteringPrompt":{
|
||||
"message":"Disable cosmetic filtering",
|
||||
"description": ""
|
||||
},
|
||||
"settingsNoLargeMediaPrompt":{
|
||||
"message":"Block media elements larger than {{input:number}} kB",
|
||||
"description": ""
|
||||
},
|
||||
"settingsNoRemoteFontsPrompt":{
|
||||
"message":"Block remote fonts",
|
||||
"description": ""
|
||||
},
|
||||
"settingsStorageUsed":{
|
||||
"message":"Storage used: {{value}} bytes",
|
||||
@ -233,23 +249,23 @@
|
||||
},
|
||||
"3pListsOfBlockedHostsPrompt":{
|
||||
"message":"{{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:",
|
||||
"description":"English: {{netFilterCount}} network filters + {{cosmeticFilterCount}} cosmetic filters from:"
|
||||
"description":"Appears at the top of the _3rd-party filters_ pane"
|
||||
},
|
||||
"3pListsOfBlockedHostsPerListStats":{
|
||||
"message":"{{used}} used out of {{total}}",
|
||||
"description":"English: {{used}} used out of {{total}}"
|
||||
"description":"Appears aside each filter list in the _3rd-party filters_ pane"
|
||||
},
|
||||
"3pAutoUpdatePrompt1":{
|
||||
"message":"Auto-update filter lists.",
|
||||
"description":"English: Auto-update filter lists."
|
||||
"description":"A checkbox in the _3rd-party filters_ pane"
|
||||
},
|
||||
"3pUpdateNow":{
|
||||
"message":"Update now",
|
||||
"description":"English: Update now"
|
||||
"description":"A button in the in the _3rd-party filters_ pane"
|
||||
},
|
||||
"3pPurgeAll":{
|
||||
"message":"Purge all caches",
|
||||
"description":"English: Purge all caches"
|
||||
"description":"A button in the in the _3rd-party filters_ pane"
|
||||
},
|
||||
"3pParseAllABPHideFiltersPrompt1":{
|
||||
"message":"Parse and enforce cosmetic filters.",
|
||||
@ -651,6 +667,10 @@
|
||||
"message": "bytes",
|
||||
"description": ""
|
||||
},
|
||||
"contextMenuTemporarilyAllowLargeMediaElements": {
|
||||
"message": "Temporarily allow large media elements",
|
||||
"description": "A context menu entry, present when large media elements have been blocked on the current site"
|
||||
},
|
||||
"dummy":{
|
||||
"message":"This entry must be the last one",
|
||||
"description":"so we dont need to deal with comma for last entry"
|
||||
|
@ -34,10 +34,17 @@ div > p:first-child {
|
||||
div > p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
input[type="number"] {
|
||||
width: 5em;
|
||||
}
|
||||
|
||||
.para {
|
||||
width: 40em;
|
||||
}
|
||||
.synopsis {
|
||||
font-size: small;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.whatisthis {
|
||||
margin: 0 0 0 8px;
|
||||
border: 0;
|
||||
|
@ -176,7 +176,7 @@ body.off #switch .fa {
|
||||
#extraTools > span {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
margin: 0 0.4em;
|
||||
margin: 0 0.5em;
|
||||
position: relative;
|
||||
}
|
||||
#extraTools > span > span.badge {
|
||||
|
@ -61,6 +61,7 @@ return {
|
||||
externalLists: defaultExternalLists,
|
||||
firewallPaneMinimized: true,
|
||||
hyperlinkAuditingDisabled: true,
|
||||
largeMediaSize: 50,
|
||||
parseAllABPHideFilters: true,
|
||||
prefetchingDisabled: true,
|
||||
requestLogMaxEntries: 1000,
|
||||
|
@ -19,8 +19,6 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Injected into content pages
|
||||
@ -31,18 +29,6 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// I've seen this happens on Firefox
|
||||
if ( window.location === null ) {
|
||||
return;
|
||||
|
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
/* jshint multistr: true */
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@ -34,18 +33,6 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
//console.debug('contentscript-start.js > vAPI not found');
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*******************************************************************************
|
||||
|
||||
µBlock - a browser extension to block requests.
|
||||
Copyright (C) 2014 Raymond Hill
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2014-2015 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
|
||||
@ -19,26 +19,19 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, µBlock */
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.contextMenu = (function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// New namespace
|
||||
|
||||
µBlock.contextMenu = (function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
var enabled = false;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onContextMenuClicked = function(details, tab) {
|
||||
if ( details.menuItemId !== 'blockElement' ) {
|
||||
return;
|
||||
}
|
||||
var onBlockElement = function(details, tab) {
|
||||
if ( tab === undefined ) {
|
||||
return;
|
||||
}
|
||||
@ -69,29 +62,90 @@ var onContextMenuClicked = function(details, tab) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleMenu = function(on) {
|
||||
// This needs to be local scope: we can't reuse it for more than one
|
||||
// menu creation call.
|
||||
var menuCreateDetails = {
|
||||
id: 'blockElement',
|
||||
title: vAPI.i18n('pickerContextMenuEntry'),
|
||||
contexts: ['page', 'editable', 'frame', 'link', 'image', 'video'],
|
||||
documentUrlPatterns: ['https://*/*', 'http://*/*']
|
||||
};
|
||||
var onTemporarilyAllowLargeMediaElements = function(details, tab) {
|
||||
if ( tab === undefined ) {
|
||||
return;
|
||||
}
|
||||
var pageStore = µb.pageStoreFromTabId(tab.id);
|
||||
if ( pageStore === null ) {
|
||||
return;
|
||||
}
|
||||
pageStore.temporarilyAllowLargeMediaElements();
|
||||
};
|
||||
|
||||
if ( on === true && enabled === false ) {
|
||||
vAPI.contextMenu.create(menuCreateDetails, onContextMenuClicked);
|
||||
enabled = true;
|
||||
} else if ( on !== true && enabled === true ) {
|
||||
vAPI.contextMenu.remove();
|
||||
enabled = false;
|
||||
/******************************************************************************/
|
||||
|
||||
var onEntryClicked = function(details, tab) {
|
||||
if ( details.menuItemId === 'uBlock0-blockElement' ) {
|
||||
return onBlockElement(details, tab);
|
||||
}
|
||||
if ( details.menuItemId === 'uBlock0-temporarilyAllowLargeMediaElements' ) {
|
||||
return onTemporarilyAllowLargeMediaElements(details, tab);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var menuEntries = [
|
||||
{
|
||||
id: 'uBlock0-blockElement',
|
||||
title: vAPI.i18n('pickerContextMenuEntry'),
|
||||
contexts: ['all'],
|
||||
documentUrlPatterns: ['https://*/*', 'http://*/*']
|
||||
},
|
||||
{
|
||||
id: 'uBlock0-temporarilyAllowLargeMediaElements',
|
||||
title: vAPI.i18n('contextMenuTemporarilyAllowLargeMediaElements'),
|
||||
contexts: ['all'],
|
||||
documentUrlPatterns: ['https://*/*', 'http://*/*']
|
||||
}
|
||||
];
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var update = function(tabId) {
|
||||
var newBits = 0;
|
||||
if ( µb.userSettings.contextMenuEnabled && tabId !== null ) {
|
||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||
if ( pageStore ) {
|
||||
newBits |= 0x01;
|
||||
if ( pageStore.largeMediaCount !== 0 ) {
|
||||
newBits |= 0x02;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( newBits === currentBits ) {
|
||||
return;
|
||||
}
|
||||
currentBits = newBits;
|
||||
var usedEntries = [];
|
||||
if ( newBits & 0x01 ) {
|
||||
usedEntries.push(menuEntries[0]);
|
||||
}
|
||||
if ( newBits & 0x02 ) {
|
||||
usedEntries.push(menuEntries[1]);
|
||||
}
|
||||
vAPI.contextMenu.setEntries(usedEntries, onEntryClicked);
|
||||
};
|
||||
|
||||
var currentBits = 0;
|
||||
|
||||
vAPI.contextMenu.onMustUpdate = update;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
return {
|
||||
toggle: toggleMenu
|
||||
update: function(tabId) {
|
||||
if ( µb.userSettings.contextMenuEnabled && tabId === undefined ) {
|
||||
vAPI.tabs.get(null, function(tab) {
|
||||
if ( tab ) {
|
||||
update(tab.id);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
update(tabId);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -40,7 +40,8 @@ var switchBitOffsets = {
|
||||
'no-strict-blocking': 0,
|
||||
'no-popups': 2,
|
||||
'no-cosmetic-filtering': 4,
|
||||
'no-remote-fonts': 6
|
||||
'no-remote-fonts': 6,
|
||||
'no-large-media': 8
|
||||
};
|
||||
|
||||
var fromLegacySwitchNames = {
|
||||
|
@ -128,10 +128,6 @@ var onMessage = function(request, sender, callback) {
|
||||
response = getDomainNames(request.targets);
|
||||
break;
|
||||
|
||||
case 'getUserSettings':
|
||||
response = µb.userSettings;
|
||||
break;
|
||||
|
||||
case 'launchElementPicker':
|
||||
// Launched from some auxiliary pages, clear context menu coords.
|
||||
µb.mouseX = µb.mouseY = -1;
|
||||
@ -277,6 +273,7 @@ var getFirewallRules = function(srcHostname, desHostnames) {
|
||||
|
||||
var popupDataFromTabId = function(tabId, tabTitle) {
|
||||
var tabContext = µb.tabContextManager.mustLookup(tabId);
|
||||
var rootHostname = tabContext.rootHostname;
|
||||
var r = {
|
||||
advancedUserEnabled: µb.userSettings.advancedUserEnabled,
|
||||
appName: vAPI.app.name,
|
||||
@ -290,7 +287,7 @@ var popupDataFromTabId = function(tabId, tabTitle) {
|
||||
netFilteringSwitch: false,
|
||||
rawURL: tabContext.rawURL,
|
||||
pageURL: tabContext.normalURL,
|
||||
pageHostname: tabContext.rootHostname,
|
||||
pageHostname: rootHostname,
|
||||
pageDomain: tabContext.rootDomain,
|
||||
pageAllowedRequestCount: 0,
|
||||
pageBlockedRequestCount: 0,
|
||||
@ -307,21 +304,22 @@ var popupDataFromTabId = function(tabId, tabTitle) {
|
||||
r.netFilteringSwitch = pageStore.getNetFilteringSwitch();
|
||||
r.hostnameDict = getHostnameDict(pageStore.hostnameToCountMap);
|
||||
r.contentLastModified = pageStore.contentLastModified;
|
||||
r.firewallRules = getFirewallRules(tabContext.rootHostname, r.hostnameDict);
|
||||
r.canElementPicker = tabContext.rootHostname.indexOf('.') !== -1;
|
||||
r.noPopups = µb.hnSwitches.evaluateZ('no-popups', tabContext.rootHostname);
|
||||
r.noStrictBlocking = µb.hnSwitches.evaluateZ('no-strict-blocking', tabContext.rootHostname);
|
||||
r.noCosmeticFiltering = µb.hnSwitches.evaluateZ('no-cosmetic-filtering', tabContext.rootHostname);
|
||||
r.noRemoteFonts = µb.hnSwitches.evaluateZ('no-remote-fonts', tabContext.rootHostname);
|
||||
r.remoteFontCount = pageStore.remoteFontCount;
|
||||
r.firewallRules = getFirewallRules(rootHostname, r.hostnameDict);
|
||||
r.canElementPicker = rootHostname.indexOf('.') !== -1;
|
||||
r.noPopups = µb.hnSwitches.evaluateZ('no-popups', rootHostname);
|
||||
r.popupBlockedCount = pageStore.popupBlockedCount;
|
||||
r.noCosmeticFiltering = µb.hnSwitches.evaluateZ('no-cosmetic-filtering', rootHostname);
|
||||
r.noLargeMedia = µb.hnSwitches.evaluateZ('no-large-media', rootHostname);
|
||||
r.largeMediaCount = pageStore.largeMediaCount;
|
||||
r.noRemoteFonts = µb.hnSwitches.evaluateZ('no-remote-fonts', rootHostname);
|
||||
r.remoteFontCount = pageStore.remoteFontCount;
|
||||
} else {
|
||||
r.hostnameDict = {};
|
||||
r.firewallRules = getFirewallRules();
|
||||
}
|
||||
r.matrixIsDirty = !µb.sessionFirewall.hasSameRules(
|
||||
µb.permanentFirewall,
|
||||
tabContext.rootHostname,
|
||||
rootHostname,
|
||||
r.hostnameDict
|
||||
);
|
||||
return r;
|
||||
@ -1342,48 +1340,6 @@ vAPI.messaging.listen('logger-ui.js', onMessage);
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// subscriber.js
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Sync
|
||||
var response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'subscriberData':
|
||||
response = {
|
||||
confirmStr: vAPI.i18n('subscriberConfirm'),
|
||||
externalLists: µBlock.userSettings.externalLists
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
return vAPI.messaging.UNHANDLED;
|
||||
}
|
||||
|
||||
callback(response);
|
||||
};
|
||||
|
||||
vAPI.messaging.listen('subscriber.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// document-blocked.js
|
||||
|
||||
(function() {
|
||||
@ -1461,6 +1417,7 @@ var logCosmeticFilters = function(tabId, details) {
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
var tabId = sender && sender.tab ? sender.tab.id : 0;
|
||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
@ -1473,8 +1430,7 @@ var onMessage = function(request, sender, callback) {
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'liveCosmeticFilteringData':
|
||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||
if ( pageStore ) {
|
||||
if ( pageStore !== null ) {
|
||||
pageStore.hiddenElementCount = request.filteredElementCount;
|
||||
}
|
||||
break;
|
||||
@ -1483,6 +1439,19 @@ var onMessage = function(request, sender, callback) {
|
||||
logCosmeticFilters(tabId, request);
|
||||
break;
|
||||
|
||||
case 'temporarilyAllowLargeMediaElement':
|
||||
if ( pageStore !== null ) {
|
||||
pageStore.allowLargeMediaElementsUntil = Date.now() + 2000;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'subscriberData':
|
||||
response = {
|
||||
confirmStr: vAPI.i18n('subscriberConfirm'),
|
||||
externalLists: µBlock.userSettings.externalLists
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
return vAPI.messaging.UNHANDLED;
|
||||
}
|
||||
|
@ -294,6 +294,16 @@ PageStore.factory = function(tabId) {
|
||||
PageStore.prototype.init = function(tabId) {
|
||||
var tabContext = µb.tabContextManager.mustLookup(tabId);
|
||||
this.tabId = tabId;
|
||||
|
||||
// If we are navigating from-to same site, remember whether large
|
||||
// media elements were temporarily allowed.
|
||||
if (
|
||||
typeof this.allowLargeMediaElementsUntil !== 'number' ||
|
||||
tabContext.rootHostname !== this.tabHostname
|
||||
) {
|
||||
this.allowLargeMediaElementsUntil = 0;
|
||||
}
|
||||
|
||||
this.tabHostname = tabContext.rootHostname;
|
||||
this.title = tabContext.rawURL;
|
||||
this.rawURL = tabContext.rawURL;
|
||||
@ -305,6 +315,8 @@ PageStore.prototype.init = function(tabId) {
|
||||
this.hiddenElementCount = ''; // Empty string means "unknown"
|
||||
this.remoteFontCount = 0;
|
||||
this.popupBlockedCount = 0;
|
||||
this.largeMediaCount = 0;
|
||||
this.largeMediaTimer = null;
|
||||
this.netFilteringCache = NetFilteringResultCache.factory();
|
||||
|
||||
// Support `elemhide` filter option. Called at this point so the required
|
||||
@ -354,6 +366,10 @@ PageStore.prototype.reuse = function(context) {
|
||||
}
|
||||
|
||||
// A new page is completely reloaded from scratch, reset all.
|
||||
if ( this.largeMediaTimer !== null ) {
|
||||
clearTimeout(this.largeMediaTimer);
|
||||
this.largeMediaTimer = null;
|
||||
}
|
||||
this.disposeFrameStores();
|
||||
this.netFilteringCache = this.netFilteringCache.dispose();
|
||||
this.init(this.tabId);
|
||||
@ -373,6 +389,11 @@ PageStore.prototype.dispose = function() {
|
||||
this.title = '';
|
||||
this.rawURL = '';
|
||||
this.hostnameToCountMap = null;
|
||||
this.allowLargeMediaElementsUntil = 0;
|
||||
if ( this.largeMediaTimer !== null ) {
|
||||
clearTimeout(this.largeMediaTimer);
|
||||
this.largeMediaTimer = null;
|
||||
}
|
||||
this.disposeFrameStores();
|
||||
this.netFilteringCache = this.netFilteringCache.dispose();
|
||||
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
|
||||
@ -469,6 +490,32 @@ PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PageStore.prototype.logLargeMedia = (function() {
|
||||
var injectScript = function() {
|
||||
this.largeMediaTimer = null;
|
||||
µb.scriptlets.injectDeep(
|
||||
this.tabId,
|
||||
'load-large-media-interactive'
|
||||
);
|
||||
µb.contextMenu.update(this.tabId);
|
||||
};
|
||||
return function() {
|
||||
this.largeMediaCount += 1;
|
||||
if ( this.largeMediaTimer === null ) {
|
||||
this.largeMediaTimer = vAPI.setTimeout(injectScript.bind(this), 500);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
PageStore.prototype.temporarilyAllowLargeMediaElements = function() {
|
||||
this.largeMediaCount = 0;
|
||||
µb.contextMenu.update(this.tabId);
|
||||
this.allowLargeMediaElementsUntil = Date.now() + 86400000;
|
||||
µb.scriptlets.injectDeep(this.tabId, 'load-large-media-all');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PageStore.prototype.filterRequest = function(context) {
|
||||
var requestType = context.requestType;
|
||||
|
||||
|
@ -160,6 +160,7 @@ var hashFromPopupData = function(reset) {
|
||||
}
|
||||
hasher.sort();
|
||||
hasher.push(uDom('body').hasClass('off'));
|
||||
hasher.push(uDom.nodeFromId('no-large-media').classList.contains('on'));
|
||||
hasher.push(uDom.nodeFromId('no-cosmetic-filtering').classList.contains('on'));
|
||||
hasher.push(uDom.nodeFromId('no-remote-fonts').classList.contains('on'));
|
||||
|
||||
@ -459,7 +460,7 @@ var renderPopup = function() {
|
||||
|
||||
// Extra tools
|
||||
uDom.nodeFromId('no-popups').classList.toggle('on', popupData.noPopups === true);
|
||||
uDom.nodeFromId('no-strict-blocking').classList.toggle('on', popupData.noStrictBlocking === true);
|
||||
uDom.nodeFromId('no-large-media').classList.toggle('on', popupData.noLargeMedia === true);
|
||||
uDom.nodeFromId('no-cosmetic-filtering').classList.toggle('on', popupData.noCosmeticFiltering === true);
|
||||
uDom.nodeFromId('no-remote-fonts').classList.toggle('on', popupData.noRemoteFonts === true);
|
||||
|
||||
@ -468,6 +469,11 @@ var renderPopup = function() {
|
||||
uDom.nodeFromSelector('#no-popups > span.badge')
|
||||
.textContent = total ? total.toLocaleString() : '';
|
||||
|
||||
// Report large media count on badge
|
||||
total = popupData.largeMediaCount;
|
||||
uDom.nodeFromSelector('#no-large-media > span.badge')
|
||||
.textContent = total ? total.toLocaleString() : '';
|
||||
|
||||
// Report remote font count on badge
|
||||
total = popupData.remoteFontCount;
|
||||
uDom.nodeFromSelector('#no-remote-fonts > span.badge')
|
||||
@ -733,18 +739,18 @@ var revertFirewallRules = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleHostnameSwitch = function() {
|
||||
var elem = uDom(this);
|
||||
var switchName = elem.attr('id');
|
||||
var toggleHostnameSwitch = function(ev) {
|
||||
var target = ev.currentTarget;
|
||||
var switchName = target.getAttribute('id');
|
||||
if ( !switchName ) {
|
||||
return;
|
||||
}
|
||||
elem.toggleClass('on');
|
||||
target.classList.toggle('on');
|
||||
messager.send({
|
||||
what: 'toggleHostnameSwitch',
|
||||
name: switchName,
|
||||
hostname: popupData.pageHostname,
|
||||
state: elem.hasClass('on'),
|
||||
state: target.classList.contains('on'),
|
||||
tabId: popupData.tabId
|
||||
});
|
||||
hashFromPopupData();
|
||||
|
@ -19,8 +19,6 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
@ -29,19 +27,6 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
@ -19,8 +19,6 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
@ -29,21 +27,7 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
//console.debug('cosmetic-off.js > no vAPI');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
@ -29,21 +27,7 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
//console.debug('cosmetic-on.js > no vAPI');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
@ -29,21 +27,7 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
//console.debug('cosmetic-survey.js > vAPI not found');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,6 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global vAPI, HTMLDocument, XMLDocument */
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
@ -30,19 +28,6 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
Home: https://github.com/gorhill/uBlock
|
||||
*/
|
||||
|
||||
/* global self, vAPI, CSS, HTMLDocument, XMLDocument */
|
||||
/* global CSS */
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
@ -66,16 +66,16 @@
|
||||
if (
|
||||
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
|
||||
// U+007F, […]
|
||||
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
|
||||
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit === 0x007F ||
|
||||
// If the character is the first character and is in the range [0-9]
|
||||
// (U+0030 to U+0039), […]
|
||||
(index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
|
||||
// If the character is the second character and is in the range [0-9]
|
||||
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
|
||||
(
|
||||
index == 1 &&
|
||||
index === 1 &&
|
||||
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
|
||||
firstCodeUnit == 0x002D
|
||||
firstCodeUnit === 0x002D
|
||||
)
|
||||
) {
|
||||
// http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
|
||||
@ -89,8 +89,8 @@
|
||||
// U+005A), or [a-z] (U+0061 to U+007A), […]
|
||||
if (
|
||||
codeUnit >= 0x0080 ||
|
||||
codeUnit == 0x002D ||
|
||||
codeUnit == 0x005F ||
|
||||
codeUnit === 0x002D ||
|
||||
codeUnit === 0x005F ||
|
||||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
|
||||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
|
||||
codeUnit >= 0x0061 && codeUnit <= 0x007A
|
||||
@ -120,20 +120,10 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
// https://github.com/chrisaljoudi/uBlock/issues/1528
|
||||
// A XMLDocument can be a valid HTML document.
|
||||
if (
|
||||
document instanceof XMLDocument === false ||
|
||||
document.createElement('div') instanceof HTMLDivElement === false
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if ( typeof vAPI !== 'object' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// don't run in frames
|
||||
if ( window.top !== window ) {
|
||||
return;
|
||||
|
69
src/js/scriptlets/load-large-media-all.js
Normal file
69
src/js/scriptlets/load-large-media-all.js
Normal file
@ -0,0 +1,69 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015 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
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(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();
|
||||
}
|
||||
}
|
||||
|
||||
// <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 (
|
||||
typeof elem.naturalWidth !== 'number' ||
|
||||
elem.naturalWidth === 0 ||
|
||||
typeof elem.naturalHeight !== 'number' ||
|
||||
elem.naturalHeight === 0
|
||||
) {
|
||||
src = elem.getAttribute('src') || '';
|
||||
elem.removeAttribute('src');
|
||||
elem.setAttribute('src', src);
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
222
src/js/scriptlets/load-large-media-interactive.js
Normal file
222
src/js/scriptlets/load-large-media-interactive.js
Normal file
@ -0,0 +1,222 @@
|
||||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
Copyright (C) 2015 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
|
||||
*/
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This can happen
|
||||
if ( typeof vAPI !== 'object' || vAPI.loadLargeMediaInteractive === true ) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var largeMediaElementAttribute = 'data-' + vAPI.sessionId;
|
||||
var largeMediaElementSelector =
|
||||
':root audio[' + largeMediaElementAttribute + '],\n' +
|
||||
':root img[' + largeMediaElementAttribute + '],\n' +
|
||||
':root video[' + largeMediaElementAttribute + ']';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var mediaNotLoaded = function(elem) {
|
||||
var src = elem.getAttribute('src') || '';
|
||||
if ( src === '' ) {
|
||||
return false;
|
||||
}
|
||||
switch ( elem.localName ) {
|
||||
case 'audio':
|
||||
case 'video':
|
||||
return elem.error !== null;
|
||||
case 'img':
|
||||
return elem.offsetWidth !== 0 && elem.offsetHeight !== 0 &&
|
||||
(elem.naturalWidth === 0 || elem.naturalHeight === 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
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement
|
||||
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
return largeMediaElementCount;
|
||||
};
|
||||
|
||||
if ( surveyMissingMediaElements() === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
vAPI.loadLargeMediaInteractive = true;
|
||||
|
||||
// Insert custom style tag.
|
||||
var 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;',
|
||||
'}'
|
||||
].join('\n');
|
||||
document.head.appendChild(styleTag);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('scriptlets');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var stayOrLeave = (function() {
|
||||
var timer = null;
|
||||
|
||||
var timeoutHandler = function(leaveNow) {
|
||||
timer = null;
|
||||
if ( leaveNow !== true ) {
|
||||
if (
|
||||
document.querySelector(largeMediaElementSelector) !== null ||
|
||||
surveyMissingMediaElements() !== 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Leave
|
||||
if ( styleTag !== null ) {
|
||||
styleTag.parentNode.removeChild(styleTag);
|
||||
styleTag = null;
|
||||
}
|
||||
vAPI.loadLargeMediaInteractive = false;
|
||||
document.removeEventListener('error', onLoadError, true);
|
||||
document.removeEventListener('click', onMouseClick, true);
|
||||
if ( messager !== null ) {
|
||||
messager.close();
|
||||
messager = null;
|
||||
}
|
||||
};
|
||||
|
||||
return function(leaveNow) {
|
||||
if ( timer !== null ) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
if ( leaveNow ) {
|
||||
timeoutHandler(true);
|
||||
} else {
|
||||
timer = vAPI.setTimeout(timeoutHandler, 5000);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMouseClick = function(ev) {
|
||||
if ( ev.button !== 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var elem = ev.target;
|
||||
if ( elem.matches(largeMediaElementSelector) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mediaNotLoaded(elem) === false ) {
|
||||
elem.removeAttribute(largeMediaElementAttribute);
|
||||
stayOrLeave();
|
||||
return;
|
||||
}
|
||||
|
||||
var src = elem.getAttribute('src');
|
||||
elem.removeAttribute('src');
|
||||
|
||||
var onLargeMediaElementAllowed = function() {
|
||||
elem.setAttribute('src', src);
|
||||
elem.removeAttribute(largeMediaElementAttribute);
|
||||
stayOrLeave();
|
||||
};
|
||||
|
||||
messager.send({
|
||||
what: 'temporarilyAllowLargeMediaElement'
|
||||
}, onLargeMediaElementAllowed);
|
||||
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
};
|
||||
|
||||
document.addEventListener('click', onMouseClick, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onLoad = function(ev) {
|
||||
var elem = ev.target;
|
||||
if ( elem.hasAttribute(largeMediaElementAttribute) ) {
|
||||
elem.removeAttribute(largeMediaElementAttribute);
|
||||
stayOrLeave();
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('load', onLoad, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onLoadError = function(ev) {
|
||||
var elem = ev.target;
|
||||
if ( mediaNotLoaded(elem) ) {
|
||||
elem.setAttribute(largeMediaElementAttribute, '');
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener('error', onLoadError, true);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.shutdown.add(function() {
|
||||
stayOrLeave(true);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
@ -52,14 +52,14 @@ if ( typeof vAPI !== 'object' ) {
|
||||
|
||||
if (
|
||||
document.querySelector('a[href^="abp:"],a[href^="https://subscribe.adblockplus.org/?"]') === null &&
|
||||
window.location.href.startsWith('https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web') === false
|
||||
window.location.href.lastIndexOf('https://github.com/gorhill/uBlock/wiki/Filter-lists-from-around-the-web', 0) !== 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('subscriber.js');
|
||||
var messager = vAPI.messaging.channel('scriptlets');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -159,18 +159,41 @@ var changeUserSettings = function(name, value) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onInputChanged = function(ev) {
|
||||
var input = ev.target;
|
||||
var name = this.getAttribute('data-setting-name');
|
||||
var value = input.value;
|
||||
if ( name === 'largeMediaSize' ) {
|
||||
value = Math.min(Math.max(Math.floor(parseInt(value, 10) || 0), 0), 1000000);
|
||||
}
|
||||
if ( value !== input.value ) {
|
||||
input.value = value;
|
||||
}
|
||||
changeUserSettings(name, value);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// TODO: use data-* to declare simple settings
|
||||
|
||||
var onUserSettingsReceived = function(details) {
|
||||
uDom('[data-setting-type="bool"]').forEach(function(uNode) {
|
||||
var input = uNode.nodeAt(0);
|
||||
uNode.prop('checked', details[input.getAttribute('data-setting-name')] === true)
|
||||
uNode.prop('checked', details[uNode.attr('data-setting-name')] === true)
|
||||
.on('change', function() {
|
||||
changeUserSettings(
|
||||
this.getAttribute('data-setting-name'),
|
||||
this.checked
|
||||
);
|
||||
});
|
||||
changeUserSettings(
|
||||
this.getAttribute('data-setting-name'),
|
||||
this.checked
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
uDom('[data-setting-name="noLargeMedia"] ~ label:first-of-type > input[type="number"]')
|
||||
.attr('data-setting-name', 'largeMediaSize')
|
||||
.attr('data-setting-type', 'input');
|
||||
|
||||
uDom('[data-setting-type="input"]').forEach(function(uNode) {
|
||||
uNode.val(details[uNode.attr('data-setting-name')])
|
||||
.on('change', onInputChanged);
|
||||
});
|
||||
|
||||
uDom('#export').on('click', exportToFile);
|
||||
|
@ -76,7 +76,7 @@ var onAllReady = function() {
|
||||
//quickProfiler.stop(0);
|
||||
|
||||
vAPI.onLoadAllCompleted();
|
||||
|
||||
µb.contextMenu.update(null);
|
||||
µb.firstInstall = false;
|
||||
};
|
||||
|
||||
@ -169,7 +169,6 @@ var onUserSettingsReady = function(fetched) {
|
||||
// Disabling local mirroring for the time being
|
||||
userSettings.experimentalEnabled = false;
|
||||
|
||||
µb.contextMenu.toggle(userSettings.contextMenuEnabled);
|
||||
vAPI.browserSettings.set({
|
||||
'hyperlinkAuditing': !userSettings.hyperlinkAuditingDisabled,
|
||||
'prefetching': !userSettings.prefetchingDisabled,
|
||||
|
@ -784,7 +784,7 @@ vAPI.tabs.registerListeners();
|
||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||
return;
|
||||
}
|
||||
tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 500);
|
||||
tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 666);
|
||||
};
|
||||
})();
|
||||
|
||||
|
@ -140,7 +140,7 @@ var onBeforeRequest = function(details) {
|
||||
tabId,
|
||||
'redirect',
|
||||
'rr:' + µb.redirectEngine.resourceNameRegister,
|
||||
'redirect',
|
||||
requestType,
|
||||
requestURL,
|
||||
requestContext.rootHostname,
|
||||
requestContext.pageHostname
|
||||
@ -336,7 +336,9 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// To handle `inline-script`.
|
||||
// To handle:
|
||||
// - inline script tags
|
||||
// - media elements larger than n kB
|
||||
|
||||
var onHeadersReceived = function(details) {
|
||||
// Do not interfere with behind-the-scene requests.
|
||||
@ -354,6 +356,10 @@ var onHeadersReceived = function(details) {
|
||||
if ( requestType === 'sub_frame' ) {
|
||||
return onFrameHeadersReceived(details);
|
||||
}
|
||||
|
||||
if ( requestType === 'image' || requestType === 'media' ) {
|
||||
return foilLargeMediaElement(details);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -449,6 +455,58 @@ var onFrameHeadersReceived = function(details) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/1163
|
||||
// "Block elements by size"
|
||||
|
||||
var foilLargeMediaElement = function(details) {
|
||||
var µb = µBlock;
|
||||
var tabId = details.tabId;
|
||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||
if ( pageStore === null ) {
|
||||
return;
|
||||
}
|
||||
if ( pageStore.getNetFilteringSwitch() !== true ) {
|
||||
return;
|
||||
}
|
||||
if ( Date.now() < pageStore.allowLargeMediaElementsUntil ) {
|
||||
return;
|
||||
}
|
||||
if ( µb.hnSwitches.evaluateZ('no-large-media', pageStore.tabHostname) !== true ) {
|
||||
return;
|
||||
}
|
||||
// Not all servers provide the Content-Length header: when this happens,
|
||||
// assume the worse.
|
||||
var contentLength = 1000000,
|
||||
i = headerIndexFromName('content-length', details.responseHeaders);
|
||||
if ( i !== -1 ) {
|
||||
contentLength = parseInt(details.responseHeaders[i].value, 10);
|
||||
if ( isNaN(contentLength) ) {
|
||||
contentLength = 1000000;
|
||||
}
|
||||
}
|
||||
if ( (contentLength >>> 10) < µb.userSettings.largeMediaSize ) {
|
||||
return;
|
||||
}
|
||||
|
||||
pageStore.logLargeMedia();
|
||||
|
||||
if ( µb.logger.isEnabled() ) {
|
||||
µb.logger.writeOne(
|
||||
tabId,
|
||||
'net',
|
||||
µb.hnSwitches.toResultString(),
|
||||
details.type,
|
||||
details.url,
|
||||
pageStore.tabHostname,
|
||||
pageStore.tabHostname
|
||||
);
|
||||
}
|
||||
|
||||
return { cancel: true };
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var foilInlineScripts = function(headers) {
|
||||
// Below is copy-pasta from uMatrix's project.
|
||||
|
||||
@ -567,8 +625,10 @@ vAPI.net.onHeadersReceived = {
|
||||
'https://*/*'
|
||||
],
|
||||
types: [
|
||||
"main_frame",
|
||||
"sub_frame"
|
||||
'main_frame',
|
||||
'sub_frame',
|
||||
'image',
|
||||
'media'
|
||||
],
|
||||
extra: [ 'blocking', 'responseHeaders' ],
|
||||
callback: onHeadersReceived
|
||||
|
@ -234,34 +234,44 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Return all settings if none specified.
|
||||
|
||||
µBlock.changeUserSettings = function(name, value) {
|
||||
var us = this.userSettings;
|
||||
|
||||
// Return all settings if none specified.
|
||||
if ( name === undefined ) {
|
||||
return this.userSettings;
|
||||
us = JSON.parse(JSON.stringify(us));
|
||||
us.noCosmeticFiltering = this.hnSwitches.evaluate('no-cosmetic-filtering', '*') === 1;
|
||||
us.noLargeMedia = this.hnSwitches.evaluate('no-large-media', '*') === 1;
|
||||
us.noRemoteFonts = this.hnSwitches.evaluate('no-remote-fonts', '*') === 1;
|
||||
return us;
|
||||
}
|
||||
|
||||
if ( typeof name !== 'string' || name === '' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not allow an unknown user setting to be created
|
||||
if ( this.userSettings[name] === undefined ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( value === undefined ) {
|
||||
return this.userSettings[name];
|
||||
return us[name];
|
||||
}
|
||||
|
||||
// Pre-change
|
||||
switch ( name ) {
|
||||
case 'largeMediaSize':
|
||||
if ( typeof value !== 'number' ) {
|
||||
value = parseInt(value, 10) || 0;
|
||||
}
|
||||
value = Math.ceil(Math.max(value, 0));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Change
|
||||
this.userSettings[name] = value;
|
||||
// Change -- but only if the user setting actually exists.
|
||||
var mustSave = us.hasOwnProperty(name) &&
|
||||
value !== us[name];
|
||||
if ( mustSave ) {
|
||||
us[name] = value;
|
||||
}
|
||||
|
||||
// Post-change
|
||||
switch ( name ) {
|
||||
@ -271,13 +281,28 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||
}
|
||||
break;
|
||||
case 'contextMenuEnabled':
|
||||
this.contextMenu.toggle(value);
|
||||
this.contextMenu.update(null);
|
||||
break;
|
||||
case 'experimentalEnabled':
|
||||
break;
|
||||
case 'hyperlinkAuditingDisabled':
|
||||
vAPI.browserSettings.set({ 'hyperlinkAuditing': !value });
|
||||
break;
|
||||
case 'noCosmeticFiltering':
|
||||
if ( this.hnSwitches.toggle('no-cosmetic-filtering', '*', value ? 1 : 0) ) {
|
||||
this.saveHostnameSwitches();
|
||||
}
|
||||
break;
|
||||
case 'noLargeMedia':
|
||||
if ( this.hnSwitches.toggle('no-large-media', '*', value ? 1 : 0) ) {
|
||||
this.saveHostnameSwitches();
|
||||
}
|
||||
break;
|
||||
case 'noRemoteFonts':
|
||||
if ( this.hnSwitches.toggle('no-remote-fonts', '*', value ? 1 : 0) ) {
|
||||
this.saveHostnameSwitches();
|
||||
}
|
||||
break;
|
||||
case 'prefetchingDisabled':
|
||||
vAPI.browserSettings.set({ 'prefetching': !value });
|
||||
break;
|
||||
@ -288,7 +313,9 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||
break;
|
||||
}
|
||||
|
||||
this.saveUserSettings();
|
||||
if ( mustSave ) {
|
||||
this.saveUserSettings();
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -379,16 +406,22 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||
}
|
||||
|
||||
// Take action if needed
|
||||
if ( details.name === 'no-cosmetic-filtering' ) {
|
||||
switch ( details.name ) {
|
||||
case 'no-cosmetic-filtering':
|
||||
this.scriptlets.injectDeep(
|
||||
details.tabId,
|
||||
details.state ? 'cosmetic-off' : 'cosmetic-on'
|
||||
);
|
||||
return;
|
||||
break;
|
||||
case 'no-large-media':
|
||||
if ( details.state === false ) {
|
||||
var pageStore = this.pageStoreFromTabId(details.tabId);
|
||||
if ( pageStore !== null ) {
|
||||
pageStore.temporarilyAllowLargeMediaElements();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Whatever else
|
||||
// ...
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -28,7 +28,7 @@
|
||||
<p class="statValue" id="popupHitDomainCount"> </p>
|
||||
<div id="extraTools">
|
||||
<span id="no-popups" class="hnSwitch fa" data-i18n-tip="popupTipNoPopups"><span class="badge"></span><span></span></span>
|
||||
<span id="no-strict-blocking" class="hnSwitch fa" data-i18n-tip="popupTipNoStrictBlocking"><span></span></span>
|
||||
<span id="no-large-media" class="hnSwitch fa" data-i18n-tip="popupTipNoLargeMedia"><span class="badge"></span><span></span></span>
|
||||
<span id="no-cosmetic-filtering" class="hnSwitch fa" data-i18n-tip="popupTipNoCosmeticFiltering"><span class="badge"></span><span></span></span>
|
||||
<span id="no-remote-fonts" class="hnSwitch fa" data-i18n-tip="popupTipNoRemoteFonts"><span class="badge"></span><span></span></span>
|
||||
</div>
|
||||
|
@ -23,6 +23,12 @@
|
||||
<li><input id="hyperlink-auditing-disabled" type="checkbox" data-setting-name="hyperlinkAuditingDisabled" data-setting-type="bool"><label data-i18n="settingsHyperlinkAuditingDisabledPrompt" for="hyperlink-auditing-disabled"></label> <a class="fa info" href="http://www.wilderssecurity.com/threads/hyperlink-auditing-aka-a-ping-and-beacon-aka-navigator-sendbeacon.364904/" target="_blank"></a>
|
||||
<li><input id="webrtc-ipaddress-hidden" type="checkbox" data-setting-name="webrtcIPAddressHidden" data-setting-type="bool"><label data-i18n="settingsWebRTCIPAddressHiddenPrompt" for="webrtc-ipaddress-hidden"></label> <a class="fa info important" href="https://github.com/gorhill/uBlock/wiki/Prevent-WebRTC-from-leaking-local-IP-address" target="_blank"></a>
|
||||
</ul>
|
||||
<li class="subgroup"><span data-i18n="settingPerSiteSwitchGroup"></span><ul>
|
||||
<li><label class="synopsis"><span data-i18n="settingPerSiteSwitchGroupSynopsis"></span> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Per-site-switches" target="_blank"></a></label>
|
||||
<li><input id="no-cosmetic-filtering" type="checkbox" data-setting-name="noCosmeticFiltering" data-setting-type="bool"><label data-i18n="settingsNoCosmeticFilteringPrompt" for="no-cosmetic-filtering"></label> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Per-site-switches#no-cosmetic-filtering" target="_blank"></a>
|
||||
<li><input id="no-large-media" type="checkbox" data-setting-name="noLargeMedia" data-setting-type="bool"><label data-i18n="settingsNoLargeMediaPrompt" for="no-large-media"></label> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Per-site-switches#no-large-media-elements" target="_blank"></a>
|
||||
<li><input id="no-remote-fonts" type="checkbox" data-setting-name="noRemoteFonts" data-setting-type="bool"><label data-i18n="settingsNoRemoteFontsPrompt" for="no-remote-fonts"></label> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Per-site-switches#no-remote-fonts" target="_blank"></a>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<div id="localData" style="margin: 0 1em;">
|
||||
|
@ -24,7 +24,7 @@ source_locale_dir = pj(build_dir, '_locales')
|
||||
target_locale_dir = pj(build_dir, 'locale')
|
||||
language_codes = []
|
||||
descriptions = OrderedDict({})
|
||||
title_case_strings = ['pickerContextMenuEntry']
|
||||
title_case_strings = ['pickerContextMenuEntry', 'contextMenuTemporarilyAllowLargeMediaElements']
|
||||
|
||||
for alpha2 in sorted(os.listdir(source_locale_dir)):
|
||||
locale_path = pj(source_locale_dir, alpha2, 'messages.json')
|
||||
|
Loading…
Reference in New Issue
Block a user