1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-15 15:32:28 +02:00
This commit is contained in:
gorhill 2014-09-28 12:05:46 -04:00
parent b920b49d7d
commit 8d839d0230
10 changed files with 463 additions and 137 deletions

View File

@ -25,6 +25,7 @@
<script src="js/tab.js"></script> <script src="js/tab.js"></script>
<script src="js/traffic.js"></script> <script src="js/traffic.js"></script>
<script src="js/messaging-handlers.js"></script> <script src="js/messaging-handlers.js"></script>
<script src="js/contextmenu.js"></script>
<script src="js/start.js"></script> <script src="js/start.js"></script>
</body> </body>
</html> </html>

View File

@ -54,6 +54,7 @@ return {
userSettings: { userSettings: {
autoUpdate: true, autoUpdate: true,
collapseBlocked: true, collapseBlocked: true,
contextMenuEnabled: false,
externalLists: defaultExternalLists, externalLists: defaultExternalLists,
logRequests: false, logRequests: false,
parseAllABPHideFilters: true, parseAllABPHideFilters: true,
@ -114,6 +115,7 @@ return {
noopFunc: function(){}, noopFunc: function(){},
apiErrorCount: 0, apiErrorCount: 0,
elementPickerTarget: '',
// so that I don't have to care for last comma // so that I don't have to care for last comma
dummy: 0 dummy: 0

104
js/contextmenu.js Normal file
View File

@ -0,0 +1,104 @@
/*******************************************************************************
µ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
*/
/* global */
/******************************************************************************/
// New namespace
µBlock.contextMenu = (function() {
/******************************************************************************/
var µb = µBlock;
var enabled = false;
/******************************************************************************/
var onContextMenuClicked = function(details, tab) {
if ( details.menuItemId !== 'blockElement' ) {
return;
}
if ( tab === undefined ) {
return;
}
if ( /^https?:\/\//.test(tab.url) === false ) {
return;
}
var tagName = '';
var src = '';
if ( typeof details.frameUrl === 'string' ) {
tagName = 'iframe';
src = details.frameUrl;
} else if ( typeof details.srcUrl === 'string' ) {
if ( details.mediaType === 'image' ) {
tagName = 'img';
} else if ( details.mediaType === 'video' ) {
tagName = 'video';
} else if ( details.mediaType === 'audio' ) {
tagName = 'audio';
}
src = details.srcUrl;
} else if ( typeof details.linkUrl === 'string' ) {
tagName = 'a';
src = details.linkUrl;
}
if ( src === '' ) {
return;
}
µb.elementPickerExec(tab.id, tagName + '\t' + src);
};
/******************************************************************************/
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: chrome.i18n.getMessage('pickerContextMenuEntry'),
contexts: ['frame', 'link', 'image', 'video'],
documentUrlPatterns: ['https://*/*', 'http://*/*']
};
if ( on === true && enabled === false ) {
chrome.contextMenus.create(menuCreateDetails);
chrome.contextMenus.onClicked.addListener(onContextMenuClicked);
enabled = true;
} else if ( on !== true && enabled === true ) {
chrome.contextMenus.onClicked.removeListener(onContextMenuClicked);
chrome.contextMenus.remove(menuCreateDetails.id);
enabled = false;
}
};
/******************************************************************************/
return {
toggle: toggleMenu
};
/******************************************************************************/
})();

View File

@ -229,6 +229,10 @@ var svgOcean = null;
var svgIslands = null; var svgIslands = null;
var divDialog = null; var divDialog = null;
var taCandidate = null; var taCandidate = null;
var urlNormalizer = null;
var netFilterCandidates = [];
var cosmeticFilterCandidates = [];
var targetElements = []; var targetElements = [];
var svgWidth = 0; var svgWidth = 0;
@ -329,7 +333,7 @@ var removeElements = function(elems) {
// Extract the best possible net filter, i.e. as specific as possible. // Extract the best possible net filter, i.e. as specific as possible.
var netFilterFromElement = function(elem) { var netFilterFromElement = function(elem, out) {
if ( elem === null ) { if ( elem === null ) {
return; return;
} }
@ -344,14 +348,31 @@ var netFilterFromElement = function(elem) {
if ( typeof src !== 'string' || src.length === 0 ) { if ( typeof src !== 'string' || src.length === 0 ) {
return; return;
} }
return src.replace(/^https?:\/\//, '||').replace(/\?.*$/, ''); // Remove fragment
var pos = src.indexOf('#');
if ( pos !== -1 ) {
src = src.slice(0, pos);
}
// Feed the attribute to a link element, then retrieve back: this
// should normalize it.
urlNormalizer.href = src;
src = urlNormalizer.href;
// Anchor absolute filter to hostname
src = src.replace(/^https?:\/\//, '||');
out.push(src);
// Suggest a less narrow filter if possible
pos = src.indexOf('?');
if ( pos !== -1 ) {
src = src.slice(0, pos);
out.push(src);
}
}; };
/******************************************************************************/ /******************************************************************************/
// Extract the best possible cosmetic filter, i.e. as specific as possible. // Extract the best possible cosmetic filter, i.e. as specific as possible.
var cosmeticFilterFromElement = function(elem) { var cosmeticFilterFromElement = function(elem, out) {
if ( elem === null ) { if ( elem === null ) {
return; return;
} }
@ -417,42 +438,73 @@ var cosmeticFilterFromElement = function(elem) {
} }
} }
return '##' + prefix + suffix.join(''); out.push('##' + prefix + suffix.join(''));
}; };
/******************************************************************************/ /******************************************************************************/
var selectorFromCandidate = function() { var filtersFromElement = function(elem) {
var selector = ''; netFilterCandidates.length = 0;
var v = taCandidate.value; cosmeticFilterCandidates.length = 0;
if ( v.slice(0, 2) === '##' ) { while ( elem && elem !== document.body ) {
selector = v.replace('##', ''); netFilterFromElement(elem, netFilterCandidates);
} else { cosmeticFilterFromElement(elem, cosmeticFilterCandidates);
if ( v.slice(0, 2) === '||' ) { elem = elem.parentNode;
v = v.replace('||', '');
}
selector = '[src*="' + v + '"]';
} }
try {
if ( document.querySelector(selector) === null ) {
return '';
}
}
catch (e) {
return '';
}
return selector;
}; };
/******************************************************************************/ /******************************************************************************/
var elementsFromFilter = function(filter) {
var out = [];
// Cosmetic filters: these are straight CSS selectors
// TODO: This is still not working well for a[href], because there are
// many ways to compose a valid href to the same effective URL.
// One idea is to normalize all a[href] on the page, but for now I will
// wait and see, as I prefer to refrain from tampering with the page
// content if I can avoid it.
if ( filter.slice(0, 2) === '##' ) {
try {
out = document.querySelectorAll(filter.replace('##', ''));
}
catch (e) {
}
return out;
}
// Net filters: we need to lookup manually -- translating into a
// foolproof CSS selector is just not possible
if ( filter.slice(0, 2) === '||' ) {
filter = filter.replace('||', '');
}
var elems = document.querySelectorAll('[src]');
var i = elems.length;
var elem;
while ( i-- ) {
elem = elems[i];
if ( typeof elem.src !== 'string' ) {
continue;
}
if ( elem.src.indexOf(filter) !== -1 ) {
out.push(elem);
}
}
return out;
};
// https://www.youtube.com/watch?v=YI2XuIOW3gM
/******************************************************************************/
var userFilterFromCandidate = function() { var userFilterFromCandidate = function() {
if ( selectorFromCandidate() === '' ) { var v = taCandidate.value;
var elems = elementsFromFilter(v);
if ( elems.length === 0 ) {
return false; return false;
} }
var v = taCandidate.value;
// Cosmetic filter? // Cosmetic filter?
if ( v.slice(0, 2) === '##' ) { if ( v.slice(0, 2) === '##' ) {
return window.location.hostname + v; return window.location.hostname + v;
@ -468,52 +520,62 @@ var userFilterFromCandidate = function() {
/******************************************************************************/ /******************************************************************************/
var onCandidateChanged = function(ev) { var onCandidateChanged = function() {
var selector = selectorFromCandidate(); var elems = elementsFromFilter(taCandidate.value);
divDialog.querySelector('#create').disabled = selector === ''; divDialog.querySelector('#create').disabled = elems.length === 0;
if ( selector === '' ) { highlightElements(elems);
highlightElements([]);
return;
}
highlightElements(document.querySelectorAll(selector));
}; };
/******************************************************************************/ /******************************************************************************/
var candidateFromClickEvent = function(ev) { var candidateFromFilterChoice = function(filterChoice) {
var target = ev.target; var slot = filterChoice.slot;
if ( !target ) { var filters = filterChoice.filters;
var filter = filters[slot];
if ( filter === undefined ) {
return ''; return '';
} }
// Bare
if ( ev.ctrlKey || ev.metaKey ) {
return target.textContent;
}
// For net filters there no such thing as a path // For net filters there no such thing as a path
if ( target.textContent.charAt(0) !== '#' ) { if ( filterChoice.type === 'net' || filterChoice.modifier ) {
return target.textContent; return filter;
} }
// Return path: the target element, then all siblings prepended // Return path: the target element, then all siblings prepended
var selector = []; var selector = [];
while ( target ) { var filter;
if ( target.nodeType !== 1 || target.tagName.toLowerCase() !== 'li' ) { for ( ; slot < filters.length; slot++ ) {
continue; filter = filters[slot];
} selector.unshift(filter.replace(/^##/, ''));
selector.unshift(target.textContent.replace(/^##/, ''));
// Stop at any element with an id: these are unique in a web page // Stop at any element with an id: these are unique in a web page
if ( target.textContent.slice(0, 3) === '###' ) { if ( filter.slice(0, 3) === '###' ) {
break; break;
} }
target = target.nextSibling;
} }
return '##' + selector.join(' > '); return '##' + selector.join(' > ');
}; };
/******************************************************************************/ /******************************************************************************/
var filterChoiceFromEvent = function(ev) {
var li = ev.target;
var isNetFilter = li.textContent.slice(0, 2) !== '##';
var r = {
type: isNetFilter ? 'net' : 'cosmetic',
filters: isNetFilter ? netFilterCandidates : cosmeticFilterCandidates,
slot: 0,
modifier: ev.ctrlKey || ev.metaKey
};
while ( li.previousSibling !== null ) {
li = li.previousSibling;
r.slot += 1;
}
return r;
};
/******************************************************************************/
var onDialogClicked = function(ev) { var onDialogClicked = function(ev) {
if ( ev.target === null ) { if ( ev.target === null ) {
/* do nothing */ /* do nothing */
@ -523,7 +585,7 @@ var onDialogClicked = function(ev) {
var filter = userFilterFromCandidate(); var filter = userFilterFromCandidate();
if ( filter ) { if ( filter ) {
messaging.tell({ what: 'createUserFilter', filters: filter }); messaging.tell({ what: 'createUserFilter', filters: filter });
removeElements(document.querySelectorAll(selectorFromCandidate())); removeElements(elementsFromFilter(taCandidate.value));
stopPicker(); stopPicker();
} }
} }
@ -537,7 +599,7 @@ var onDialogClicked = function(ev) {
} }
else if ( ev.target.tagName.toLowerCase() === 'li' && pickerRootDistance(ev.target) === 5 ) { else if ( ev.target.tagName.toLowerCase() === 'li' && pickerRootDistance(ev.target) === 5 ) {
taCandidate.value = candidateFromClickEvent(ev); taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev));
onCandidateChanged(); onCandidateChanged();
} }
@ -558,28 +620,51 @@ var removeAllChildren = function(parent) {
// TODO: for convenience I could provide a small set of net filters instead // TODO: for convenience I could provide a small set of net filters instead
// of just a single one. Truncating the right-most part of the path etc. // of just a single one. Truncating the right-most part of the path etc.
var showDialog = function(filters) { var showDialog = function(options) {
var divNet = divDialog.querySelector('ul > li:nth-of-type(1) > ul');
var divCosmetic = divDialog.querySelector('ul > li:nth-of-type(2) > ul');
removeAllChildren(divCosmetic);
removeAllChildren(divNet);
var filter, li;
for ( var i = 0; i < filters.length; i++ ) {
filter = filters[i];
li = document.createElement('li');
li.textContent = filter;
if ( filter.indexOf('##') === 0 ) {
divCosmetic.appendChild(li);
} else {
divNet.appendChild(li);
}
}
divDialog.querySelector('ul > li:nth-of-type(1)').style.display = divNet.firstChild ? '' : 'none';
divDialog.querySelector('ul > li:nth-of-type(2)').style.display = divCosmetic.firstChild ? '' : 'none';
divDialog.querySelector('ul').style.display = divNet.firstChild || divCosmetic.firstChild ? '' : 'none';
taCandidate.value = '';
divDialog.querySelector('#create').disabled = true;
pausePicker(); pausePicker();
options = options || {};
// Create lists of candidate filters
var populate = function(src, des) {
var root = divDialog.querySelector(des);
var ul = root.querySelector('ul');
removeAllChildren(ul);
var li;
for ( var i = 0; i < src.length; i++ ) {
li = document.createElement('li');
li.textContent = src[i];
ul.appendChild(li);
}
root.style.display = src.length !== 0 ? '' : 'none';
};
populate(netFilterCandidates, 'ul > li:nth-of-type(1)');
populate(cosmeticFilterCandidates, 'ul > li:nth-of-type(2)');
divDialog.querySelector('ul').style.display = netFilterCandidates.length || cosmeticFilterCandidates.length ? '' : 'none';
divDialog.querySelector('#create').disabled = true;
// Auto-select a candidate filter
var filterChoice = {
type: '',
filters: [],
slot: 0,
modifier: options.modifier || false
};
if ( netFilterCandidates.length ) {
filterChoice.type = 'net';
filterChoice.filters = netFilterCandidates;
} else if ( cosmeticFilterCandidates.length ) {
filterChoice.type = 'cosmetic';
filterChoice.filters = cosmeticFilterCandidates;
}
taCandidate.value = '';
if ( filterChoice.type !== '' ) {
taCandidate.value = candidateFromFilterChoice(filterChoice);
onCandidateChanged();
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -604,18 +689,8 @@ var onSvgClicked = function() {
if ( pickerPaused() ) { if ( pickerPaused() ) {
return; return;
} }
filtersFromElement(targetElements[0]);
var filter; showDialog();
var filters = [];
for ( var elem = targetElements[0]; elem && elem !== document.body; elem = elem.parentNode ) {
if ( filter = cosmeticFilterFromElement(elem) ) {
filters.push(filter);
}
if ( filter = netFilterFromElement(elem) ) {
filters.push(filter);
}
}
showDialog(filters);
}; };
/******************************************************************************/ /******************************************************************************/
@ -646,6 +721,9 @@ var onScrolled = function(ev) {
/******************************************************************************/ /******************************************************************************/
// Let's have the element picker code flushed from memory when no longer
// in use: to ensure this, release all local references.
var stopPicker = function() { var stopPicker = function() {
if ( pickerRoot !== null ) { if ( pickerRoot !== null ) {
document.removeEventListener('keydown', onKeyPressed); document.removeEventListener('keydown', onKeyPressed);
@ -655,7 +733,11 @@ var stopPicker = function() {
svgRoot.removeEventListener('mousemove', onSvgHovered); svgRoot.removeEventListener('mousemove', onSvgHovered);
svgRoot.removeEventListener('click', onSvgClicked); svgRoot.removeEventListener('click', onSvgClicked);
document.body.removeChild(pickerRoot); document.body.removeChild(pickerRoot);
pickerRoot = divDialog = svgRoot = svgOcean = svgIslands = taCandidate = null; pickerRoot =
divDialog =
svgRoot = svgOcean = svgIslands =
taCandidate =
urlNormalizer = null;
messaging.stop(); messaging.stop();
} }
targetElements = []; targetElements = [];
@ -685,6 +767,7 @@ var startPicker = function() {
'color: #000;', 'color: #000;',
'font: 12px sans-serif;', 'font: 12px sans-serif;',
'margin: 0;', 'margin: 0;',
'max-width: none;',
'outline: 0;', 'outline: 0;',
'overflow: visible;', 'overflow: visible;',
'padding: 0;', 'padding: 0;',
@ -710,6 +793,9 @@ var startPicker = function() {
'color: #999;', 'color: #999;',
'background-color: #ccc;', 'background-color: #ccc;',
'}', '}',
'.µBlock button#create:not(:disabled) {',
'background-color: #ffdca8;',
'}',
'.µBlock > svg {', '.µBlock > svg {',
'position: absolute;', 'position: absolute;',
'top: 0;', 'top: 0;',
@ -782,6 +868,17 @@ var startPicker = function() {
'text-align: left;', 'text-align: left;',
'overflow: hidden;', 'overflow: hidden;',
'}', '}',
'.µBlock > div > ul > li:not(:first-child) {',
'margin-top: 0.5em;',
'}',
'.µBlock > div > ul > li > span:nth-of-type(1) {',
'font-weight: bold;',
'}',
'.µBlock > div > ul > li > span:nth-of-type(2) {',
'margin: 0 0 0 1em;',
'font-size: smaller;',
'color: gray;',
'}',
'.µBlock > div > ul > li > ul {', '.µBlock > div > ul > li > ul {',
'margin: 0 0 0 1em;', 'margin: 0 0 0 1em;',
'list-style-type: none;', 'list-style-type: none;',
@ -832,8 +929,8 @@ var startPicker = function() {
'</div>', '</div>',
'</div>', '</div>',
'<ul>', '<ul>',
'<li>.<ul></ul>', '<li><span>.</span><ul></ul>',
'<li>.<ul></ul>', '<li><span>.</span><span>.</span><ul></ul>',
'</ul>', '</ul>',
'' ''
].join(''); ].join('');
@ -845,6 +942,7 @@ var startPicker = function() {
divDialog.addEventListener('click', onDialogClicked); divDialog.addEventListener('click', onDialogClicked);
taCandidate = divDialog.querySelector('textarea'); taCandidate = divDialog.querySelector('textarea');
taCandidate.addEventListener('input', onCandidateChanged); taCandidate.addEventListener('input', onCandidateChanged);
urlNormalizer = document.createElement('a');
window.addEventListener('scroll', onScrolled); window.addEventListener('scroll', onScrolled);
document.addEventListener('keydown', onKeyPressed); document.addEventListener('keydown', onKeyPressed);
}; };
@ -855,12 +953,59 @@ startPicker();
/******************************************************************************/ /******************************************************************************/
messaging.ask({ what: 'i18n' }, function(details) { messaging.ask({ what: 'elementPickerArguments' }, function(details) {
divDialog.querySelector('#create').firstChild.nodeValue = details.create; var i18nMap = {
divDialog.querySelector('#pick').firstChild.nodeValue = details.pick; '#create': 'create',
divDialog.querySelector('#quit').firstChild.nodeValue = details.quit; '#pick': 'pick',
divDialog.querySelector('ul > li:nth-of-type(1)').firstChild.nodeValue = details.netFilters; '#quit': 'quit',
divDialog.querySelector('ul > li:nth-of-type(2)').firstChild.nodeValue = details.cosmeticFilters; 'ul > li:nth-of-type(1) > span:nth-of-type(1)': 'netFilters',
'ul > li:nth-of-type(2) > span:nth-of-type(1)': 'cosmeticFilters',
'ul > li:nth-of-type(2) > span:nth-of-type(2)': 'cosmeticFiltersHint'
};
for ( var k in i18nMap ) {
if ( i18nMap.hasOwnProperty(k) === false ) {
continue;
}
divDialog.querySelector(k).firstChild.nodeValue = details.i18n[i18nMap[k]];
}
// Auto-select a specific target, if any, and if possible
var targetElement = details.targetElement || '';
var pos = targetElement.indexOf('\t');
if ( pos === -1 ) {
return;
}
var srcAttrMap = {
'a': 'href',
'img': 'src',
'iframe': 'src',
'video': 'src',
'audio': 'src'
};
var tagName = targetElement.slice(0, pos);
var url = targetElement.slice(pos + 1);
var attr = srcAttrMap[tagName];
if ( attr === undefined ) {
return;
}
var elems = document.querySelectorAll(tagName + '[' + attr + ']');
var i = elems.length;
var elem, src;
while ( i-- ) {
elem = elems[i];
src = elem.getAttribute(attr);
if ( src === null || src === '' ) {
continue;
}
urlNormalizer.href = src;
src = urlNormalizer.href;
if ( src === url ) {
filtersFromElement(elem);
showDialog({ modifier: true });
break;
}
}
}); });
/******************************************************************************/ /******************************************************************************/

View File

@ -24,14 +24,17 @@
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
// popup.js
(function() { (function() {
// popup.js /******************************************************************************/
var µb = µBlock;
/******************************************************************************/ /******************************************************************************/
var getStats = function(request) { var getStats = function(request) {
var µb = µBlock;
var r = { var r = {
globalBlockedRequestCount: µb.localSettings.blockedRequestCount, globalBlockedRequestCount: µb.localSettings.blockedRequestCount,
globalAllowedRequestCount: µb.localSettings.allowedRequestCount, globalAllowedRequestCount: µb.localSettings.allowedRequestCount,
@ -72,26 +75,28 @@ var onMessage = function(request, sender, callback) {
break; break;
case 'toggleNetFiltering': case 'toggleNetFiltering':
µBlock.toggleNetFilteringSwitch( µb.toggleNetFilteringSwitch(
request.url, request.url,
request.scope, request.scope,
request.state request.state
); );
µBlock.updateBadgeAsync(request.tabId); µb.updateBadgeAsync(request.tabId);
break; break;
case 'gotoPick': case 'gotoPick':
chrome.tabs.executeScript(request.tabId, { file: 'js/element-picker.js' }); µb.elementPickerExec(request.tabId);
break; break;
default: default:
return µBlock.messaging.defaultHandler(request, sender, callback); return µb.messaging.defaultHandler(request, sender, callback);
} }
callback(response); callback(response);
}; };
µBlock.messaging.listen('popup.js', onMessage); µb.messaging.listen('popup.js', onMessage);
/******************************************************************************/
})(); })();
@ -102,8 +107,12 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
/******************************************************************************/
var µb = µBlock; var µb = µBlock;
/******************************************************************************/
var onMessage = function(request, sender, callback) { var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
@ -135,6 +144,8 @@ var onMessage = function(request, sender, callback) {
µb.messaging.listen('contentscript-start.js', onMessage); µb.messaging.listen('contentscript-start.js', onMessage);
/******************************************************************************/
})(); })();
/******************************************************************************/ /******************************************************************************/
@ -144,6 +155,8 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
/******************************************************************************/
var µb = µBlock; var µb = µBlock;
/******************************************************************************/ /******************************************************************************/
@ -256,6 +269,8 @@ var onMessage = function(details, sender, callback) {
µb.messaging.listen('contentscript-end.js', onMessage); µb.messaging.listen('contentscript-end.js', onMessage);
/******************************************************************************/
})(); })();
/******************************************************************************/ /******************************************************************************/
@ -265,6 +280,12 @@ var onMessage = function(details, sender, callback) {
(function() { (function() {
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
var onMessage = function(request, sender, callback) { var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
@ -276,28 +297,35 @@ var onMessage = function(request, sender, callback) {
var response; var response;
switch ( request.what ) { switch ( request.what ) {
case 'i18n': case 'elementPickerArguments':
response = { response = {
create: chrome.i18n.getMessage('pickerCreate'), i18n: {
pick: chrome.i18n.getMessage('pickerPick'), create: chrome.i18n.getMessage('pickerCreate'),
quit: chrome.i18n.getMessage('pickerQuit'), pick: chrome.i18n.getMessage('pickerPick'),
netFilters: chrome.i18n.getMessage('pickerNetFilters'), quit: chrome.i18n.getMessage('pickerQuit'),
cosmeticFilters: chrome.i18n.getMessage('pickerCosmeticFilters') netFilters: chrome.i18n.getMessage('pickerNetFilters'),
cosmeticFilters: chrome.i18n.getMessage('pickerCosmeticFilters'),
cosmeticFiltersHint: chrome.i18n.getMessage('pickerCosmeticFiltersHint')
},
targetElement: µb.elementPickerTarget
}; };
µb.elementPickerTarget = '';
break; break;
case 'createUserFilter': case 'createUserFilter':
µBlock.appendUserFilters(request.filters); µb.appendUserFilters(request.filters);
break; break;
default: default:
return µBlock.messaging.defaultHandler(request, sender, callback); return µb.messaging.defaultHandler(request, sender, callback);
} }
callback(response); callback(response);
}; };
µBlock.messaging.listen('element-picker.js', onMessage); µb.messaging.listen('element-picker.js', onMessage);
/******************************************************************************/
})(); })();
@ -308,8 +336,13 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
var getLists = function(callback) { var getLists = function(callback) {
var µb = µBlock;
var r = { var r = {
available: null, available: null,
current: µb.remoteBlacklists, current: µb.remoteBlacklists,
@ -334,8 +367,6 @@ var getLists = function(callback) {
/******************************************************************************/ /******************************************************************************/
var onMessage = function(request, sender, callback) { var onMessage = function(request, sender, callback) {
var µb = µBlock;
// Async // Async
switch ( request.what ) { switch ( request.what ) {
case 'getLists': case 'getLists':
@ -369,7 +400,9 @@ var onMessage = function(request, sender, callback) {
callback(response); callback(response);
}; };
µBlock.messaging.listen('3p-filters.js', onMessage); µb.messaging.listen('3p-filters.js', onMessage);
/******************************************************************************/
})(); })();
@ -380,9 +413,13 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
var onMessage = function(request, sender, callback) { /******************************************************************************/
var µb = µBlock;
var µb = µBlock;
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
case 'readUserFilters': case 'readUserFilters':
@ -406,7 +443,9 @@ var onMessage = function(request, sender, callback) {
callback(response); callback(response);
}; };
µBlock.messaging.listen('1p-filters.js', onMessage); µb.messaging.listen('1p-filters.js', onMessage);
/******************************************************************************/
})(); })();
@ -417,9 +456,13 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
var onMessage = function(request, sender, callback) { /******************************************************************************/
var µb = µBlock;
var µb = µBlock;
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
default: default:
@ -446,7 +489,9 @@ var onMessage = function(request, sender, callback) {
callback(response); callback(response);
}; };
µBlock.messaging.listen('whitelist.js', onMessage); µb.messaging.listen('whitelist.js', onMessage);
/******************************************************************************/
})(); })();
@ -457,6 +502,12 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
var getPageDetails = function(µb, tabId) { var getPageDetails = function(µb, tabId) {
var r = { var r = {
blockedRequests: [], blockedRequests: [],
@ -510,8 +561,6 @@ var getPageDetails = function(µb, tabId) {
/******************************************************************************/ /******************************************************************************/
var onMessage = function(request, sender, callback) { var onMessage = function(request, sender, callback) {
var µb = µBlock;
// Async // Async
switch ( request.what ) { switch ( request.what ) {
default: default:
@ -537,7 +586,9 @@ var onMessage = function(request, sender, callback) {
callback(response); callback(response);
}; };
µBlock.messaging.listen('stats.js', onMessage); µb.messaging.listen('stats.js', onMessage);
/******************************************************************************/
})(); })();
@ -548,9 +599,13 @@ var onMessage = function(request, sender, callback) {
(function() { (function() {
var onMessage = function(request, sender, callback) { /******************************************************************************/
var µb = µBlock;
var µb = µBlock;
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
@ -570,7 +625,9 @@ var onMessage = function(request, sender, callback) {
callback(response); callback(response);
}; };
µBlock.messaging.listen('about.js', onMessage); µb.messaging.listen('about.js', onMessage);
/******************************************************************************/
})(); })();

View File

@ -55,6 +55,12 @@ var onUserSettingsReceived = function(details) {
.on('change', function(){ .on('change', function(){
changeUserSettings('showIconBadge', this.checked); changeUserSettings('showIconBadge', this.checked);
}); });
uDom('#context-menu-enabled')
.prop('checked', details.contextMenuEnabled === true)
.on('change', function(){
changeUserSettings('contextMenuEnabled', this.checked);
});
}; };
messaging.ask({ what: 'userSettings' }, onUserSettingsReceived); messaging.ask({ what: 'userSettings' }, onUserSettingsReceived);

View File

@ -74,7 +74,7 @@
var settingsLoaded = function(store) { var settingsLoaded = function(store) {
µBlock.userSettings = store; µBlock.userSettings = store;
if ( typeof callback === 'function' ) { if ( typeof callback === 'function' ) {
callback(); callback(store);
} }
}; };
@ -666,7 +666,8 @@
}; };
// User settings are in memory // User settings are in memory
var onUserSettingsReady = function() { var onUserSettingsReady = function(settings) {
µb.contextMenu.toggle(settings.contextMenuEnabled);
µb.fromSelfie(onSelfieReady); µb.fromSelfie(onSelfieReady);
}; };

View File

@ -184,10 +184,8 @@
// Return all settings if none specified. // Return all settings if none specified.
µBlock.changeUserSettings = function(name, value) { µBlock.changeUserSettings = function(name, value) {
var µb = µBlock;
if ( name === undefined ) { if ( name === undefined ) {
return µb.userSettings; return this.userSettings;
} }
if ( typeof name !== 'string' || name === '' ) { if ( typeof name !== 'string' || name === '' ) {
@ -195,12 +193,12 @@
} }
// Do not allow an unknown user setting to be created // Do not allow an unknown user setting to be created
if ( µb.userSettings[name] === undefined ) { if ( this.userSettings[name] === undefined ) {
return; return;
} }
if ( value === undefined ) { if ( value === undefined ) {
return µb.userSettings[name]; return this.userSettings[name];
} }
// Pre-change // Pre-change
@ -210,15 +208,18 @@
} }
// Change // Change
µb.userSettings[name] = value; this.userSettings[name] = value;
// Post-change // Post-change
switch ( name ) { switch ( name ) {
case 'contextMenuEnabled':
this.contextMenu.toggle(value === true);
break;
default: default:
break; break;
} }
µb.saveUserSettings(); this.saveUserSettings();
}; };
/******************************************************************************/ /******************************************************************************/
@ -242,3 +243,10 @@
}; };
/******************************************************************************/ /******************************************************************************/
µBlock.elementPickerExec = function(tabId, targetElement) {
this.elementPickerTarget = targetElement || '';
chrome.tabs.executeScript(tabId, { file: 'js/element-picker.js' });
};
/******************************************************************************/

View File

@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "__MSG_extName__", "name": "__MSG_extName__",
"short_name": "µBlock", "short_name": "µBlock",
"version": "0.6.4.0", "version": "0.6.5.0",
"description": "__MSG_extShortDesc__", "description": "__MSG_extShortDesc__",
"icons": { "icons": {
"16": "img/icon_16.png", "16": "img/icon_16.png",
@ -38,6 +38,7 @@
"minimum_chrome_version": "22.0", "minimum_chrome_version": "22.0",
"options_page": "dashboard.html", "options_page": "dashboard.html",
"permissions": [ "permissions": [
"contextMenus",
"downloads", "downloads",
"storage", "storage",
"tabs", "tabs",

View File

@ -17,6 +17,7 @@ ul {
<ul> <ul>
<li><input id="collapse-blocked" type="checkbox"> <label data-i18n="settingsCollapseBlockedPrompt"></label> <li><input id="collapse-blocked" type="checkbox"> <label data-i18n="settingsCollapseBlockedPrompt"></label>
<li><input id="icon-badge" type="checkbox"> <label data-i18n="settingsIconBadgePrompt"></label> <li><input id="icon-badge" type="checkbox"> <label data-i18n="settingsIconBadgePrompt"></label>
<li><input id="context-menu-enabled" type="checkbox"><label data-i18n="settingsContextMenuPrompt"></label>
</ul> </ul>
<script src="js/udom.js"></script> <script src="js/udom.js"></script>