uBlock/src/js/contentscript-end.js

797 lines
27 KiB
JavaScript
Raw Normal View History

2014-06-24 00:42:43 +02:00
/*******************************************************************************
µBlock - a Chromium browser extension to block requests.
Copyright (C) 2014 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
*/
2015-02-14 18:16:36 +01:00
/* global vAPI, HTMLDocument */
2014-11-16 20:06:29 +01:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
// Injected into content pages
2015-01-02 03:14:53 +01:00
(function() {
'use strict';
/******************************************************************************/
2015-01-13 21:52:15 +01:00
// https://github.com/gorhill/uBlock/issues/464
if ( document instanceof HTMLDocument === false ) {
2015-01-23 17:32:49 +01:00
//console.debug('contentscript-end.js > not a HTLMDocument');
2015-01-13 21:52:15 +01:00
return false;
}
if ( !vAPI ) {
2015-01-23 17:32:49 +01:00
//console.debug('contentscript-end.js > vAPI not found');
2015-01-13 21:52:15 +01:00
return;
}
2015-01-23 17:47:31 +01:00
// https://github.com/gorhill/uBlock/issues/587
2015-01-23 17:32:49 +01:00
// Pointless to execute without the start script having done its job.
if ( !vAPI.contentscriptStartInjected ) {
2015-01-13 21:52:15 +01:00
return;
}
2014-06-24 00:42:43 +02:00
// https://github.com/gorhill/uBlock/issues/456
// Already injected?
2015-01-02 03:14:53 +01:00
if ( vAPI.contentscriptEndInjected ) {
2015-01-23 17:32:49 +01:00
//console.debug('contentscript-end.js > content script already injected');
return;
}
2015-01-02 03:14:53 +01:00
vAPI.contentscriptEndInjected = true;
/******************************************************************************/
var messager = vAPI.messaging.channel('contentscript-end.js');
2014-06-24 00:42:43 +02:00
/******************************************************************************/
2015-02-14 18:16:36 +01:00
// https://github.com/gorhill/uBlock/issues/789
// Be sure that specific cosmetic filters are still applied.
// Executed once, then flushed from memory.
(function() {
// Were there specific cosmetic filters?
if ( vAPI.specificHideStyle instanceof HTMLStyleElement === false ) {
2015-02-14 18:16:36 +01:00
return;
}
// Is our style tag still in the DOM? (the guess is whatever parent there
// is, it is in the DOM)
if ( vAPI.specificHideStyle.parentNode !== null ) {
2015-02-14 18:16:36 +01:00
return;
}
// Put it back
var parent = document.head || document.documentElement;
if ( parent ) {
parent.appendChild(vAPI.specificHideStyle);
2015-02-14 18:16:36 +01:00
}
})();
/******************************************************************************/
// Cosmetic filters
2014-06-24 00:42:43 +02:00
(function() {
2014-12-19 20:00:46 +01:00
if ( vAPI.skipCosmeticFiltering ) {
// console.debug('Abort cosmetic filtering');
return;
}
//var tStart = window.performance.now();
2014-07-02 18:02:29 +02:00
var queriedSelectors = {};
2015-01-01 05:09:22 +01:00
var injectedSelectors = {};
var lowGenericSelectors = [];
var highGenerics = null;
var contextNodes = [document];
var nullArray = { push: function(){} };
2014-07-02 18:02:29 +02:00
var retrieveGenericSelectors = function() {
2015-03-02 01:43:42 +01:00
if ( lowGenericSelectors.length !== 0 || highGenerics === null ) {
//console.log('µBlock> ABP cosmetic filters: retrieving CSS rules using %d selectors', lowGenericSelectors.length);
messager.send({
what: 'retrieveGenericCosmeticSelectors',
pageURL: window.location.href,
selectors: lowGenericSelectors,
highGenerics: highGenerics === null
},
retrieveHandler
);
// https://github.com/gorhill/uBlock/issues/452
// There is only one first..
retrieveHandler = otherRetrieveHandler;
} else {
otherRetrieveHandler(null);
}
lowGenericSelectors = [];
2015-01-01 05:09:22 +01:00
};
// https://github.com/gorhill/uBlock/issues/452
// This needs to be executed *after* the response from our query is
2015-01-01 05:09:22 +01:00
// received, not at `DOMContentLoaded` time, or else there is a good
// likeliness to outrun contentscript-start.js, which may still be waiting
// on a response from its own query.
var firstRetrieveHandler = function(response) {
2014-11-16 20:06:29 +01:00
// https://github.com/gorhill/uBlock/issues/158
// Ensure injected styles are enforced
// rhill 2014-11-16: not sure this is needed anymore. Test case in
// above issue was fine without the line below..
2014-11-27 12:41:28 +01:00
var selectors = vAPI.hideCosmeticFilters;
if ( typeof selectors === 'object' ) {
2015-01-01 05:09:22 +01:00
injectedSelectors = selectors;
//hideElements(Object.keys(selectors));
2014-06-26 00:44:35 +02:00
}
// Add exception filters into injected filters collection, in order
// to force them to be seen as "already injected".
2014-11-27 12:41:28 +01:00
selectors = vAPI.donthideCosmeticFilters;
if ( typeof selectors === 'object' ) {
2015-01-01 13:46:33 +01:00
for ( var selector in selectors ) {
2014-11-27 12:41:28 +01:00
if ( selectors.hasOwnProperty(selector) ) {
injectedSelectors[selector] = true;
}
}
}
// Flush dead code from memory (does this work?)
firstRetrieveHandler = null;
otherRetrieveHandler(response);
2014-07-02 18:02:29 +02:00
};
2014-06-24 00:42:43 +02:00
var otherRetrieveHandler = function(selectors) {
//var tStart = window.performance.now();
//console.debug('µBlock> contextNodes = %o', contextNodes);
if ( selectors && selectors.highGenerics ) {
highGenerics = selectors.highGenerics;
2014-06-24 00:42:43 +02:00
}
if ( selectors && selectors.donthide.length ) {
processLowGenerics(selectors.donthide, nullArray);
}
if ( highGenerics ) {
if ( highGenerics.donthideLowCount ) {
processHighLowGenerics(highGenerics.donthideLow, nullArray);
}
if ( highGenerics.donthideMediumCount ) {
processHighMediumGenerics(highGenerics.donthideMedium, nullArray);
2014-08-07 22:12:15 +02:00
}
}
// No such thing as high-high generic exceptions.
//if ( highGenerics.donthideHighCount ) {
// processHighHighGenerics(document, highGenerics.donthideHigh, nullArray);
//}
var hideSelectors = [];
if ( selectors && selectors.hide.length ) {
processLowGenerics(selectors.hide, hideSelectors);
}
if ( highGenerics ) {
if ( highGenerics.hideLowCount ) {
processHighLowGenerics(highGenerics.hideLow, hideSelectors);
}
if ( highGenerics.hideMediumCount ) {
processHighMediumGenerics(highGenerics.hideMedium, hideSelectors);
}
if ( highGenerics.hideHighCount ) {
processHighHighGenericsAsync();
}
}
if ( hideSelectors.length ) {
addStyleTag(hideSelectors);
2014-06-24 00:42:43 +02:00
}
contextNodes.length = 0;
//console.debug('%f: uBlock: CSS injection time', window.performance.now() - tStart);
2014-07-02 18:02:29 +02:00
};
2014-06-24 00:42:43 +02:00
var retrieveHandler = firstRetrieveHandler;
// Ensure elements matching a set of selectors are visually removed
// from the page, by:
// - Modifying the style property on the elements themselves
// - Injecting a style tag
var addStyleTag = function(selectors) {
//hideElements(selectors);
var style = document.createElement('style');
// The linefeed before the style block is very important: do no remove!
style.appendChild(document.createTextNode(selectors.toString() + '\n{display:none !important;}'));
var parent = document.body || document.documentElement;
if ( parent ) {
parent.appendChild(style);
}
messager.send({
what: 'injectedSelectors',
type: 'cosmetic',
hostname: window.location.hostname,
selectors: selectors
});
//console.debug('µBlock> generic cosmetic filters: injecting %d CSS rules:', selectors.length, text);
};
2014-08-17 22:07:42 +02:00
var hideElements = function(selectors) {
2014-09-04 02:14:55 +02:00
// https://github.com/gorhill/uBlock/issues/207
// Do not call querySelectorAll() using invalid CSS selectors
if ( selectors.length === 0 ) {
return;
}
2014-07-02 18:02:29 +02:00
if ( document.body === null ) {
return;
2014-06-24 00:42:43 +02:00
}
2014-08-17 22:07:42 +02:00
// https://github.com/gorhill/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
2014-07-02 18:02:29 +02:00
var elems = document.querySelectorAll(selectors);
var i = elems.length;
while ( i-- ) {
2014-08-17 22:07:42 +02:00
elems[i].style.setProperty('display', 'none', 'important');
2014-06-24 00:42:43 +02:00
}
2014-07-02 18:02:29 +02:00
};
// Extract and return the staged nodes which (may) match the selectors.
var selectNodes = function(selector) {
var targetNodes = [];
var i = contextNodes.length;
var node, nodeList, j;
var doc = document;
while ( i-- ) {
node = contextNodes[i];
if ( node === doc ) {
return doc.querySelectorAll(selector);
}
targetNodes.push(node);
nodeList = node.querySelectorAll(selector);
j = nodeList.length;
while ( j-- ) {
targetNodes.push(nodeList[j]);
}
2014-07-04 22:47:34 +02:00
}
return targetNodes;
2014-07-04 22:47:34 +02:00
};
// Low generics:
// - [id]
// - [class]
var processLowGenerics = function(generics, out) {
var i = generics.length;
var selector;
while ( i-- ) {
selector = generics[i];
2015-01-01 05:09:22 +01:00
if ( injectedSelectors.hasOwnProperty(selector) ) {
continue;
}
injectedSelectors[selector] = true;
out.push(selector);
2014-07-04 22:47:34 +02:00
}
};
// High-low generics:
// - [alt="..."]
// - [title="..."]
var processHighLowGenerics = function(generics, out) {
var attrs = ['title', 'alt'];
2014-08-14 02:03:55 +02:00
var attr, attrValue, nodeList, iNode, node;
var selector;
while ( attr = attrs.pop() ) {
nodeList = selectNodes('[' + attr + ']');
iNode = nodeList.length;
while ( iNode-- ) {
node = nodeList[iNode];
attrValue = node.getAttribute(attr);
if ( !attrValue ) { continue; }
2015-01-02 01:09:40 +01:00
// Candidate 1 = generic form
2015-01-02 01:16:02 +01:00
// If generic form is injected, no need to process the specific
2015-01-02 01:09:40 +01:00
// form, as the generic will affect all related specific forms
selector = '[' + attr + '="' + attrValue + '"]';
2015-01-02 01:09:40 +01:00
if ( generics.hasOwnProperty(selector) ) {
2015-01-01 05:09:22 +01:00
if ( injectedSelectors.hasOwnProperty(selector) === false ) {
2014-08-14 02:03:55 +02:00
injectedSelectors[selector] = true;
out.push(selector);
2015-01-02 01:09:40 +01:00
continue;
}
}
2015-01-02 01:09:40 +01:00
// Candidate 2 = specific form
selector = node.tagName.toLowerCase() + selector;
2015-01-02 01:09:40 +01:00
if ( generics.hasOwnProperty(selector) ) {
2015-01-01 05:09:22 +01:00
if ( injectedSelectors.hasOwnProperty(selector) === false ) {
2014-08-14 02:03:55 +02:00
injectedSelectors[selector] = true;
out.push(selector);
}
}
}
2014-07-04 22:47:34 +02:00
}
};
// High-medium generics:
// - [href^="http"]
var processHighMediumGenerics = function(generics, out) {
var nodeList = selectNodes('a[href^="http"]');
var iNode = nodeList.length;
2014-08-14 02:03:55 +02:00
var node, href, pos, hash, selectors, selector, iSelector;
while ( iNode-- ) {
node = nodeList[iNode];
href = node.getAttribute('href');
if ( !href ) { continue; }
pos = href.indexOf('://');
if ( pos === -1 ) { continue; }
hash = href.slice(pos + 3, pos + 11);
2014-08-14 02:03:55 +02:00
selectors = generics[hash];
if ( selectors === undefined ) { continue; }
iSelector = selectors.length;
while ( iSelector-- ) {
selector = selectors[iSelector];
2015-01-01 05:09:22 +01:00
if ( injectedSelectors.hasOwnProperty(selector) === false ) {
2014-08-14 02:03:55 +02:00
injectedSelectors[selector] = true;
out.push(selector);
2014-08-14 02:03:55 +02:00
}
2014-07-02 18:02:29 +02:00
}
}
};
// High-high generics are *very costly* to process, so we will coalesce
// requests to process high-high generics into as few requests as possible.
// The gain is *significant* on bloated pages.
// Speed boost (uBlock 0.8.9.2+):
// Element.matches is MUCH faster than document.querySelector().
//
// Example:
// gmail.com, a page rich in mutation events, which causes many calls
// to processHighHighGenerics(). Cumulative time of 24 calls to
// matchesSelector() (default filter lists):
// Chromium 40:
// - document.querySelector() = 44.79 ms
// - document.body.matches() = 6.06 ms
// Firefox 36:
// - document.querySelector() = 27.10 ms
// - document.body.matches() = 3.50 ms
//
// However, Element.matches() is available from Chromium 34/Firefox 34. So
// we fall back to slower document.querySelector() when Element.matches()
// is not available.
var matchesSelector = (function() {
if ( document.body && document.body.matches ) {
return function(selector) {
return document.body.matches(selector);
};
}
return function(selector) {
return document.querySelector(selector) !== null;
};
})();
var processHighHighGenericsTimer = null;
var processHighHighGenerics = function() {
processHighHighGenericsTimer = null;
if ( injectedSelectors.hasOwnProperty('{{highHighGenerics}}') ) {
return;
}
//var tStart = window.performance.now();
if ( matchesSelector(highGenerics.hideHigh) === false ) {
//console.debug('%f: uBlock: high-high generic time', window.performance.now() - tStart);
return;
}
2014-08-14 02:03:55 +02:00
injectedSelectors['{{highHighGenerics}}'] = true;
// We need to filter out possible exception cosmetic filters from
// high-high generics selectors.
var selectors = highGenerics.hideHigh.split(',\n');
var i = selectors.length;
2014-08-14 02:03:55 +02:00
var selector;
while ( i-- ) {
selector = selectors[i];
if ( injectedSelectors.hasOwnProperty(selector) ) {
selectors.splice(i, 1);
} else {
2014-08-14 02:03:55 +02:00
injectedSelectors[selector] = true;
2014-07-02 18:02:29 +02:00
}
}
if ( selectors.length !== 0 ) {
addStyleTag(selectors);
}
};
var processHighHighGenericsAsync = function() {
if ( processHighHighGenericsTimer !== null ) {
clearTimeout(processHighHighGenericsTimer);
}
processHighHighGenericsTimer = setTimeout(processHighHighGenerics, 300);
2014-07-02 18:02:29 +02:00
};
// Extract all ids: these will be passed to the cosmetic filtering
// engine, and in return we will obtain only the relevant CSS selectors.
2014-07-20 21:00:26 +02:00
var idsFromNodeList = function(nodes) {
2014-07-02 18:02:29 +02:00
if ( !nodes || !nodes.length ) {
return;
2014-06-24 00:42:43 +02:00
}
2014-07-02 18:02:29 +02:00
var qq = queriedSelectors;
var ll = lowGenericSelectors;
2014-07-20 21:00:26 +02:00
var node, v;
2014-07-02 18:02:29 +02:00
var i = nodes.length;
while ( i-- ) {
node = nodes[i];
2014-07-20 21:00:26 +02:00
if ( node.nodeType !== 1 ) { continue; }
2014-07-02 18:02:29 +02:00
// id
2014-07-09 07:53:49 +02:00
v = nodes[i].id;
2014-07-20 21:00:26 +02:00
if ( typeof v !== 'string' ) { continue; }
2014-07-09 07:53:49 +02:00
v = v.trim();
2014-07-20 21:00:26 +02:00
if ( v === '' ) { continue; }
v = '#' + v;
if ( qq.hasOwnProperty(v) ) { continue; }
ll.push(v);
2014-07-20 21:00:26 +02:00
qq[v] = true;
}
};
// Extract all classes: these will be passed to the cosmetic filtering
// engine, and in return we will obtain only the relevant CSS selectors.
2014-07-20 21:00:26 +02:00
var classesFromNodeList = function(nodes) {
if ( !nodes || !nodes.length ) {
return;
}
var qq = queriedSelectors;
var ll = lowGenericSelectors;
2014-07-20 21:00:26 +02:00
var node, v, vv, j;
var i = nodes.length;
while ( i-- ) {
node = nodes[i];
2014-11-05 16:29:19 +01:00
vv = node.classList;
if ( typeof vv !== 'object' ) { continue; }
j = vv.length || 0;
2014-07-02 18:02:29 +02:00
while ( j-- ) {
2014-11-05 16:29:19 +01:00
v = vv[j];
if ( typeof v !== 'string' ) { continue; }
2014-07-02 18:02:29 +02:00
v = '.' + v;
if ( qq.hasOwnProperty(v) ) { continue; }
ll.push(v);
2014-07-02 18:02:29 +02:00
qq[v] = true;
}
2014-06-24 00:42:43 +02:00
}
2014-07-02 18:02:29 +02:00
};
2014-06-24 00:42:43 +02:00
// Start cosmetic filtering.
2015-02-14 18:16:36 +01:00
idsFromNodeList(document.querySelectorAll('[id]'));
classesFromNodeList(document.querySelectorAll('[class]'));
retrieveGenericSelectors();
2014-06-24 00:42:43 +02:00
//console.debug('%f: uBlock: survey time', window.performance.now() - tStart);
// Below this point is the code which takes care to observe changes in
// the page and to add if needed relevant CSS rules as a result of the
// changes.
// Observe changes in the DOM only if...
// - there is a document.body
// - there is at least one `script` tag
if ( !document.body || !document.querySelector('script') ) {
return;
}
var ignoreTags = {
'link': true,
'LINK': true,
'script': true,
'SCRIPT': true,
'style': true,
'STYLE': true
};
2014-09-16 21:39:21 +02:00
// Added node lists will be cumulated here before being processed
var addedNodeLists = [];
var addedNodeListsTimer = null;
var mutationObservedHandler = function() {
var nodeList, iNode, node;
2014-09-16 21:39:21 +02:00
while ( nodeList = addedNodeLists.pop() ) {
iNode = nodeList.length;
while ( iNode-- ) {
node = nodeList[iNode];
if ( node.nodeType !== 1 ) {
continue;
}
2014-09-05 22:13:48 +02:00
if ( ignoreTags.hasOwnProperty(node.tagName) ) {
continue;
}
contextNodes.push(node);
}
}
2014-09-16 21:39:21 +02:00
addedNodeListsTimer = null;
if ( contextNodes.length !== 0 ) {
idsFromNodeList(selectNodes('[id]'));
classesFromNodeList(selectNodes('[class]'));
retrieveGenericSelectors();
}
2014-07-02 18:02:29 +02:00
};
2014-09-16 21:39:21 +02:00
// https://github.com/gorhill/uBlock/issues/205
// Do not handle added node directly from within mutation observer.
var treeMutationObservedHandlerAsync = function(mutations) {
2014-09-16 21:39:21 +02:00
var iMutation = mutations.length;
var nodeList;
while ( iMutation-- ) {
nodeList = mutations[iMutation].addedNodes;
if ( nodeList.length !== 0 ) {
2014-09-16 21:39:21 +02:00
addedNodeLists.push(nodeList);
}
}
if ( addedNodeListsTimer === null ) {
2015-01-01 05:09:22 +01:00
// I arbitrarily chose 100 ms for now:
// I have to compromise between the overhead of processing too few
2014-09-16 21:39:21 +02:00
// nodes too often and the delay of many nodes less often.
2015-01-01 05:09:22 +01:00
addedNodeListsTimer = setTimeout(mutationObservedHandler, 100);
2014-09-16 21:39:21 +02:00
}
};
// https://github.com/gorhill/httpswitchboard/issues/176
var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync);
treeObserver.observe(document.body, {
childList: true,
subtree: true
});
2014-07-02 18:02:29 +02:00
})();
2014-06-24 00:42:43 +02:00
2014-08-02 17:40:27 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
/******************************************************************************/
2014-09-14 22:20:40 +02:00
// Permanent
2014-06-27 23:06:42 +02:00
2014-07-20 21:00:26 +02:00
(function() {
2015-02-04 06:24:12 +01:00
// https://github.com/gorhill/uBlock/issues/683
// Instead of a closure we use a map to remember the element to collapse
var filterRequestId = 1;
var filterRequests = {};
var FilterRequest = function(target, selector) {
this.id = filterRequestId++;
this.target = target;
this.selector = selector;
};
2015-02-04 06:24:12 +01:00
FilterRequest.send = function(target, tagName, prop, src) {
var req = new FilterRequest(
target,
tagName + '[' + prop + '="' + src + '"]'
);
filterRequests[req.id] = req;
messager.send(
{
what: 'filterRequest',
id: req.id,
tagName: tagName,
requestURL: src,
pageHostname: window.location.hostname,
pageURL: window.location.href
},
onAnswerReceived
);
};
2015-02-04 06:24:12 +01:00
// Process answer: collapse, hide, or do nothing.
var onAnswerReceived = function(details) {
// This should not happen under normal circumstances. It probably can
// happen if the extension is disabled though.
if ( typeof details !== 'object' || details === null ) {
return;
}
// This should definitely not happen
if ( filterRequests.hasOwnProperty(details.id) === false ) {
return;
}
var req = filterRequests[details.id];
delete filterRequests[details.id];
2015-02-25 02:10:10 +01:00
// https://github.com/gorhill/uBlock/issues/869
if ( details.collapse !== true ) {
2015-02-04 06:24:12 +01:00
return;
}
//console.log('contentscript-end.js > onAnswerReceived(%o)', req);
// If `!important` is not there, going back using history will
// likely cause the hidden element to re-appear.
2015-02-25 02:10:10 +01:00
// https://github.com/gorhill/uBlock/issues/399
// Never remove elements from the DOM, just hide them
req.target.style.setProperty('display', 'none', 'important');
2015-02-04 06:24:12 +01:00
messager.send({
what: 'injectedSelectors',
type: 'net',
hostname: window.location.hostname,
selectors: req.selector
});
};
2015-02-04 06:24:12 +01:00
// https://github.com/gorhill/uBlock/issues/174
// Do not remove fragment from src URL
// TODO: Find out whether trying to send more than one filter request per
// message is worth it.
var onResource = function(target, dict) {
if ( !target ) {
return;
}
var tagName = target.tagName.toLowerCase();
var prop = dict[tagName];
if ( prop === undefined ) {
return;
}
var src = target[prop];
if ( typeof src !== 'string' || src === '' ) {
return;
}
2015-01-27 19:42:17 +01:00
if ( src.lastIndexOf('http', 0) !== 0 ) {
2014-09-14 22:20:40 +02:00
return;
}
2015-02-04 06:24:12 +01:00
FilterRequest.send(target, tagName, prop, src);
};
// Listeners to mop up whatever is otherwise missed:
// - Future requests not blocked yet
// - Elements dynamically added to the page
// - Elements which resource URL changes
2014-09-14 22:20:40 +02:00
2015-02-04 06:24:12 +01:00
var loadedElements = {
'iframe': 'src'
};
2014-08-25 14:49:08 +02:00
2015-02-04 06:24:12 +01:00
var failedElements = {
'img': 'src',
'input': 'src',
'object': 'data'
};
var onResourceLoaded = function(ev) {
2015-01-27 19:42:17 +01:00
//console.debug('onResourceLoaded(%o)', ev);
onResource(ev.target, loadedElements);
2014-08-02 17:40:27 +02:00
};
2014-08-02 17:40:27 +02:00
var onResourceFailed = function(ev) {
2015-01-27 19:42:17 +01:00
//console.debug('onResourceFailed(%o)', ev);
onResource(ev.target, failedElements);
2014-06-27 23:06:42 +02:00
};
2014-07-20 21:00:26 +02:00
document.addEventListener('load', onResourceLoaded, true);
2014-08-02 17:40:27 +02:00
document.addEventListener('error', onResourceFailed, true);
2014-07-02 18:02:29 +02:00
})();
2014-06-24 00:42:43 +02:00
/******************************************************************************/
2014-09-14 22:20:40 +02:00
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/7
// Executed only once
(function() {
var srcProps = {
'embed': 'src',
'iframe': 'src',
'img': 'src',
'object': 'data'
};
var elements = [];
var onAnswerReceived = function(details) {
if ( typeof details !== 'object' || details === null ) {
return;
}
var collapse = details.collapse;
2015-02-25 02:10:10 +01:00
// https://github.com/gorhill/uBlock/issues/869
if ( collapse !== true ) {
return;
}
var requests = details.requests;
2014-09-14 22:20:40 +02:00
var selectors = [];
var i = requests.length;
2015-02-25 02:10:10 +01:00
var request;
2014-09-14 22:20:40 +02:00
while ( i-- ) {
request = requests[i];
2015-02-25 02:10:10 +01:00
// https://github.com/gorhill/uBlock/issues/399
// Never remove elements from the DOM, just hide them
elements[request.index].style.setProperty('display', 'none', 'important');
2014-09-14 22:20:40 +02:00
selectors.push(request.tagName + '[' + srcProps[request.tagName] + '="' + request.url + '"]');
}
if ( selectors.length !== 0 ) {
messager.send({
2014-09-14 22:20:40 +02:00
what: 'injectedSelectors',
type: 'net',
hostname: window.location.hostname,
selectors: selectors
});
}
};
var requests = [];
var tagNames = ['embed','iframe','img','object'];
var elementIndex = 0;
var tagName, elems, i, elem, prop, src;
while ( tagName = tagNames.pop() ) {
elems = document.getElementsByTagName(tagName);
i = elems.length;
while ( i-- ) {
elem = elems[i];
prop = srcProps[tagName];
if ( prop === undefined ) {
continue;
}
src = elem[prop];
if ( typeof src !== 'string' || src === '' ) {
continue;
}
2015-01-27 19:42:17 +01:00
if ( src.lastIndexOf('http', 0) !== 0 ) {
2014-09-14 22:20:40 +02:00
continue;
}
requests.push({
index: elementIndex,
tagName: tagName,
url: src
});
elements[elementIndex] = elem;
elementIndex += 1;
}
}
var details = {
what: 'filterRequests',
pageURL: window.location.href,
pageHostname: window.location.hostname,
requests: requests
};
messager.send(details, onAnswerReceived);
2014-09-14 22:20:40 +02:00
})();
/******************************************************************************/
2014-09-28 20:38:17 +02:00
/******************************************************************************/
// To send mouse coordinates to context menu handler, as the chrome API fails
// to provide the mouse position to context menu listeners.
// This could be inserted in its own content script, but it's so simple that
// I feel it's not worth the overhead.
// Ref.: https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu
(function() {
if ( window !== window.top ) {
return;
}
2014-09-28 20:38:17 +02:00
var onContextMenu = function(ev) {
messager.send({
2014-09-28 20:38:17 +02:00
what: 'contextMenuEvent',
clientX: ev.clientX,
clientY: ev.clientY
});
};
window.addEventListener('contextmenu', onContextMenu, true);
2014-09-28 20:38:17 +02:00
})();
/******************************************************************************/
2015-01-02 03:14:53 +01:00
/******************************************************************************/
})();
/******************************************************************************/