Merge pull request #1 from gorhill/master

123
This commit is contained in:
gyg3@ya.ru 2017-09-05 13:19:37 +03:00 committed by GitHub
commit 697e4ae7bb
84 changed files with 2033 additions and 1307 deletions

View file

@ -4,7 +4,8 @@
"eqeqeq": true,
"esnext": true,
"globals": {
"chrome": false,
"browser": false, // global variable in Firefox, Edge
"chrome": false, // global variable in Chromium, Chrome, Opera
"Components": false, // global variable in Firefox
"safari": false,
"self": false,

View file

@ -1,12 +1,14 @@
# Submitting issues
For **support/discussions**, there is [Mozilla Discourse](https://discourse.mozilla-community.org/t/support-ublock-origin/6746).
Please stop opening invalid issues regarding the "Legacy" label of uBlock Origin on AMO, it's all explained in the [Release notes](https://github.com/gorhill/uBlock/releases).
For **support/discussions/help**, there is [/r/uBlockOrigin](https://www.reddit.com/r/uBlockOrigin/) on Reddit -- this is where I see the most activity for people helping each other regarding uBlock Origin.
For **filter-related issues**, report on the respective filter list support site, or at [uBlockOrigin/uAssets](https://github.com/uBlockOrigin/uAssets/issues). Use [the logger](https://github.com/gorhill/uBlock/wiki/The-logger) to diagnose/confirm filter-related issues. If something does not work properly with uBO enabled, the **first step** is to rule out filter-related issues.
Ignorance of the above rules is no excuse: **Opening an issue for purpose of support or discussion, or opening a filter-related issue will result in the user being immediately blocked.** Given the [amount of invalid issues being opened](https://github.com/gorhill/uBlock/issues?q=is%3Aissue+label%3Ainvalid+is%3Aclosed), I have no choice but to resort to such a drastic measure. You will still be able to open filter list issues at [uBlockOrigin/uAssets](https://github.com/uBlockOrigin/uAssets/issues).
**The issue tracker is for provable issues only:** You will have to make the case that the issue is really with uBlock Origin and not something else on your side. To make a case means to provide detailed steps so that anybody can reproduce the issue. Be sure to rule out that the issue is not caused by something specific on your side.
**The issue tracker is for provable issues only:** You will have to make the case that the issue is really with uBlock Origin and not something else on your side. To make a case means to provide detailed steps so that anybody can reproduce the issue. Be sure to rule out that the issue is not caused by something specific on your side. Specifically, _speculated_ performance issues will be marked as invalid and closed if they do not come with **actual profiling data + analysis** supporting the claim.
**Any issue opened without effort to provide the required details for me (or anybody else) to reproduce the problem will be closed as _invalid_.** If you provide more details thereafter for me to reproduce the issue, I will reopen it if I can confirm there is indeed an issue with uBlock Origin. Example of detailed steps:

View file

@ -1,10 +1,10 @@
[![Build](https://travis-ci.org/gorhill/uBlock.svg?branch=master)](https://travis-ci.org/gorhill/uBlock)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/ublock/localized.png)](https://crowdin.com/project/ublock)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/ublock/localized.svg)](https://crowdin.com/project/ublock)
[![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/gorhill/uBlock/blob/master/LICENSE.txt)
***
##### BEWARE! uBlock Origin is COMPLETELY UNRELATED to the web site ublock.org
##### BEWARE! uBlock Origin is (and has always been) COMPLETELY UNRELATED to the web site ublock.org
The donations sought by the [individual](https://github.com/chrisaljoudi/) behind `ublock.org` (_"to keeps uBlock development possible"_, [a misrepresentation](https://en.wikipedia.org/wiki/UBlock_Origin#uBlock_.28ublock.org.29)) are _not_ benefiting any of those who contributed most to create uBlock Origin ([developers](https://github.com/gorhill/uBlock/graphs/contributors), [translators](https://crowdin.com/project/ublock), and all those who put efforts in opening detailed issues). For the differences between uBlock Origin and uBlock, see the unbiased [Wikipedia article](https://en.wikipedia.org/wiki/UBlock_Origin).
@ -66,6 +66,8 @@ uBlock Origin
Visit the [uBlock Origin's wiki](https://github.com/gorhill/uBlock/wiki) for documentation.
For support/questions/help, there is [/r/uBlockOrigin](https://www.reddit.com/r/uBlockOrigin/) on Reddit.
## Philosophy
uBlock Origin (or uBlock₀) is not an *ad blocker*; it's a general-purpose blocker. uBlock₀ blocks ads through its support of the [Adblock Plus filter syntax](https://adblockplus.org/en/filters). uBlock₀ [extends](https://github.com/gorhill/uBlock/wiki/Filter-syntax-extensions) the syntax and is designed to work with custom rules and filters. Furthermore, advanced mode allows uBlock₀ to work in [default-deny mode](https://github.com/gorhill/uBlock/wiki/Dynamic-filtering:-default-deny), which mode will cause [all 3rd-party network requests](https://requestpolicycontinued.github.io/#what-are-cross-site-requests) to be blocked by default, unless allowed by the user.
@ -122,16 +124,12 @@ You can install the latest version [manually](https://github.com/gorhill/uBlock/
It is expected that uBlock Origin is compatible with any Chromium-based browsers.
**Important:** Chromium-based browsers do not relay [websocket connections](https://en.wikipedia.org/wiki/WebSocket) to the extension API. This means websites can use websocket connections to bypass uBO (or any other blocker). This can be remediated by installing uBO's companion extension [uBO-Extra](https://github.com/gorhill/uBO-Extra).
#### Firefox / Firefox for Android
[Firefox Add-ons web site](https://addons.mozilla.org/addon/ublock-origin/). There is also a development version if you want to test uBlock Origin with the latest changes: see [_uBlock Origin Version History_](https://addons.mozilla.org/addon/ublock-origin/versions/beta)
uBlock Origin is compatible with [SeaMonkey](http://www.seamonkey-project.org/), [Pale Moon](https://www.palemoon.org/), and possibly other browsers based on Firefox.
The Firefox version of uBlock Origin has [an extra feature](https://github.com/gorhill/uBlock/wiki/Inline-script-tag-filtering) currently not yet available on Chromium-based browsers -- which feature is of great help to foil attempts by many web sites to circumvent blockers.
Also of interest: [Deploying uBlock Origin for Firefox with CCK2 and Group Policy](http://decentsecurity.com/ublock-for-firefox-deployment/).
Thanks to Debian contributor [Sean Whitton](https://wiki.debian.org/SeanWhitton), users of Debian 9 or later or Ubuntu 16.04 or later may simply
@ -153,7 +151,7 @@ Development version available at <https://github.com/el1t/uBlock-Safari#ublock-o
#### Note for all browsers
To benefit from uBlock Origin's higher efficiency, it's advised that you don't use other inefficient blockers at the same time (such as AdBlock or Adblock Plus). uBlock₀ will do [as well or better](#blocking) than most popular ad blockers.
To benefit from uBlock Origin's higher efficiency, it's advised that you don't use other inefficient blockers at the same time (such as AdBlock or Adblock Plus). uBlock₀ will do [as well or better](#blocking) than most popular ad blockers. Other blockers can also prevent uBlock₀'s privacy or anti-blocker features from working properly.
## Release History

View file

@ -364,8 +364,8 @@
"off": true,
"title": "EST: Eesti saitidele kohandatud filter",
"lang": "et",
"contentURL": "http://adblock.ee/list.php",
"supportURL": "http://adblock.ee/"
"contentURL": "https://adblock.ee/list.php",
"supportURL": "https://adblock.ee/"
},
"EU-prebake": {
"content": "filters",

26
dist/README.md vendored
View file

@ -1,6 +1,6 @@
## INSTALL
#### Chromium
### Chromium
- Download and unzip `ublock0.chromium.zip` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).
- Rename the unzipped directory to `ublock`
@ -20,26 +20,42 @@ Remember that you have to update manually also. For some users, updating manuall
- You can update when **you** want
- If ever a new version sucks, you can easily just re-install the previous one
#### Firefox
### Firefox webext
Compatible with Firefox 52 and beyond.
- Download `ublock0.webext.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).
- Drag and drop the previously downloaded `ublock0.webext.xpi` into Firefox
On Linux, the settings are saved in a JSON file located at `~/.mozilla/firefox/[profile name]/browser-extension-data/uBlock0@raymondhill.net/storage.js`.
When you uninstall the extension, Firefox deletes that file, so all your settings are lost when you uninstall.
### Firefox legacy
Compatible with Firefox 24 to Firefox 56.
- Download `ublock0.firefox.xpi` ([latest release desirable](https://github.com/gorhill/uBlock/releases)).
- Drag and drop the previously downloaded `ublock0.firefox.xpi` into Firefox
With Firefox 43 and beyond, you may need to toggle the setting `xpinstall.signatures.required` to `false` in `about:config`.
Your uBlock Origin settings are kept intact even after you uninstall the addon.
On Linux, the settings are saved in a SQlite file located at `~/.mozilla/firefox/[profile name]/extension-data/ublock0.sqlite`.
On Windows, the settings are saved in a SQlite file located at `%APPDATA%\Mozilla\Firefox\Profiles\[profile name]\extension-data\ublock0.sqlite`.
#### Build instructions (for developers)
### Build instructions (for developers)
- Clone [uBlock](https://github.com/gorhill/uBlock) and [uAssets](https://github.com/uBlockOrigin/uAssets) repositories in the same parent directory
- Set path to uBlock: `cd uBlock`
- Optional: Select the version to build: `git checkout <tag>`
- Build the plugin:
- Chromium: `./tools/make-chromium.sh`
- Firefox: `./tools/make-firefox.sh all`
- Firefox webext: `./tools/make-webext.sh all`
- Firefox legacy: `./tools/make-firefox.sh all`
- Load the result of the build into your browser:
- Chromium: load the unpacked extension folder `/uBlock/dist/build/uBlock0.chromium/` in Chromium to use the extension.
- Firefox: drag-and-drop `/uBlock/dist/build/uBlock0.firefox.xpi` into Firefox.
- Firefox: drag-and-drop `/uBlock/dist/build/uBlock0.firefox.xpi` or `/uBlock/dist/build/uBlock0.webext.xpi` into Firefox.

View file

@ -1,4 +1,4 @@
Un bloquejador eficient: el consum de memòria i de processador és baix però, no obstant això, pot carregar i aplicar milers de filtres més que altres bloquejadors coneguts.
Un blocador eficient: Amb un consum discret de memòria i de processador, pot carregar i aplicar milers de filtres més que altres aplicacions semblants.
Gràfic de l'eficiència: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared

View file

@ -1,4 +1,4 @@
Ефикасан блокатор: ниски процесорски и меморијски захтеви и може учитати и применити хиљаде филтера више него остали популарни блокатори.
Ефикасан блокатор: ниски процесорски и меморијски захтеви а може учитати и применити хиљаде филтера више него остали популарни блокатори.
Илустровани преглед његове ефикасности: https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared
@ -6,7 +6,7 @@
***
Флексибилан је, више је од блокатора реклама: може читати и стварати филтере из хост датотека.
Флексибилан је, више је од блокатора реклама: може читати и креирати филтере из хост датотека.
Одмах по инсталирању, следећи спискови филтера су учитани и спроведени:
@ -30,7 +30,7 @@
***
Без предефинисаних спискова филтера, ово проширење је ништа. Тако да ако икад желите да допринесете нешто, размислите о људима који напорно раде одржавајући спискове филтера које користите и који су доступни за бесплатно коришћење свима.
Без предефинисаних спискова филтера, ово проширење је ништа. Тако да ако икад желите да допринесете нечим, размислите о људима који напорно раде одржавајући спискове филтера које користите и који су доступни за бесплатно коришћење свима.
***
@ -45,5 +45,5 @@
Ово је рана верзија, имајте то на уму када будете оцењивали.
Листа измена:
Евиденција промена:
https://github.com/gorhill/uBlock/releases

View file

@ -1,13 +1,13 @@
Etkili bir engelleyici: Belleği ve işlemciyi yormaz, yine de diğer popüler engelleyicilere göre binlerce daha fazla süzgeci yükleyip uygulayabilir.
Etkili bir engelleyici: Belleği ve işlemciyi yormaz, yine de diğer popüler engelleyicilere göre binlerce daha çok süzgeci yükleyip uygulayabilir.
Verimliliğine örneklendirilmiş genel bakış:
https://github.com/gorhill/uBlock/wiki/uBlock-vs.-ABP:-efficiency-compared
Kullanımı: Arayüzdeki büyük güç düğmesi mevcut web sitesinde uBlock'u kalıcı olarak etkisiz/etkin kılmak içindir. Bu yalnızca mevcut web sitesine uygulanır, evrensel bir güç düğmesi değildir.
Kullanımı: Arayüzdeki büyük güç düğmesi o anki web sitesinde, uBlock'u kalıcı olarak devre dışı bırakmak/etkinleştirmek içindir. Bu yalnızca o anki web sitesine uygulanır, evrensel bir güç düğmesi değildir.
***
Esnek, bir "reklam engelleyici"den daha fazlası: Alan adları dosyalarınızdan süzgeçleri okuyabilir ve oluşturabilir.
Esnek, bir "reklam engelleyici"den daha fazlası: Ayrıca alan adları dosyalarınızdan süzgeçleri okuyabilir ve oluşturabilir.
Hazır olarak şu süzgeç listeleri yüklüdür ve uygulanır:
@ -23,21 +23,21 @@ Hazır olarak şu süzgeç listeleri yüklüdür ve uygulanır:
- hpHosts'un Reklam ve izleyici sunucuları
- MVPS HOSTS
- Spam404
- Ve daha birçoğu
- Ve daha başkaları
Tabii ki, daha fazla süzgeç etkinleştirildikçe, bellek kullanımı da yükselir. Ama, Fanboy'un iki ekstra listesi, hpHosts'un reklam ve izleyici sunucuları ekledikten sonra dahi uBlock diğer oldukça popüler olan engelleyicilere göre daha az bellek kullanır.
Elbette, daha çok süzgeç etkinleştirildikçe, bellek kullanımı da artar. Yine de, Fanboy'un iki ekstra listesi, hpHosts'un reklam ve izleyici sunucuları ekledikten sonra bile uBlock diğer oldukça popüler engelleyicilere göre daha az bellek kullanır.
Ayrıca, bazı ekstra listelerin seçilmesinin web sitelerinin bozulması olasılığını artırabileceğini unutmayın -- özellikle normalde alan adları dosyası olarak kullanılan listelerin.
***
Ön yüklü gelen süzgeç listeleri olmadan, bu eklenti hiçbir işe yaramaz. Eğer gerçekten bir şekilde katkıda bulunmak isterseniz, herkes tarafından özgürce kullanıma imkan veren, kullandığınız süzgeç listelerini oluşturmak için uğraşan insanları düşünün.
Ön yüklü gelen süzgeç listeleri olmadan, bu eklenti bir işe yaramaz. Bu yüzden, gerçekten bir şekilde katkıda bulunmak isterseniz, herkesin özgürce kullanması için sunulan kullandığınız süzgeç listelerini oluşturmak için uğraşan insanları düşünün.
***
Özgür.
ık kaynak kamu lisanslı (GPLv3)
Kullanıcılar tarafından kullanıcılar için.
Kullanıcılardan kullanıcılara.
Katkıda bulunanlar @ Github: https://github.com/gorhill/uBlock/graphs/contributors
Katkıda bulunanlar @ Crowdin: https://crowdin.net/project/ublock

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script async src="js/is-webrtc-supported.js"></script>
</head>

View file

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "uBlock Origin",
"version": "1.13.4",
"version": "1.14.8",
"commands": {
"launch-element-zapper": {

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="js/vapi-client.js"></script>
<script src="js/options_ui.js"></script>
<title></title>

View file

@ -47,6 +47,22 @@ var noopFunc = function(){};
/******************************************************************************/
if (
typeof browser === 'object' &&
browser !== null &&
browser.runtime instanceof Object &&
typeof browser.runtime.getBrowserInfo === 'function'
) {
browser.runtime.getBrowserInfo().then(function(info) {
vAPI.supportsUserStylesheets =
info.name === 'Firefox' &&
parseInt(info.version, 10) > 52;
});
}
/******************************************************************************/
vAPI.app = {
name: manifest.name,
version: manifest.version
@ -327,7 +343,9 @@ vAPI.tabs.registerListeners = function() {
};
var onActivated = function(details) {
vAPI.contextMenu.onMustUpdate(details.tabId);
if ( vAPI.contextMenu instanceof Object ) {
vAPI.contextMenu.onMustUpdate(details.tabId);
}
};
var onUpdated = function(tabId, changeInfo, tab) {
@ -478,12 +496,12 @@ vAPI.tabs.open = function(details) {
var targetURLWithoutHash = pos === -1 ? targetURL : targetURL.slice(0, pos);
chrome.tabs.query({ url: targetURLWithoutHash }, function(tabs) {
var tab = tabs[0];
if ( chrome.runtime.lastError ) { /* noop */ }
var tab = Array.isArray(tabs) && tabs[0];
if ( !tab ) {
wrapper();
return;
}
var _details = {
active: true,
url: undefined
@ -608,32 +626,71 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
// Since we may be called asynchronously, the tab id may not exist
// anymore, so this ensures it does still exist.
vAPI.setIcon = function(tabId, iconStatus, badge) {
tabId = toChromiumTabId(tabId);
if ( tabId === 0 ) {
return;
}
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/browserAction#Browser_compatibility
// Firefox for Android does no support browser.browserAction.setIcon().
var onIconReady = function() {
if ( vAPI.lastError() ) {
return;
vAPI.setIcon = (function() {
var browserAction = chrome.browserAction,
titleTemplate = chrome.runtime.getManifest().name + ' ({badge})';
var iconPaths = [
{
'19': 'img/browsericons/icon19-off.png',
'38': 'img/browsericons/icon38-off.png'
},
{
'19': 'img/browsericons/icon19.png',
'38': 'img/browsericons/icon38.png'
}
chrome.browserAction.setBadgeText({ tabId: tabId, text: badge });
if ( badge !== '' ) {
chrome.browserAction.setBadgeBackgroundColor({
];
return function(tabId, iconStatus, badge) {
tabId = toChromiumTabId(tabId);
if ( tabId === 0 ) { return; }
if ( browserAction.setIcon !== undefined ) {
browserAction.setIcon(
{
tabId: tabId,
path: iconPaths[iconStatus === 'on' ? 1 : 0]
},
function onIconReady() {
if ( vAPI.lastError() ) { return; }
chrome.browserAction.setBadgeText({
tabId: tabId,
text: badge
});
if ( badge !== '' ) {
chrome.browserAction.setBadgeBackgroundColor({
tabId: tabId,
color: '#666'
});
}
}
);
}
if ( browserAction.setTitle !== undefined ) {
browserAction.setTitle({
tabId: tabId,
color: '#666'
title: titleTemplate.replace(
'{badge}',
iconStatus === 'on' ? (badge !== '' ? badge : '0') : 'off'
)
});
}
if ( vAPI.contextMenu instanceof Object ) {
vAPI.contextMenu.onMustUpdate(tabId);
}
};
})();
var iconPaths = iconStatus === 'on' ?
{ '19': 'img/browsericons/icon19.png', '38': 'img/browsericons/icon38.png' } :
{ '19': 'img/browsericons/icon19-off.png', '38': 'img/browsericons/icon38-off.png' };
chrome.browserAction.setIcon({ tabId: tabId, path: iconPaths }, onIconReady);
vAPI.contextMenu.onMustUpdate(tabId);
};
chrome.browserAction.onClicked.addListener(function(tab) {
vAPI.tabs.open({
select: true,
url: 'popup.html?tabId=' + tab.id + '&mobile=1'
});
});
/******************************************************************************/
/******************************************************************************/
@ -655,8 +712,8 @@ vAPI.messaging.listen = function(listenerName, callback) {
/******************************************************************************/
vAPI.messaging.onPortMessage = (function() {
var messaging = vAPI.messaging;
var toAuxPending = {};
var messaging = vAPI.messaging,
toAuxPending = {};
// Use a wrapper to avoid closure and to allow reuse.
var CallbackWrapper = function(port, request, timeout) {
@ -703,8 +760,8 @@ vAPI.messaging.onPortMessage = (function() {
};
var toAux = function(details, portFrom) {
var port, portTo;
var chromiumTabId = toChromiumTabId(details.toTabId);
var port, portTo,
chromiumTabId = toChromiumTabId(details.toTabId);
// TODO: This could be an issue with a lot of tabs: easy to address
// with a port name to tab id map.
@ -761,6 +818,32 @@ vAPI.messaging.onPortMessage = (function() {
wrapper.callback(details.msg);
};
var toFramework = function(msg, sender) {
var tabId = sender && sender.tab && sender.tab.id;
if ( !tabId ) { return; }
switch ( msg.what ) {
case 'userCSS':
var details = {
code: undefined,
frameId: sender.frameId,
matchAboutBlank: true
};
if ( vAPI.supportsUserStylesheets === true ) {
details.cssOrigin = 'user';
}
if ( msg.toRemove ) {
details.code = msg.toRemove;
chrome.tabs.removeCSS(tabId, details);
}
if ( msg.toAdd ) {
details.code = msg.toAdd;
details.runAt = 'document_start';
chrome.tabs.insertCSS(tabId, details);
}
break;
}
};
return function(request, port) {
// Auxiliary process to auxiliary process
if ( request.toTabId !== undefined ) {
@ -774,6 +857,13 @@ vAPI.messaging.onPortMessage = (function() {
return;
}
// Content process to main process: framework handler.
// No callback supported/needed for now.
if ( request.channelName === 'vapi-background' ) {
toFramework(request.msg, port.sender);
return;
}
// Auxiliary process to main process: prepare response
var callback = messaging.NOOPFUNC;
if ( request.auxProcessId !== undefined ) {
@ -781,8 +871,8 @@ vAPI.messaging.onPortMessage = (function() {
}
// Auxiliary process to main process: specific handler
var r = messaging.UNHANDLED;
var listener = messaging.listeners[request.channelName];
var r = messaging.UNHANDLED,
listener = messaging.listeners[request.channelName];
if ( typeof listener === 'function' ) {
r = listener(request.msg, port.sender, callback);
}
@ -856,296 +946,10 @@ vAPI.messaging.broadcast = function(message) {
/******************************************************************************/
/******************************************************************************/
vAPI.net = {};
// https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenus#Browser_compatibility
// Firefox for Android does no support browser.contextMenus.
/******************************************************************************/
vAPI.net.registerListeners = function() {
var µb = µBlock,
µburi = µb.URI,
wrApi = chrome.webRequest;
// https://bugs.chromium.org/p/chromium/issues/detail?id=410382
// Between Chromium 38-48, plug-ins' network requests were reported as
// type "other" instead of "object".
var is_v38_48 = /\bChrom[a-z]+\/(?:3[89]|4[0-8])\.[\d.]+\b/.test(navigator.userAgent);
// legacy Chromium understands 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
};
// modern Chromium/WebExtensions: more types available.
if ( wrApi.ResourceType ) {
(function() {
for ( var typeKey in wrApi.ResourceType ) {
if ( wrApi.ResourceType.hasOwnProperty(typeKey) ) {
validTypes[wrApi.ResourceType[typeKey]] = true;
}
}
})();
}
var extToTypeMap = new Map([
['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'],
['mp3','media'],['mp4','media'],['webm','media'],
['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image']
]);
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[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) {
details.tabId = details.tabId.toString();
var type = details.type;
// https://github.com/gorhill/uBlock/issues/1493
// Chromium 49+/WebExtensions support a new request type: `ping`,
// which is fired as a result of using `navigator.sendBeacon`.
if ( type === 'ping' ) {
details.type = 'beacon';
return;
}
if ( type === 'imageset' ) {
details.type = 'image';
return;
}
// The rest of the function code is to normalize type
if ( type !== 'other' ) {
return;
}
// Try to map known "extension" part of URL to request type.
var path = µburi.pathFromURI(details.url),
pos = path.indexOf('.', path.length - 6);
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) {
details.type = type;
return;
}
// Try to extract type from response headers if present.
if ( details.responseHeaders ) {
type = headerValue(details.responseHeaders, 'content-type');
if ( type.startsWith('font/') ) {
details.type = 'font';
return;
}
if ( type.startsWith('image/') ) {
details.type = 'image';
return;
}
if ( type.startsWith('audio/') || type.startsWith('video/') ) {
details.type = 'media';
return;
}
}
// https://github.com/chrisaljoudi/uBlock/issues/862
// If no transposition possible, transpose to `object` as per
// Chromium bug 410382
// https://code.google.com/p/chromium/issues/detail?id=410382
if ( is_v38_48 ) {
details.type = 'object';
}
};
// https://bugs.chromium.org/p/chromium/issues/detail?id=129353
// https://github.com/gorhill/uBlock/issues/1497
// Expose websocket-based network requests to uBO's filtering engine,
// logger, etc.
// Counterpart of following block of code is found in "vapi-client.js" --
// search for "https://github.com/gorhill/uBlock/issues/1497".
//
// Once uBO 1.11.1 and uBO-Extra 2.12 are widespread, the image-based
// handling code can be removed.
var onBeforeWebsocketRequest = function(details) {
if ( (details.type !== 'image') &&
(details.method !== 'HEAD' || details.type !== 'xmlhttprequest')
) {
return;
}
var requestURL = details.url,
matches = /[?&]u(?:rl)?=([^&]+)/.exec(requestURL);
if ( matches === null ) { return; }
details.type = 'websocket';
details.url = decodeURIComponent(matches[1]);
var r = onBeforeRequestClient(details);
if ( r && r.cancel ) { return r; }
// Redirect to the provided URL, or a 1x1 data: URI if none provided.
matches = /[?&]r=([^&]+)/.exec(requestURL);
return {
redirectUrl: matches !== null ?
decodeURIComponent(matches[1]) :
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
};
};
var onBeforeRequestClient = this.onBeforeRequest.callback;
var onBeforeRequest = validTypes.websocket
// modern Chromium/WebExtensions: type 'websocket' is supported
? function(details) {
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
}
// legacy Chromium
: function(details) {
// https://github.com/gorhill/uBlock/issues/1497
if ( details.url.endsWith('ubofix=f41665f3028c7fd10eecf573336216d3') ) {
var r = onBeforeWebsocketRequest(details);
if ( r !== undefined ) { return r; }
}
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
};
// This is needed for Chromium 49-55.
var onBeforeSendHeaders = validTypes.csp_report
// modern Chromium/WebExtensions: type 'csp_report' is supported
? null
// legacy Chromium
: function(details) {
if ( details.type !== 'ping' || details.method !== 'POST' ) { return; }
var type = headerValue(details.requestHeaders, 'content-type');
if ( type === '' ) { return; }
if ( type.endsWith('/csp-report') ) {
details.type = 'csp_report';
return onBeforeRequestClient(details);
}
};
var onHeadersReceivedClient = this.onHeadersReceived.callback,
onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0),
onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
var onHeadersReceived = validTypes.font
// modern Chromium/WebExtensions: type 'font' is supported
? function(details) {
normalizeRequestDetails(details);
if (
onHeadersReceivedClientTypes.length !== 0 &&
onHeadersReceivedClientTypes.indexOf(details.type) === -1
) {
return;
}
return onHeadersReceivedClient(details);
}
// legacy Chromium
: function(details) {
normalizeRequestDetails(details);
// Hack to work around Chromium API limitations, where requests of
// type `font` are returned as `other`. For example, our normalization
// fail at transposing `other` into `font` for URLs which are outside
// what is expected. At least when headers are received we can check
// for content type `font/*`. Blocking at onHeadersReceived time is
// less worse than not blocking at all. Also, due to Chromium bug,
// `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 === '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);
};
var urls, types;
if ( onBeforeRequest ) {
urls = this.onBeforeRequest.urls || ['<all_urls>'];
types = this.onBeforeRequest.types || undefined;
if (
(validTypes.websocket) &&
(types === undefined || types.indexOf('websocket') !== -1) &&
(urls.indexOf('<all_urls>') === -1)
) {
if ( urls.indexOf('ws://*/*') === -1 ) {
urls.push('ws://*/*');
}
if ( urls.indexOf('wss://*/*') === -1 ) {
urls.push('wss://*/*');
}
}
wrApi.onBeforeRequest.addListener(
onBeforeRequest,
{ urls: urls, types: types },
this.onBeforeRequest.extra
);
}
// Chromium 48 and lower does not support `ping` type.
// Chromium 56 and higher does support `csp_report` stype.
if ( onBeforeSendHeaders ) {
wrApi.onBeforeSendHeaders.addListener(
onBeforeSendHeaders,
{
'urls': [ '<all_urls>' ],
'types': [ 'ping' ]
},
[ 'blocking', 'requestHeaders' ]
);
}
if ( onHeadersReceived ) {
urls = this.onHeadersReceived.urls || ['<all_urls>'];
types = onHeadersReceivedTypes;
wrApi.onHeadersReceived.addListener(
onHeadersReceived,
{ urls: urls, types: types },
this.onHeadersReceived.extra
);
}
};
/******************************************************************************/
/******************************************************************************/
vAPI.contextMenu = {
vAPI.contextMenu = chrome.contextMenus && {
_callback: null,
_entries: [],
_createEntry: function(entry) {
@ -1271,7 +1075,7 @@ vAPI.punycodeURL = function(url) {
// https://github.com/gorhill/uBlock/issues/900
// Also, UC Browser: http://www.upsieutoc.com/image/WXuH
vAPI.adminStorage = {
vAPI.adminStorage = chrome.storage.managed && {
getItem: function(key, callback) {
var onRead = function(store) {
var data;
@ -1317,7 +1121,7 @@ vAPI.cloud = (function() {
var options = {
defaultDeviceName: window.navigator.platform,
deviceName: window.localStorage.getItem('deviceName') || ''
deviceName: vAPI.localStorage.getItem('deviceName') || ''
};
// This is used to find out a rough count of how many chunks exists:
@ -1461,7 +1265,7 @@ vAPI.cloud = (function() {
}
if ( typeof details.deviceName === 'string' ) {
window.localStorage.setItem('deviceName', details.deviceName);
vAPI.localStorage.setItem('deviceName', details.deviceName);
options.deviceName = details.deviceName;
}

View file

@ -72,70 +72,6 @@ if ( vAPI.sessionId ) {
/******************************************************************************/
var referenceCounter = 0;
vAPI.lock = function() {
referenceCounter += 1;
};
vAPI.unlock = function() {
referenceCounter -= 1;
if ( referenceCounter === 0 ) {
// Eventually there will be code here to flush the javascript code
// from this file out of memory when it ends up unused.
}
};
/******************************************************************************/
vAPI.executionCost = {
start: function(){},
stop: function(){}
};
/*
vAPI.executionCost = {
tcost: 0,
tstart: 0,
nstart: 0,
level: 1,
start: function() {
if ( this.nstart === 0 ) {
this.tstart = window.performance.now();
}
this.nstart += 1;
},
stop: function(mark) {
this.nstart -= 1;
if ( this.nstart !== 0 ) {
return;
}
var tcost = window.performance.now() - this.tstart;
this.tcost += tcost;
if ( mark === undefined ) {
return;
}
var top = window === window.top;
if ( !top && this.level < 2 ) {
return;
}
var context = window === window.top ? ' top' : 'frame';
var percent = this.tcost / window.performance.now() * 100;
console.log(
'uBO cost (%s): %sms/%s%% (%s: %sms)',
context,
this.tcost.toFixed(1),
percent.toFixed(1),
mark,
tcost.toFixed(2)
);
}
};
*/
vAPI.executionCost.start();
/******************************************************************************/
vAPI.randomToken = function() {
return String.fromCharCode(Date.now() % 26 + 97) +
Math.floor(Math.random() * 982451653 + 982451653).toString(36);
@ -412,8 +348,6 @@ vAPI.shutdown.add(function() {
// https://www.youtube.com/watch?v=rT5zCHn0tsg
// https://www.youtube.com/watch?v=E-jS4e3zacI
vAPI.executionCost.stop('vapi-client.js');
/******************************************************************************/
/******************************************************************************/

View file

@ -67,12 +67,6 @@ vAPI.download = function(details) {
/******************************************************************************/
vAPI.insertHTML = function(node, html) {
node.innerHTML = html;
};
/******************************************************************************/
vAPI.getURL = chrome.runtime.getURL;
/******************************************************************************/
@ -100,6 +94,26 @@ try {
} catch (ex) {
}
// https://github.com/gorhill/uBlock/issues/2824
// Use a dummy localStorage if for some reasons it's not available.
if ( vAPI.localStorage instanceof Object === false ) {
vAPI.localStorage = {
length: 0,
clear: function() {
},
getItem: function() {
return null;
},
key: function() {
throw new RangeError();
},
removeItem: function() {
},
setItem: function() {
}
};
}
/******************************************************************************/
})(this);

View file

@ -0,0 +1,313 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017 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
*/
// For background page
'use strict';
/******************************************************************************/
vAPI.net = {};
vAPI.net.registerListeners = function() {
var µb = µBlock,
µburi = µb.URI,
wrApi = chrome.webRequest;
// https://bugs.chromium.org/p/chromium/issues/detail?id=410382
// Between Chromium 38-48, plug-ins' network requests were reported as
// type "other" instead of "object".
var is_v38_48 = /\bChrom[a-z]+\/(?:3[89]|4[0-8])\.[\d.]+\b/.test(navigator.userAgent);
// legacy Chromium understands 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
};
// modern Chromium/WebExtensions: more types available.
if ( wrApi.ResourceType ) {
(function() {
for ( var typeKey in wrApi.ResourceType ) {
if ( wrApi.ResourceType.hasOwnProperty(typeKey) ) {
validTypes[wrApi.ResourceType[typeKey]] = true;
}
}
})();
}
var extToTypeMap = new Map([
['eot','font'],['otf','font'],['svg','font'],['ttf','font'],['woff','font'],['woff2','font'],
['mp3','media'],['mp4','media'],['webm','media'],
['gif','image'],['ico','image'],['jpeg','image'],['jpg','image'],['png','image'],['webp','image']
]);
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[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) {
details.tabId = details.tabId.toString();
var type = details.type;
// https://github.com/gorhill/uBlock/issues/1493
// Chromium 49+/WebExtensions support a new request type: `ping`,
// which is fired as a result of using `navigator.sendBeacon`.
if ( type === 'ping' ) {
details.type = 'beacon';
return;
}
if ( type === 'imageset' ) {
details.type = 'image';
return;
}
// The rest of the function code is to normalize type
if ( type !== 'other' ) {
return;
}
// Try to map known "extension" part of URL to request type.
var path = µburi.pathFromURI(details.url),
pos = path.indexOf('.', path.length - 6);
if ( pos !== -1 && (type = extToTypeMap.get(path.slice(pos + 1))) ) {
details.type = type;
return;
}
// Try to extract type from response headers if present.
if ( details.responseHeaders ) {
type = headerValue(details.responseHeaders, 'content-type');
if ( type.startsWith('font/') ) {
details.type = 'font';
return;
}
if ( type.startsWith('image/') ) {
details.type = 'image';
return;
}
if ( type.startsWith('audio/') || type.startsWith('video/') ) {
details.type = 'media';
return;
}
}
// https://github.com/chrisaljoudi/uBlock/issues/862
// If no transposition possible, transpose to `object` as per
// Chromium bug 410382
// https://code.google.com/p/chromium/issues/detail?id=410382
if ( is_v38_48 ) {
details.type = 'object';
}
};
// https://bugs.chromium.org/p/chromium/issues/detail?id=129353
// https://github.com/gorhill/uBlock/issues/1497
// Expose websocket-based network requests to uBO's filtering engine,
// logger, etc.
// Counterpart of following block of code is found in "vapi-client.js" --
// search for "https://github.com/gorhill/uBlock/issues/1497".
//
// Once uBO 1.11.1 and uBO-Extra 2.12 are widespread, the image-based
// handling code can be removed.
var onBeforeWebsocketRequest = function(details) {
if ( (details.type !== 'image') &&
(details.method !== 'HEAD' || details.type !== 'xmlhttprequest')
) {
return;
}
var requestURL = details.url,
matches = /[?&]u(?:rl)?=([^&]+)/.exec(requestURL);
if ( matches === null ) { return; }
details.type = 'websocket';
details.url = decodeURIComponent(matches[1]);
var r = onBeforeRequestClient(details);
if ( r && r.cancel ) { return r; }
// Redirect to the provided URL, or a 1x1 data: URI if none provided.
matches = /[?&]r=([^&]+)/.exec(requestURL);
return {
redirectUrl: matches !== null ?
decodeURIComponent(matches[1]) :
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
};
};
var onBeforeRequestClient = this.onBeforeRequest.callback;
var onBeforeRequest = validTypes.websocket
// modern Chromium/WebExtensions: type 'websocket' is supported
? function(details) {
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
}
// legacy Chromium
: function(details) {
// https://github.com/gorhill/uBlock/issues/1497
if ( details.url.endsWith('ubofix=f41665f3028c7fd10eecf573336216d3') ) {
var r = onBeforeWebsocketRequest(details);
if ( r !== undefined ) { return r; }
}
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
};
// This is needed for Chromium 49-55.
var onBeforeSendHeaders = validTypes.csp_report
// modern Chromium/WebExtensions: type 'csp_report' is supported
? null
// legacy Chromium
: function(details) {
if ( details.type !== 'ping' || details.method !== 'POST' ) { return; }
var type = headerValue(details.requestHeaders, 'content-type');
if ( type === '' ) { return; }
if ( type.endsWith('/csp-report') ) {
details.type = 'csp_report';
return onBeforeRequestClient(details);
}
};
var onHeadersReceivedClient = this.onHeadersReceived.callback,
onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0),
onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
var onHeadersReceived = validTypes.font
// modern Chromium/WebExtensions: type 'font' is supported
? function(details) {
normalizeRequestDetails(details);
if (
onHeadersReceivedClientTypes.length !== 0 &&
onHeadersReceivedClientTypes.indexOf(details.type) === -1
) {
return;
}
return onHeadersReceivedClient(details);
}
// legacy Chromium
: function(details) {
normalizeRequestDetails(details);
// Hack to work around Chromium API limitations, where requests of
// type `font` are returned as `other`. For example, our normalization
// fail at transposing `other` into `font` for URLs which are outside
// what is expected. At least when headers are received we can check
// for content type `font/*`. Blocking at onHeadersReceived time is
// less worse than not blocking at all. Also, due to Chromium bug,
// `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 === '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);
};
var urls, types;
if ( onBeforeRequest ) {
urls = this.onBeforeRequest.urls || ['<all_urls>'];
types = this.onBeforeRequest.types || undefined;
if (
(validTypes.websocket) &&
(types === undefined || types.indexOf('websocket') !== -1) &&
(urls.indexOf('<all_urls>') === -1)
) {
if ( urls.indexOf('ws://*/*') === -1 ) {
urls.push('ws://*/*');
}
if ( urls.indexOf('wss://*/*') === -1 ) {
urls.push('wss://*/*');
}
}
wrApi.onBeforeRequest.addListener(
onBeforeRequest,
{ urls: urls, types: types },
this.onBeforeRequest.extra
);
}
// Chromium 48 and lower does not support `ping` type.
// Chromium 56 and higher does support `csp_report` stype.
if ( onBeforeSendHeaders ) {
wrApi.onBeforeSendHeaders.addListener(
onBeforeSendHeaders,
{
'urls': [ '<all_urls>' ],
'types': [ 'ping' ]
},
[ 'blocking', 'requestHeaders' ]
);
}
if ( onHeadersReceived ) {
urls = this.onHeadersReceived.urls || ['<all_urls>'];
types = onHeadersReceivedTypes;
wrApi.onHeadersReceived.addListener(
onHeadersReceived,
{ urls: urls, types: types },
this.onHeadersReceived.extra
);
}
};
/******************************************************************************/

View file

@ -21,7 +21,7 @@
<Description>
<em:id>{{ec8030f7-c20a-464f-9b0e-13a3a9e97384}}</em:id>
<em:minVersion>24.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:maxVersion>57.0a1</em:maxVersion>
</Description>
</em:targetApplication>
@ -30,7 +30,7 @@
<Description>
<em:id>{{aa3c5121-dab2-40e2-81ca-7ea25febc110}}</em:id>
<em:minVersion>27.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:maxVersion>57.0a1</em:maxVersion>
</Description>
</em:targetApplication>

View file

@ -680,6 +680,12 @@ var winWatcher = (function() {
if ( !win || windowToIdMap.delete(win) !== true ) {
return;
}
// https://github.com/uBlockOrigin/uAssets/issues/567
// We need to cleanup if and only if the window being closed is
// the actual top window.
if ( win.gBrowser && win.gBrowser.ownerGlobal !== win ) {
return;
}
if ( typeof api.onCloseWindow === 'function' ) {
api.onCloseWindow(win);
}
@ -3125,6 +3131,7 @@ vAPI.toolbarButton = {
'#' + this.viewId + ',',
'#' + this.viewId + ' > iframe {',
'height: 290px;',
'max-width: none !important;',
'min-width: 0 !important;',
'overflow: hidden !important;',
'padding: 0 !important;',

View file

@ -54,63 +54,6 @@ var vAPI = self.vAPI;
/******************************************************************************/
var referenceCounter = 0;
vAPI.lock = function() {
referenceCounter += 1;
};
vAPI.unlock = function() {
referenceCounter -= 1;
};
/******************************************************************************/
vAPI.executionCost = {
start: function(){},
stop: function(){}
};
/*
vAPI.executionCost = vAPI.executionCost || {
tcost: 0,
tstart: 0,
nstart: 0,
level: 1,
start: function() {
if ( this.nstart === 0 ) {
this.tstart = window.performance.now();
}
this.nstart += 1;
},
stop: function(mark) {
this.nstart -= 1;
if ( this.nstart !== 0 ) {
return;
}
var tcost = window.performance.now() - this.tstart;
this.tcost += tcost;
if ( mark === undefined ) {
return;
}
var top = window === window.top;
if ( !top && this.level < 2 ) {
return;
}
var context = window === window.top ? ' top' : 'frame';
var percent = this.tcost / window.performance.now() * 100;
console.log(
'uBO cost (' + context + '): ' +
this.tcost.toFixed(1) + 'ms/' +
percent.toFixed(1) + '% (' +
mark + ': ' + tcost.toFixed(2) + 'ms)'
);
}
};
*/
vAPI.executionCost.start();
/******************************************************************************/
vAPI.firefox = true;
vAPI.randomToken = function() {
@ -498,10 +441,6 @@ if ( window !== window.top ) {
/******************************************************************************/
vAPI.executionCost.stop('vapi-client.js');
/******************************************************************************/
})(this);
/******************************************************************************/

View file

@ -75,30 +75,6 @@ vAPI.download = function(details) {
/******************************************************************************/
vAPI.insertHTML = (function() {
const parser = Components.classes['@mozilla.org/parserutils;1']
.getService(Components.interfaces.nsIParserUtils);
// https://github.com/gorhill/uBlock/issues/845
// Apparently dashboard pages execute with `about:blank` principal.
return function(node, html) {
while ( node.firstChild ) {
node.removeChild(node.firstChild);
}
node.appendChild(parser.parseFragment(
html,
parser.SanitizerAllowStyle,
false,
Services.io.newURI('about:blank', null, null),
document.documentElement
));
};
})();
/******************************************************************************/
vAPI.getURL = function(path) {
return 'chrome://' + location.host + '/content/' + path.replace(/^\/+/, '');
};

View file

@ -1,16 +1,5 @@
{
"manifest_version": 2,
"name": "uBlock Origin",
"version": "1.9.15.101",
"default_locale": "en",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
},
"author": "All uBlock Origin contributors",
"browser_action": {
"default_icon": {
"19": "img/browsericons/icon19.png",
@ -19,11 +8,20 @@
"default_title": "uBlock Origin",
"default_popup": "popup.html"
},
"author": "All uBlock Origin contributors",
"background": {
"page": "background.html"
},
"commands": {
"launch-element-zapper": {
"description": "__MSG_popupTipZapper__"
},
"launch-element-picker": {
"description": "__MSG_popupTipPicker__"
},
"launch-logger": {
"description": "__MSG_popupTipLog__"
}
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
@ -38,8 +36,16 @@
"all_frames": false
}
],
"default_locale": "en",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
},
"incognito": "split",
"minimum_chrome_version": "38.0",
"manifest_version": 2,
"minimum_opera_version": "25.0",
"name": "uBlock Origin",
"optional_permissions": [ "file:///*" ],
"options_page": "dashboard.html",
"permissions": [
@ -53,5 +59,6 @@
"webRequestBlocking",
"<all_urls>"
],
"short_name": "uBlock₀"
"short_name": "uBlock₀",
"version": "1.9.15.101"
}

View file

@ -10,6 +10,7 @@
<script src="lib/publicsuffixlist.js"></script>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-background.js"></script>
<script src="js/vapi-cachestorage.js"></script><!-- Optional -->
<script src="js/background.js"></script>
<script src="js/utils.js"></script>
<script src="js/uritools.js"></script>

View file

@ -29,8 +29,8 @@ const hostName = 'ublock0';
/******************************************************************************/
function startup({ webExtension }) {
webExtension.startup().then(api => {
function startup({ webExtension }, reason) {
webExtension.startup(reason).then(api => {
let { browser } = api,
storageMigrator;
let onMessage = function(message, sender, callback) {

View file

@ -27,21 +27,31 @@
(function() {
let µb = µBlock;
let migratedKeys = new Set();
let reCacheStorageKeys = /^(?:assetCacheRegistry|assetSourceRegistry|cache\/.+|selfie)$/;
let migrateAll = function(callback) {
let mustRestart = false;
let migrateKeyValue = function(details, callback) {
// https://github.com/gorhill/uBlock/issues/2653
// Be ready to deal graciously with corrupted DB.
if ( migratedKeys.has(details.key) ) {
callback();
return;
}
migratedKeys.add(details.key);
let bin = {};
bin[details.key] = JSON.parse(details.value);
self.browser.storage.local.set(bin, callback);
mustRestart = true;
if ( reCacheStorageKeys.test(details.key) ) {
vAPI.cacheStorage.set(bin, callback);
} else {
vAPI.storage.set(bin, callback);
}
};
let migrateNext = function() {
self.browser.runtime.sendMessage({ what: 'webext:storageMigrateNext' }, response => {
if ( response.key === undefined ) {
if ( mustRestart ) {
if ( migratedKeys.size !== 0 ) {
self.browser.runtime.reload();
} else {
callback();
@ -57,7 +67,7 @@
self.browser.runtime.sendMessage({ what: 'webext:storageMigrateDone' });
return callback();
}
self.browser.storage.local.set({ legacyStorageMigrated: true });
vAPI.storage.set({ legacyStorageMigrated: true });
migrateNext();
});
};

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>uBlock0-webext@raymondhill.net</em:id>
<em:id>uBlock0@raymondhill.net</em:id>
<em:version>{version}</em:version>
<em:name>{name}</em:name>
<em:description>{description}</em:description>
@ -20,8 +20,8 @@
<em:targetApplication>
<Description>
<em:id>{{ec8030f7-c20a-464f-9b0e-13a3a9e97384}}</em:id>
<em:minVersion>52.0a1</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:minVersion>54.0</em:minVersion>
<em:maxVersion>56.*</em:maxVersion>
</Description>
</em:targetApplication>
@ -30,7 +30,7 @@
<Description>
<em:id>{{aa3c5121-dab2-40e2-81ca-7ea25febc110}}</em:id>
<em:minVersion>54.0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:maxVersion>56.*</em:maxVersion>
</Description>
</em:targetApplication>

View file

@ -1,84 +1,81 @@
{
"manifest_version": 2,
"name": "uBlock Origin",
"version": "1.9.15.101",
"applications": {
"gecko": {
"id": "uBlock0@raymondhill.net",
"strict_min_version": "52.0a1"
}
},
"commands": {
"launch-element-zapper": {
"suggested_key": {
"default": "Alt+Z"
},
"description": "__MSG_popupTipZapper__"
},
"launch-element-picker": {
"suggested_key": {
"default": "Alt+X"
},
"description": "__MSG_popupTipPicker__"
},
"launch-logger": {
"suggested_key": {
"default": "Alt+L"
},
"description": "__MSG_popupTipLog__"
}
},
"default_locale": "en",
"description": "__MSG_extShortDesc__",
"icons": {
"16": "img/icon_16.png",
"128": "img/icon_128.png"
},
"browser_action": {
"browser_style": false,
"default_icon": {
"19": "img/browsericons/icon19.png",
"38": "img/browsericons/icon38.png"
},
"default_title": "uBlock Origin",
"default_popup": "popup.html"
},
"author": "All uBlock Origin contributors",
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["js/vapi-client.js", "js/contentscript.js"],
"run_at": "document_start",
"all_frames": true
},
{
"matches": ["http://*/*", "https://*/*"],
"js": ["js/scriptlets/subscriber.js"],
"run_at": "document_idle",
"all_frames": false
}
],
"minimum_chrome_version": "26.0",
"options_ui": {
"page": "options_ui.html"
},
"permissions": [
"contextMenus",
"privacy",
"storage",
"tabs",
"webNavigation",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"short_name": "uBlock₀"
"applications":{
"gecko":{
"id":"uBlock0@raymondhill.net",
"strict_min_version":"52.0"
}
},
"author":"All uBlock Origin contributors",
"background":{
"page":"background.html"
},
"browser_action":{
"browser_style":false,
"default_icon":{
"19":"img/browsericons/icon19.png",
"38":"img/browsericons/icon38.png"
},
"default_title":"uBlock Origin",
"default_popup":"popup.html"
},
"commands":{
"launch-element-zapper":{
"description":"__MSG_popupTipZapper__"
},
"launch-element-picker":{
"description":"__MSG_popupTipPicker__"
},
"launch-logger":{
"description":"__MSG_popupTipLog__"
}
},
"content_scripts":[
{
"matches":[
"http://*/*",
"https://*/*"
],
"js":[
"js/vapi-client.js",
"js/vapi-usercss.js",
"js/contentscript.js"
],
"run_at":"document_start",
"all_frames":true
},
{
"matches":[
"http://*/*",
"https://*/*"
],
"js":[
"js/scriptlets/subscriber.js"
],
"run_at":"document_idle",
"all_frames":false
}
],
"default_locale":"en",
"description":"__MSG_extShortDesc__",
"icons":{
"16":"img/icon_16.png",
"128":"img/icon_128.png"
},
"manifest_version":2,
"name":"uBlock Origin",
"options_ui":{
"page":"options_ui.html"
},
"permissions":[
"contextMenus",
"privacy",
"storage",
"tabs",
"webNavigation",
"webRequest",
"webRequestBlocking",
"<all_urls>"
],
"short_name":"uBlock₀",
"version":"1.9.15.101"
}

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<a href="dashboard.html" data-i18n="popupTipDashboard" target="uBO-dashboard"></a><br>
<a href="logger-ui.html" data-i18n="popupTipLog" target="uBO-logger"></a>
<script src="js/vapi-common.js"></script>
<script src="js/i18n.js"></script>
</body>
</html>

View file

@ -0,0 +1,269 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2016-2017 The uBlock Origin authors
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 indexedDB, IDBDatabase */
'use strict';
/******************************************************************************/
// The code below has been originally manually imported from:
// Commit: https://github.com/nikrolls/uBlock-Edge/commit/d1538ea9bea89d507219d3219592382eee306134
// Commit date: 29 October 2016
// Commit author: https://github.com/nikrolls
// Commit message: "Implement cacheStorage using IndexedDB"
// The original imported code has been subsequently modified as it was not
// compatible with Firefox.
// (a Promise thing, see https://github.com/dfahlander/Dexie.js/issues/317)
// Furthermore, code to migrate from browser.storage.local to vAPI.cacheStorage
// has been added, for seamless migration of cache-related entries into
// indexedDB.
vAPI.cacheStorage = (function() {
const STORAGE_NAME = 'uBlock0CacheStorage';
var db;
var pending = [];
// prime the db so that it's ready asap for next access.
getDb(noopfn);
return { get, set, remove, clear, getBytesInUse };
function get(input, callback) {
if ( typeof callback !== 'function' ) { return; }
if ( input === null ) {
return getAllFromDb(callback);
}
var toRead, output = {};
if ( typeof input === 'string' ) {
toRead = [ input ];
} else if ( Array.isArray(input) ) {
toRead = input;
} else /* if ( typeof input === 'object' ) */ {
toRead = Object.keys(input);
output = input;
}
return getFromDb(toRead, output, callback);
}
function set(input, callback) {
putToDb(input, callback);
}
function remove(key, callback) {
deleteFromDb(key, callback);
}
function clear(callback) {
clearDb(callback);
}
function getBytesInUse(keys, callback) {
// TODO: implement this
callback(0);
}
function genericErrorHandler(error) {
console.error('[uBlock0 cacheStorage]', error);
}
function noopfn() {
}
function processPendings() {
var cb;
while ( (cb = pending.shift()) ) {
cb(db);
}
}
function getDb(callback) {
if ( pending === undefined ) {
return callback();
}
if ( pending.length !== 0 ) {
return pending.push(callback);
}
if ( db instanceof IDBDatabase ) {
return callback(db);
}
pending.push(callback);
if ( pending.length !== 1 ) { return; }
// This will fail in private browsing mode.
var req = indexedDB.open(STORAGE_NAME, 1);
req.onupgradeneeded = function(ev) {
db = ev.target.result;
db.onerror = genericErrorHandler;
var table = db.createObjectStore(STORAGE_NAME, { keyPath: 'key' });
table.createIndex('value', 'value', { unique: false });
};
req.onsuccess = function(ev) {
db = ev.target.result;
db.onerror = genericErrorHandler;
processPendings();
};
req.onerror = function() {
console.log(this.error);
processPendings();
pending = undefined;
};
}
function getFromDb(keys, store, callback) {
if ( typeof callback !== 'function' ) { return; }
if ( keys.length === 0 ) { return callback(store); }
var notfoundKeys = new Set(keys);
var gotOne = function() {
if ( typeof this.result === 'object' ) {
store[this.result.key] = this.result.value;
notfoundKeys.delete(this.result.key);
}
};
getDb(function(db) {
if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME);
transaction.oncomplete = transaction.onerror = function() {
// TODO: remove once storage.local is clean
if ( notfoundKeys.size === 0 ) {
vAPI.storage.remove(keys);
return callback(store);
}
maybeMigrate(Array.from(notfoundKeys), store, callback);
};
var table = transaction.objectStore(STORAGE_NAME);
for ( var key of keys ) {
var req = table.get(key);
req.onsuccess = gotOne;
req.onerror = noopfn;
}
});
}
// Migrate from storage API
// TODO: removes once all users are migrated to the new cacheStorage.
function maybeMigrate(keys, store, callback) {
var toMigrate = new Set(),
i = keys.length;
while ( i-- ) {
var key = keys[i];
toMigrate.add(key);
// If migrating a compiled list, also migrate the non-compiled
// counterpart.
if ( /^cache\/compiled\//.test(key) ) {
toMigrate.add(key.replace('/compiled', ''));
}
}
vAPI.storage.get(Array.from(toMigrate), function(bin) {
if ( bin instanceof Object === false ) {
return callback(store);
}
var migratedKeys = Object.keys(bin);
if ( migratedKeys.length === 0 ) {
return callback(store);
}
var i = migratedKeys.length;
while ( i-- ) {
var key = migratedKeys[i];
store[key] = bin[key];
}
vAPI.storage.remove(migratedKeys);
vAPI.cacheStorage.set(bin);
callback(store);
});
}
function getAllFromDb(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
getDb(function(db) {
if ( !db ) { return callback(); }
var output = {};
var transaction = db.transaction(STORAGE_NAME);
transaction.oncomplete = transaction.onerror = function() {
callback(output);
};
var table = transaction.objectStore(STORAGE_NAME),
req = table.openCursor();
req.onsuccess = function(ev) {
var cursor = ev.target.result;
if ( !cursor ) { return; }
output[cursor.key] = cursor.value;
cursor.continue();
};
});
}
function putToDb(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
var keys = Object.keys(input);
if ( keys.length === 0 ) { return callback(); }
getDb(function(db) {
if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete = transaction.onerror = callback;
var table = transaction.objectStore(STORAGE_NAME),
entry = {};
for ( var key of keys ) {
entry.key = key;
entry.value = input[key];
table.put(entry);
}
});
}
function deleteFromDb(input, callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
var keys = Array.isArray(input) ? input.slice() : [ input ];
if ( keys.length === 0 ) { return callback(); }
getDb(function(db) {
if ( !db ) { return callback(); }
var transaction = db.transaction(STORAGE_NAME, 'readwrite');
transaction.oncomplete = transaction.onerror = callback;
var table = transaction.objectStore(STORAGE_NAME);
for ( var key of keys ) {
table.delete(key);
}
});
// TODO: removes once all users are migrated to the new cacheStorage.
vAPI.storage.remove(keys);
}
function clearDb(callback) {
if ( typeof callback !== 'function' ) {
callback = noopfn;
}
getDb(function(db) {
if ( !db ) { return callback(); }
var req = db.transaction(STORAGE_NAME, 'readwrite')
.objectStore(STORAGE_NAME)
.clear();
req.onsuccess = req.onerror = callback;
});
}
}());
/******************************************************************************/

View file

@ -0,0 +1,77 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
// For content pages
/******************************************************************************/
(function() {
if ( typeof vAPI !== 'object' ) { return; }
vAPI.userCSS = {
_userCSS: '',
_disabled: false,
_send: function(toRemove, toAdd) {
vAPI.messaging.send('vapi-background', {
what: 'userCSS',
toRemove: toRemove,
toAdd: toAdd
});
},
add: function(cssText) {
if ( cssText === '' ) { return; }
var before = this._userCSS,
after = before;
if ( after !== '' ) { after += '\n'; }
after += cssText;
this._userCSS = after;
if ( this._disabled ) { return; }
this._send(before, after);
},
remove: function(cssText) {
if ( cssText === '' || this._userCSS === '' ) { return; }
var before = this._userCSS,
after = before;
after = before.replace(cssText, '').trim();
this._userCSS = after;
if ( this._disabled ) { return; }
this._send(before, after);
},
toggle: function(state) {
if ( state === undefined ) {
state = this._disabled;
}
if ( state !== this._disabled ) { return; }
this._disabled = !state;
if ( this._userCSS === '' ) { return; }
var toAdd, toRemove;
if ( state ) {
toAdd = this._userCSS;
} else {
toRemove = this._userCSS;
}
this._send(toRemove, toAdd);
}
};
vAPI.hideNode = vAPI.unhideNode = function(){};
})();

View file

@ -0,0 +1,182 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2017 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
*/
// For background page
'use strict';
/******************************************************************************/
vAPI.net = {};
vAPI.net.registerListeners = function() {
// https://github.com/gorhill/uBlock/issues/2950
// Firefox 55 does not normalize URLs to ASCII, uBO must do this itself.
// https://bugzilla.mozilla.org/show_bug.cgi?id=945240
var mustPunycode = false;
(function() {
if (
typeof browser === 'object' &&
browser !== null &&
browser.runtime instanceof Object &&
typeof browser.runtime.getBrowserInfo === 'function'
) {
browser.runtime.getBrowserInfo().then(info => {
mustPunycode = info.name === 'Firefox' &&
/^5[0-6]\./.test(info.version);
});
}
})();
var wrApi = browser.webRequest;
// legacy Chromium understands 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
};
// modern Chromium/WebExtensions: more types available.
if ( wrApi.ResourceType ) {
for ( let typeKey in wrApi.ResourceType ) {
if ( wrApi.ResourceType.hasOwnProperty(typeKey) ) {
validTypes[wrApi.ResourceType[typeKey]] = 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[type] ) {
out.push(type);
}
if ( type === 'other' ) {
needOther = false;
}
}
if ( needOther ) {
out.push('other');
}
return out;
};
var punycode = self.punycode;
var reMustNormalizeHostname = /[^0-9a-z._-]/;
var parsedURL = new URL('about:blank');
var normalizeRequestDetails = function(details) {
details.tabId = details.tabId.toString();
if (
mustPunycode === true &&
reMustNormalizeHostname.test(details.url) === true
) {
parsedURL.href = details.url;
details.url = details.url.replace(
parsedURL.hostname,
punycode.toASCII(parsedURL.hostname)
);
}
var type = details.type;
// https://github.com/gorhill/uBlock/issues/1493
// Chromium 49+/WebExtensions support a new request type: `ping`,
// which is fired as a result of using `navigator.sendBeacon`.
if ( type === 'ping' ) {
details.type = 'beacon';
return;
}
if ( type === 'imageset' ) {
details.type = 'image';
return;
}
};
var onBeforeRequestClient = this.onBeforeRequest.callback;
var onBeforeRequest = function(details) {
normalizeRequestDetails(details);
return onBeforeRequestClient(details);
};
var onHeadersReceivedClient = this.onHeadersReceived.callback,
onHeadersReceivedClientTypes = this.onHeadersReceived.types.slice(0),
onHeadersReceivedTypes = denormalizeTypes(onHeadersReceivedClientTypes);
var onHeadersReceived = function(details) {
normalizeRequestDetails(details);
if (
onHeadersReceivedClientTypes.length !== 0 &&
onHeadersReceivedClientTypes.indexOf(details.type) === -1
) {
return;
}
return onHeadersReceivedClient(details);
};
if ( onBeforeRequest ) {
let urls = this.onBeforeRequest.urls || ['<all_urls>'];
let types = this.onBeforeRequest.types || undefined;
if (
(validTypes.websocket) &&
(types === undefined || types.indexOf('websocket') !== -1) &&
(urls.indexOf('<all_urls>') === -1)
) {
if ( urls.indexOf('ws://*/*') === -1 ) {
urls.push('ws://*/*');
}
if ( urls.indexOf('wss://*/*') === -1 ) {
urls.push('wss://*/*');
}
}
wrApi.onBeforeRequest.addListener(
onBeforeRequest,
{ urls: urls, types: types },
this.onBeforeRequest.extra
);
}
if ( onHeadersReceived ) {
let urls = this.onHeadersReceived.urls || ['<all_urls>'];
let types = onHeadersReceivedTypes;
wrApi.onHeadersReceived.addListener(
onHeadersReceived,
{ urls: urls, types: types },
this.onHeadersReceived.extra
);
}
};
/******************************************************************************/

View file

@ -45,7 +45,7 @@
<input type="checkbox"><!--
--><a class="content" type="text/plain" target="_blank" href=""></a>&#8203;<!--
--><a class="fa support" href="" target="_blank">&#xf015;</a>&#8203;<!--
--><a class="fa remove" href="">&#xf00d;</a>&#8203;<!--
--><a class="fa remove" href="">&#xf014;</a>&#8203;<!--
--><a class="fa mustread" href="" target="_blank">&#xf05a;</a>&#8203;<!--
--><span class="fa status unsecure" title="http">&#xf13e;</span>&#8203;<!--
--><span class="counts dim"></span>&#8203;<!--

View file

@ -340,7 +340,7 @@
"description":"English: One URL per line. Lines prefixed with &lsquo;!&rsquo; will be ignored. Invalid URLs will be silently ignored."
},
"3pExternalListObsolete":{
"message":"متقادم.",
"message":"إنتهت صلاحيته.",
"description":"used as a tooltip for the out-of-date icon beside a list"
},
"3pLastUpdate":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Kliknutím sem otevřete ovládací panel",
"message":"Otevřít ovládací panel",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Klõpsa, et avada töölaud",
"message":"Ava töölaud",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Klik kontrol panela irekitzeko",
"message":"Ireki kontrol panela",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -40,7 +40,7 @@
"description":"appears as tab name in dashboard"
},
"advancedSettingsPageName":{
"message":"Advanced settings",
"message":"تنظیمات پیشرفته",
"description":"Title for the advanced settings page"
},
"popupPowerSwitchInfo":{
@ -68,11 +68,11 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":رای باز کردن داشبورد کلیک کنید",
"message":از کردن داشبورد",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"ورود به حالت انتخاب اشیاء",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
@ -80,7 +80,7 @@
"description":"English: Enter element picker mode"
},
"popupTipLog":{
"message":"بازنمایی ثبت درخواست",
"message":"رفتن به لاگ درخواست",
"description":"Tooltip used for the logger icon in the panel"
},
"popupTipNoPopups":{
@ -216,7 +216,7 @@
"description":""
},
"settingsAdvancedUserSettings":{
"message":"advanced settings",
"message":"تنظیمات پیشرفته",
"description":"For the tooltip of a link which gives access to advanced settings"
},
"settingsPrefetchingDisabledPrompt":{
@ -348,11 +348,11 @@
"description":"used as a tooltip for the clock icon beside a list"
},
"3pUpdating":{
"message":"Updating...",
"message":"درحال بروزرسانی...",
"description":"used as a tooltip for the spinner icon beside a list"
},
"3pNetworkError":{
"message":"A network error prevented the resource from being updated.",
"message":"یک خطای شبکه از بروزشدن منابع جلوگیری کرد.",
"description":"used as a tooltip for error icon beside a list"
},
"1pFormatHint":{
@ -680,7 +680,7 @@
"description":"used as a prompt for the user to provide a custom device name"
},
"advancedSettingsWarning":{
"message":"Warning! Change these advanced settings at your own risk.",
"message":"هشدار! این تنظیمات پیشرفته را با مسئولیت خود تغییر دهید.",
"description":"A warning to users at the top of 'Advanced settings' page"
},
"genericSubmit":{

View file

@ -68,11 +68,11 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Avaa hallintapaneeli napsauttamalla",
"message":"Hallintapaneelityökaluun",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"Aktivoi elementtien piilotus\/palautus -poimintatyökalu",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
@ -148,7 +148,7 @@
"description":""
},
"popupHitDomainCountPrompt":{
"message":"yhdistetyt verkkotunnukset",
"message":"aktiiviset verkkotunnusyhteydet",
"description":"appears in popup"
},
"popupHitDomainCount":{
@ -204,7 +204,7 @@
"description":"English: Make use of context menu where appropriate"
},
"settingsColorBlindPrompt":{
"message":"Mukauta värisokeille",
"message":"Aktivoi vaihtoehtoiset värit puna-vihervärien sijaan",
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{

View file

@ -32,7 +32,7 @@
"description":"appears as tab name in dashboard"
},
"statsPageName":{
"message":"uBlock₀ — Logger",
"message":"uBlock - Talaan",
"description":"Title for the logger window"
},
"aboutPageName":{
@ -40,7 +40,7 @@
"description":"appears as tab name in dashboard"
},
"advancedSettingsPageName":{
"message":"Advanced settings",
"message":"Mga pinalawig na ayos",
"description":"Title for the advanced settings page"
},
"popupPowerSwitchInfo":{
@ -72,7 +72,7 @@
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"Buksan ang element zapper na paraan",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
@ -84,19 +84,19 @@
"description":"Tooltip used for the logger icon in the panel"
},
"popupTipNoPopups":{
"message":"Toggle the blocking of all popups for this site",
"message":"Palit-ayos sa paghalang ng mga labasin sa site",
"description":"Tooltip for the no-popups per-site switch"
},
"popupTipNoLargeMedia":{
"message":"Toggle the blocking of large media elements for this site",
"message":"Palit-ayos sa paghalang ng mga malaking media element sa site",
"description":"Tooltip for the no-large-media per-site switch"
},
"popupTipNoCosmeticFiltering":{
"message":"Toggle cosmetic filtering for this site",
"message":"Palit-ayos ng panala ng itsura para sa site na ito",
"description":"Tooltip for the no-cosmetic-filtering per-site switch"
},
"popupTipNoRemoteFonts":{
"message":"Toggle the blocking of remote fonts for this site",
"message":"Palit-ayos sa paghalang ng malayuang itsura ng titik para sa site na ito",
"description":"Tooltip for the no-remote-fonts per-site switch"
},
"popupTipGlobalRules":{
@ -116,15 +116,15 @@
"description":"Tooltip when hovering over the eraser in the dynamic filtering pane."
},
"popupAnyRulePrompt":{
"message":"all",
"message":"lahat",
"description":""
},
"popupImageRulePrompt":{
"message":"images",
"message":"mga imahe",
"description":""
},
"popup3pAnyRulePrompt":{
"message":"3rd-party",
"message":"Pang-tatlo na personalidad",
"description":""
},
"popup3pPassiveRulePrompt":{
@ -156,19 +156,19 @@
"description":"appears in popup"
},
"pickerCreate":{
"message":"Create",
"message":"Gumawa",
"description":"English: Create"
},
"pickerPick":{
"message":"Pick",
"message":"Pili",
"description":"English: Pick"
},
"pickerQuit":{
"message":"Quit",
"message":"Tumigil",
"description":"English: Quit"
},
"pickerPreview":{
"message":"Preview",
"message":"Paunang-tingin",
"description":"Element picker preview mode: will cause the elements matching the current filter to be removed from the page"
},
"pickerNetFilters":{
@ -184,7 +184,7 @@
"description":"English: Click, Ctrl-click"
},
"pickerContextMenuEntry":{
"message":"Block element",
"message":"Paghalang sa elemento",
"description":"English: Block element"
},
"settingsCollapseBlockedPrompt":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Klik om it dashboerd te iepenjen",
"message":"Dashboerd iepenje",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"לחץ כדי לפתוח את לוח המחוונים",
"message":"פתח את פאנל הקונפיגורציות",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -168,7 +168,7 @@
"description":"English: Quit"
},
"pickerPreview":{
"message":"Preview",
"message":"पूर्वावलोकन",
"description":"Element picker preview mode: will cause the elements matching the current filter to be removed from the page"
},
"pickerNetFilters":{
@ -216,7 +216,7 @@
"description":""
},
"settingsAdvancedUserSettings":{
"message":"advanced settings",
"message":"उन्नत सेटिंग्स",
"description":"For the tooltip of a link which gives access to advanced settings"
},
"settingsPrefetchingDisabledPrompt":{
@ -272,7 +272,7 @@
"description":"Appears aside each filter list in the _3rd-party filters_ pane"
},
"3pAutoUpdatePrompt1":{
"message":"Auto-update filter lists",
"message":"स्व-अद्यतन फ़िल्टर सूचियां",
"description":"A checkbox in the _3rd-party filters_ pane"
},
"3pUpdateNow":{

View file

@ -72,7 +72,7 @@
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Elemváltó mód aktiválása",
"message":"Elem eltávolító mód",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
@ -580,7 +580,7 @@
"description":"Message asking user to confirm reset"
},
"errorCantConnectTo":{
"message":"Csatlakozás sikertelen a következő címhez: {{url}}",
"message":"Hálózati hiba: {{msg}}",
"description":"English: Network error: {{msg}}"
},
"subscriberConfirm":{

View file

@ -68,11 +68,11 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Klik untuk membuka dasbor",
"message":"Buka dasbor",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"Memasuki mode penghapus elemen",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Clicca per aprire il dashboard",
"message":"Clicca per aprire il pannello",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
@ -224,7 +224,7 @@
"description":"English: "
},
"settingsHyperlinkAuditingDisabledPrompt":{
"message":"Disabilita la revisione degli hyperlink",
"message":"Disabilita la revisione dei collegamenti ipertestuali",
"description":"English: "
},
"settingsWebRTCIPAddressHiddenPrompt":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"クリックしてダッシュボードを開く",
"message":"ダッシュボードを開く",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
@ -92,7 +92,7 @@
"description":"Tooltip for the no-large-media per-site switch"
},
"popupTipNoCosmeticFiltering":{
"message":"このサイトの要素隠蔽フィルターを切り替え",
"message":"このサイトの要素隠蔽フィルターを切り替えます",
"description":"Tooltip for the no-cosmetic-filtering per-site switch"
},
"popupTipNoRemoteFonts":{
@ -176,7 +176,7 @@
"description":"English: header for a type of filter in the element picker dialog"
},
"pickerCosmeticFilters":{
"message":"要素隠蔽フィルタ",
"message":"要素隠蔽フィルタ",
"description":"English: Cosmetic filters"
},
"pickerCosmeticFiltersHint":{
@ -204,7 +204,7 @@
"description":"English: Make use of context menu where appropriate"
},
"settingsColorBlindPrompt":{
"message":"uBlock₀を優しい色使いにする",
"message":"uBlock₀を色盲の方に優しい色にする",
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
@ -240,7 +240,7 @@
"description":""
},
"settingsNoCosmeticFilteringPrompt":{
"message":"要素隠蔽フィルタを無効にする",
"message":"要素隠蔽フィルタを無効にする",
"description":""
},
"settingsNoLargeMediaPrompt":{
@ -252,7 +252,7 @@
"description":""
},
"settingsStorageUsed":{
"message":"使用中のストレージ: {{value}} bytes",
"message":"使用中のストレージ: {{value}} バイト",
"description":"English: Storage used: {{}} bytes"
},
"settingsLastRestorePrompt":{
@ -264,7 +264,7 @@
"description":"English: Last backup:"
},
"3pListsOfBlockedHostsPrompt":{
"message":"{{netFilterCount}} ネットワークフィルター+{{cosmeticFilterCount}} 要素隠蔽フィルター",
"message":"{{netFilterCount}}ネットワークフィルタ+{{cosmeticFilterCount}}コスメチックフィルタ",
"description":"Appears at the top of the _3rd-party filters_ pane"
},
"3pListsOfBlockedHostsPerListStats":{
@ -284,7 +284,7 @@
"description":"A button in the in the _3rd-party filters_ pane"
},
"3pParseAllABPHideFiltersPrompt1":{
"message":"要素隠蔽フィルタを解析、施行する",
"message":"要素隠蔽フィルタを解析、施行する",
"description":"English: Parse and enforce Adblock+ element hiding filters."
},
"3pParseAllABPHideFiltersInfo":{
@ -340,11 +340,11 @@
"description":"English: One URL per line. Lines prefixed with &lsquo;!&rsquo; will be ignored. Invalid URLs will be silently ignored."
},
"3pExternalListObsolete":{
"message":"新しいバージョンがあります",
"message":"古いバージョンです",
"description":"used as a tooltip for the out-of-date icon beside a list"
},
"3pLastUpdate":{
"message":"最終更新: {{ago}}\nクリックすると強制的に更新します",
"message":"最終更新: {{ago}}",
"description":"used as a tooltip for the clock icon beside a list"
},
"3pUpdating":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Klik om het dashboard te openen",
"message":"Dashboard openen",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Kliknij, aby otworzyć panel sterowania",
"message":"Otwórz panel sterowania",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
@ -344,7 +344,7 @@
"description":"used as a tooltip for the out-of-date icon beside a list"
},
"3pLastUpdate":{
"message":"Zaktualizowano: {{ago}}.\nKliknij by wymusić aktualizację.",
"message":"Zaktualizowano: {{ago}}.\nKliknij, aby wymusić aktualizację.",
"description":"used as a tooltip for the clock icon beside a list"
},
"3pUpdating":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Clique para abrir o painel",
"message":"Abrir painel de controle",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Clic pentru a deschide tabloul de bord",
"message":"Deschide panoul de control",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -76,7 +76,7 @@
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
"message":"Войти в режим выбора элемента",
"message":"Войти в режим выбора элементов",
"description":"English: Enter element picker mode"
},
"popupTipLog":{
@ -100,11 +100,11 @@
"description":"Tooltip for the no-remote-fonts per-site switch"
},
"popupTipGlobalRules":{
"message":"Глобальные правила: эта колонка предназначена для правил, применяемых ко всем сайтам.",
"message":"Глобальные правила: этот столбец предназначен для правил, применяемых ко всем сайтам.",
"description":"Tooltip when hovering the top-most cell of the global-rules column."
},
"popupTipLocalRules":{
"message":"Локальные правила: эта колонка предназначена для правил, применяемых только к текущему сайту.\nЛокальные правила замещают глобальные.",
"message":"Локальные правила: этот столбец предназначен для правил, применяемых только к текущему сайту.\nЛокальные правила переопределяют глобальные.",
"description":"Tooltip when hovering the top-most cell of the local-rules column."
},
"popupTipSaveRules":{
@ -180,7 +180,7 @@
"description":"English: Cosmetic filters"
},
"pickerCosmeticFiltersHint":{
"message":"Щелчок, Ctrl+щелчок",
"message":"Клик, Ctrl+клик",
"description":"English: Click, Ctrl-click"
},
"pickerContextMenuEntry":{

View file

@ -72,7 +72,7 @@
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"Vklopi način izbire elementov",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
@ -208,7 +208,7 @@
"description":"English: Color-blind friendly"
},
"settingsCloudStorageEnabledPrompt":{
"message":"Omogoči podporo za shrambo v oblako",
"message":"Omogoči podporo za shrambo v oblaku",
"description":""
},
"settingsAdvancedUserPrompt":{
@ -348,11 +348,11 @@
"description":"used as a tooltip for the clock icon beside a list"
},
"3pUpdating":{
"message":"Updating...",
"message":"Posodabljanje ...",
"description":"used as a tooltip for the spinner icon beside a list"
},
"3pNetworkError":{
"message":"A network error prevented the resource from being updated.",
"message":"Omrežna napaka je preprečila posodobitev virov.",
"description":"used as a tooltip for error icon beside a list"
},
"1pFormatHint":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Кликните за отварање контролне табле",
"message":"Отвори контролну таблу",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
@ -96,7 +96,7 @@
"description":"Tooltip for the no-cosmetic-filtering per-site switch"
},
"popupTipNoRemoteFonts":{
"message":"Укључи\/искључи блокирање спољних фонтова за овај сајт",
"message":"Укључи\/искључи блокирање удаљених фонтова за овај сајт",
"description":"Tooltip for the no-remote-fonts per-site switch"
},
"popupTipGlobalRules":{
@ -108,11 +108,11 @@
"description":"Tooltip when hovering the top-most cell of the local-rules column."
},
"popupTipSaveRules":{
"message":"Кликните да бисте измене учинили трајним.",
"message":"Кликните да бисте промене учинили трајним.",
"description":"Tooltip when hovering over the padlock in the dynamic filtering pane."
},
"popupTipRevertRules":{
"message":"Кликните да бисте вратили измене.",
"message":"Кликните да бисте вратили промене.",
"description":"Tooltip when hovering over the eraser in the dynamic filtering pane."
},
"popupAnyRulePrompt":{
@ -304,7 +304,7 @@
"description":"English: Lists of blocked hosts"
},
"3pApplyChanges":{
"message":"Примени измене",
"message":"Примени промене",
"description":"English: Apply changes"
},
"3pGroupAds":{
@ -372,7 +372,7 @@
"description":"English: my-ublock-static-filters_{{datetime}}.txt"
},
"1pApplyChanges":{
"message":"Примени измене",
"message":"Примени промене",
"description":"English: Apply changes"
},
"rulesPermanentHeader":{
@ -440,7 +440,7 @@
"description":"English: my-ublock-whitelist_{{datetime}}.txt"
},
"whitelistApply":{
"message":"Примени измене",
"message":"Примени промене",
"description":"English: Apply changes"
},
"logRequestsHeaderType":{
@ -532,7 +532,7 @@
"description":"Below this sentence, the filter lists in which the filter was found"
},
"aboutChangelog":{
"message":"Дневник измена",
"message":"Евиденција промена",
"description":"English: Change log"
},
"aboutWiki":{
@ -688,7 +688,7 @@
"description":"for generic 'Submit' buttons"
},
"genericApplyChanges":{
"message":"Примени измене",
"message":"Примени промене",
"description":"for generic 'Apply changes' buttons"
},
"genericRevert":{

View file

@ -68,15 +68,15 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Kontrol panelini aç",
"message":"Kontrol panelini açar",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":ğe silme moduna gir",
"message":ge silme moduna gir",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
"message":ğe seçme moduna gir",
"message":ge seçme moduna gir",
"description":"English: Enter element picker mode"
},
"popupTipLog":{
@ -88,7 +88,7 @@
"description":"Tooltip for the no-popups per-site switch"
},
"popupTipNoLargeMedia":{
"message":"Bu site için büyük medya öğelerini engellemeyi aç\/kapa",
"message":"Bu site için büyük medya ögelerini engellemeyi aç\/kapa",
"description":"Tooltip for the no-large-media per-site switch"
},
"popupTipNoCosmeticFiltering":{
@ -184,7 +184,7 @@
"description":"English: Click, Ctrl-click"
},
"pickerContextMenuEntry":{
"message":"Ögeyi engelle",
"message":"Öge engelle",
"description":"English: Block element"
},
"settingsCollapseBlockedPrompt":{
@ -244,7 +244,7 @@
"description":""
},
"settingsNoLargeMediaPrompt":{
"message":"Belirlenenden büyük medya öğelerini engelle {{input:number}} kB",
"message":"Belirlenenden büyük medya ögelerini engelle {{input:number}} kB",
"description":""
},
"settingsNoRemoteFontsPrompt":{
@ -252,7 +252,7 @@
"description":""
},
"settingsStorageUsed":{
"message":"Kullanılan Alan: {{value}} bayt",
"message":"Kullanılan Alan: {{value}} byte",
"description":"English: Storage used: {{}} bytes"
},
"settingsLastRestorePrompt":{
@ -280,7 +280,7 @@
"description":"A button in the in the _3rd-party filters_ pane"
},
"3pPurgeAll":{
"message":"Tüm önbelleği temizle",
"message":"Tüm önbellekleri temizle",
"description":"A button in the in the _3rd-party filters_ pane"
},
"3pParseAllABPHideFiltersPrompt1":{
@ -288,11 +288,11 @@
"description":"English: Parse and enforce Adblock+ element hiding filters."
},
"3pParseAllABPHideFiltersInfo":{
"message":"<p>Bu seçenek <a href=\"https:\/\/adblockplus.org\/en\/faq_internal#elemhide\"> Adblock Plus-uyumlu &ldquo;öğe gizleme&rdquo; süzgeçlerinin<\/a> işlenmesini ve uygulanmasını sağlar. Bu süzgeçler aslında kozmetiktir, bir web sayfasında görsel rahatsızlık yaratan ve ağ isteği-tabanlı süzme motoru tarafından engellenemeyecek olan öğelerin gizlenmesine yarar.<\/p><p>Bu özelliği etkinleştirmek uBlock₀'in bellek kullanımını artırır.<\/p>",
"message":"<p>Bu seçenek <a href=\"https:\/\/adblockplus.org\/en\/faq_internal#elemhide\"> Adblock Plus-uyumlu &ldquo;öge gizleme&rdquo; süzgeçlerinin<\/a> işlenmesini ve uygulanmasını sağlar. Bu süzgeçler aslında kozmetiktir, bir web sayfasında görsel rahatsızlık yaratan ve ağ isteği-tabanlı süzme motoru tarafından engellenemeyecek olan ögelerin gizlenmesine yarar.<\/p><p>Bu özelliği etkinleştirmek uBlock₀'in bellek kullanımını artırır.<\/p>",
"description":"Describes the purpose of the 'Parse and enforce cosmetic filters' feature."
},
"3pIgnoreGenericCosmeticFilters":{
"message":"Genel kozmetik süzgeçleri yoksay.",
"message":"Genel kozmetik süzgeçleri yok say",
"description":"This will cause uBO to ignore all generic cosmetic filters."
},
"3pIgnoreGenericCosmeticFiltersInfo":{
@ -336,7 +336,7 @@
"description":"English: Custom"
},
"3pExternalListsHint":{
"message":"Her satırda bir URL. &lsquo;!&rsquo; ile başlayan satırlar göz ardı edilir. Geçersiz URL'ler sessizce yoksayılır.",
"message":"Her satırda bir URL. &lsquo;!&rsquo; ile başlayan satırlar göz ardı edilir. Geçersiz URL'ler sessizce yok sayılır.",
"description":"English: One URL per line. Lines prefixed with &lsquo;!&rsquo; will be ignored. Invalid URLs will be silently ignored."
},
"3pExternalListObsolete":{
@ -464,15 +464,15 @@
"description":"Appears in the logger's tab selector"
},
"logBehindTheScene":{
"message":"Sahne arkası",
"message":"Perde arkası",
"description":"Pretty name for behind-the-scene network requests"
},
"logFilterPrompt":{
"message":"günlük kayıtlarını süz",
"message":"günlük girişlerini süz",
"description":"English: filter log entries"
},
"logMaxEntriesTip":{
"message":"Maksimum günlük kayıt sayısı",
"message":"Maksimum günlük giriş sayısı",
"description":"Tooltip informaing that the input field is to set the maximum number of entries in the log"
},
"loggerURLFilteringContextLabel":{
@ -568,11 +568,11 @@
"description":"English: Reset to default settings..."
},
"aboutRestoreDataConfirm":{
"message":"Tüm ayarlarınız {{time}} tarihinde yedeklenmiş veriler kullanarak değiştirilecek ve uBlock₀ yeniden başlayacak. \n\nYedeklenmiş verileriniz kullanılarak mevcut tüm ayarlarınız değiştirilsin mi?",
"message":"Tüm ayarlarınız {{time}} tarihinde yedeklenmiş veriler kullanarak değiştirilecek ve uBlock₀ yeniden başlayacak. \n\nYedeklenmiş verileriniz kullanılarak var olan tüm ayarlarınız değiştirilsin mi?",
"description":"Message asking user to confirm restore"
},
"aboutRestoreDataError":{
"message":"Veri okunamıyor ya da geçersiz",
"message":"Veri okunamadı veya geçersiz",
"description":"Message to display when an error occurred during restore"
},
"aboutResetDataConfirm":{
@ -588,7 +588,7 @@
"description":"English: The message seen by the user to confirm subscription to a ABP filter list"
},
"elapsedOneMinuteAgo":{
"message":"1 dakika önce",
"message":"bir dakika önce",
"description":"English: a minute ago"
},
"elapsedManyMinutesAgo":{
@ -596,7 +596,7 @@
"description":"English: {{value}} minutes ago"
},
"elapsedOneHourAgo":{
"message":"1 saat önce",
"message":"bir saat önce",
"description":"English: an hour ago"
},
"elapsedManyHoursAgo":{
@ -604,7 +604,7 @@
"description":"English: {{value}} hours ago"
},
"elapsedOneDayAgo":{
"message":"1 gün önce",
"message":"bir gün önce",
"description":"English: a day ago"
},
"elapsedManyDaysAgo":{
@ -628,7 +628,7 @@
"description":"English: uBlock₀ has prevented the following page from loading:"
},
"docblockedPrompt2":{
"message":"Aşağıdaki süzgeçten dolayı",
"message":"Bu süzgeç nedeniyle",
"description":"English: Because of the following filter"
},
"docblockedNoParamsPrompt":{
@ -640,7 +640,7 @@
"description":"English: List of filter list names follows"
},
"docblockedBack":{
"message":"Geri dön",
"message":"Geri git",
"description":"English: Go back"
},
"docblockedClose":{
@ -664,11 +664,11 @@
"description":"tooltip"
},
"cloudPull":{
"message":"Bulut depodan içe aktar",
"message":"Bulut depodan al",
"description":"tooltip"
},
"cloudPullAndMerge":{
"message":"Bulut depodan içe aktar ve şu anki ayarlarla birleştir",
"message":"Bulut depodan al ve şu anki ayarlarla birleştir",
"description":"tooltip"
},
"cloudNoData":{
@ -700,7 +700,7 @@
"description":""
},
"contextMenuTemporarilyAllowLargeMediaElements":{
"message":"Geçici olarak büyük medya öğelerine izin ver",
"message":"Geçici olarak büyük medya ögelerine izin ver",
"description":"A context menu entry, present when large media elements have been blocked on the current site"
},
"dummy":{

View file

@ -68,11 +68,11 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"Bấm để mở bảng điều khiển",
"message":"Mở bảng điều khiển",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"Chuyển sang chế độ chọn phần tử",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{
@ -344,7 +344,7 @@
"description":"used as a tooltip for the out-of-date icon beside a list"
},
"3pLastUpdate":{
"message":"Cập nhật trước: {{ago}}",
"message":"Cập nhật mới: {{ago}}.\nClick để cập nhập.",
"description":"used as a tooltip for the clock icon beside a list"
},
"3pUpdating":{
@ -424,7 +424,7 @@
"description":"English: dynamic rule syntax and full documentation."
},
"whitelistPrompt":{
"message":"Danh sách tên các máy chủ mà µBlock₀ sẽ bị vô hiệu. Một mục nhập trên mỗi dòng. Tên máy chủ không hợp lệ sẽ được tự động bỏ qua.",
"message":"Danh sách tên các máy chủ mà uBlock₀ sẽ bị chặn. Một mục nhập trên mỗi dòng. Tên máy chủ không hợp lệ sẽ được tự động bỏ qua.",
"description":"English: An overview of the content of the dashboard's Whitelist pane."
},
"whitelistImport":{
@ -580,7 +580,7 @@
"description":"Message asking user to confirm reset"
},
"errorCantConnectTo":{
"message":"Không thể kết nối đến {{url}}",
"message":"Lỗi kết nối: {{msg}}",
"description":"English: Network error: {{msg}}"
},
"subscriberConfirm":{

View file

@ -68,7 +68,7 @@
"description":"English: or"
},
"popupTipDashboard":{
"message":"单击打开控制面板",
"message":"打开控制面板",
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{

View file

@ -72,7 +72,7 @@
"description":"English: Click to open the dashboard"
},
"popupTipZapper":{
"message":"Enter element zapper mode",
"message":"進入元素選擇器模式",
"description":"Tooltip for the element-zapper icon in the popup panel"
},
"popupTipPicker":{

View file

@ -6,7 +6,6 @@
<title>uBlock — About</title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<link rel="stylesheet" type="text/css" href="css/about.css">
</head>
<body>
@ -19,7 +18,7 @@
<li><a href="https://github.com/gorhill/uBlock" data-i18n="aboutCode"></a>
<li><span data-i18n="aboutContributors"></span>
<ul>
<li><a href="https://github.com/gorhill/uBlock/graphs/contributors">Github</a>
<li><a href="https://github.com/gorhill/uBlock/graphs/contributors">GitHub</a>
<li><a href="https://crowdin.net/project/ublock">Crowdin</a>
</ul>
<li><a href="https://github.com/bestiejs/punycode.js" target="_blank">Punycode.js</a> by <a href="https://github.com/mathiasbynens">Mathias Bynens</a>

View file

@ -10,6 +10,8 @@
<script src="lib/publicsuffixlist.js"></script>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-background.js"></script>
<script src="js/vapi-webrequest.js"></script><!-- Forks can pick the webext, chromium, or their own implementation -->
<script src="js/vapi-cachestorage.js"></script><!-- Optional -->
<script src="js/background.js"></script>
<script src="js/utils.js"></script>
<script src="js/uritools.js"></script>

20
src/cloud-ui.html Normal file
View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<button id="cloudPush" type="button" title="cloudPush"></button>
<span data-i18n="cloudNoData"></span>
<button id="cloudPull" type="button" title="cloudPull" disabled></button>&nbsp;
<button id="cloudPullAndMerge" type="button" title="cloudPullAndMerge" disabled></button>
<span id="cloudCog" class="fa">&#xf013;</span>
<div id="cloudOptions">
<div>
<p><label data-i18n="cloudDeviceNamePrompt"></label> <input id="cloudDeviceName" type="text" value="">
<p><button id="cloudOptionsSubmit" type="button" data-i18n="genericSubmit"></button>
</div>
</div>
</body>
</html>

View file

@ -1,4 +0,0 @@
ul {
padding-__MSG_@@bidi_start_edge__: 1em;
margin-__MSG_@@bidi_start_edge__: 1em;
}

View file

@ -578,12 +578,12 @@ body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.e
#filterFinderDialog .dialog {
padding: 1em;
word-break: break-all;
}
#filterFinderDialog .dialog code {
background: #eee;
font-size: 85%;
padding: 3px;
word-break: break-all;
}
#filterFinderDialog .dialog ul {
font-size: larger;

View file

@ -193,20 +193,24 @@ body[dir="ltr"] #extraTools > span > span.badge {
body[dir="rtl"] #extraTools > span > span.badge {
right: 100%;
}
#extraTools > span.on > span:last-of-type {
#extraTools > span > span:last-of-type {
color: #e00;
font-size: 1.1em;
left: 0;
left: 50%;
position: absolute;
text-align: center;
top: 0;
width: 100%;
transform: translateX(-50%);
visibility: hidden;
}
#extraTools > span.on > span:last-of-type:after {
content: '\2715';
#extraTools > span > span:last-of-type > svg {
stroke: red;
stroke-width: 2;
width: 1em;
}
#extraTools > span.on > span:last-of-type {
visibility: visible;
}
#extraTools > span:hover {
color: #444;
color: #333;
}
#refresh {

View file

@ -1,6 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<link rel="stylesheet" href="css/common.css" type="text/css">
<style>

View file

@ -985,6 +985,22 @@ var updateFirst = function() {
updateNext();
};
// Firefox extension reviewers do not want uBO/webext to fetch its *own*
// scriptlets/resources asset from the project's *own* repo (github.com).
var noRemoteResources = false;
(function() {
if (
typeof browser === 'object' &&
browser !== null &&
browser.runtime instanceof Object &&
typeof browser.runtime.getBrowserInfo === 'function'
) {
browser.runtime.getBrowserInfo().then(function(info) {
noRemoteResources = info.vendor === 'Mozilla';
});
}
})();
var updateNext = function() {
var assetDict, cacheDict;
@ -1007,6 +1023,10 @@ var updateNext = function() {
if ( cacheEntry && (cacheEntry.writeTime + assetEntry.updateAfter * 86400000) > now ) {
continue;
}
// Update of user scripts/resources forbidden?
if ( assetKey === 'ublock-resources' && noRemoteResources === true ) {
continue;
}
if (
fireNotification(
'before-asset-updated',

View file

@ -105,7 +105,6 @@ var µBlock = (function() { // jshint ignore:line
'behind-the-scene',
'chrome-extension-scheme',
'chrome-scheme',
'loopconversation.about-scheme',
'moz-extension-scheme',
'opera-scheme',
'vivaldi-scheme',

View file

@ -182,30 +182,32 @@ var onInitialize = function(options) {
fetchCloudData();
var html = [
'<button id="cloudPush" type="button" title="cloudPush"></button>',
'<span data-i18n="cloudNoData"></span>',
'<button id="cloudPull" type="button" title="cloudPull" disabled></button>&nbsp;',
'<button id="cloudPullAndMerge" type="button" title="cloudPullAndMerge" disabled></button>',
'<span id="cloudCog" class="fa">&#xf013;</span>',
'<div id="cloudOptions">',
' <div>',
' <p><label data-i18n="cloudDeviceNamePrompt"></label> <input id="cloudDeviceName" type="text" value="">',
' <p><button id="cloudOptionsSubmit" type="button" data-i18n="genericSubmit"></button>',
' </div>',
'</div>',
].join('');
var xhr = new XMLHttpRequest();
xhr.open('GET', 'cloud-ui.html', true);
xhr.overrideMimeType('text/html;charset=utf-8');
xhr.responseType = 'text';
xhr.onload = function() {
this.onload = null;
var parser = new DOMParser(),
parsed = parser.parseFromString(this.responseText, 'text/html'),
fromParent = parsed.body;
while ( fromParent.firstElementChild !== null ) {
widget.appendChild(
document.adoptNode(fromParent.firstElementChild)
);
}
vAPI.insertHTML(widget, html);
vAPI.i18n.render(widget);
widget.classList.remove('hide');
vAPI.i18n.render(widget);
widget.classList.remove('hide');
uDom('#cloudPush').on('click', pushData);
uDom('#cloudPull').on('click', pullData);
uDom('#cloudPullAndMerge').on('click', pullAndMergeData);
uDom('#cloudCog').on('click', openOptions);
uDom('#cloudOptions').on('click', closeOptions);
uDom('#cloudOptionsSubmit').on('click', submitOptions);
uDom('#cloudPush').on('click', pushData);
uDom('#cloudPull').on('click', pullData);
uDom('#cloudPullAndMerge').on('click', pullAndMergeData);
uDom('#cloudCog').on('click', openOptions);
uDom('#cloudOptions').on('click', closeOptions);
uDom('#cloudOptionsSubmit').on('click', submitOptions);
};
xhr.send();
};
messaging.send('cloudWidget', { what: 'cloudGetOptions' }, onInitialize);

View file

@ -77,17 +77,15 @@
*/
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
// Abort execution if our global vAPI object does not exist.
// https://github.com/chrisaljoudi/uBlock/issues/456
// https://github.com/gorhill/uBlock/issues/2029
// Abort execution by throwing if an unexpected condition arise.
// - https://github.com/chrisaljoudi/uBlock/issues/456
if ( typeof vAPI !== 'undefined' ) { // >>>>>>>> start of HUGE-IF-BLOCK
if ( typeof vAPI !== 'object' ) {
throw new Error('uBlock Origin: aborting content scripts for ' + window.location);
}
vAPI.lock();
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
vAPI.matchesProp = (function() {
var docElem = document.documentElement;
@ -131,6 +129,20 @@ vAPI.SafeAnimationFrame.prototype.clear = function() {
/******************************************************************************/
/******************************************************************************/
vAPI.injectScriptlet = function(doc, text) {
if ( !doc ) { return; }
try {
var script = doc.createElement('script');
script.appendChild(doc.createTextNode(text));
(doc.head || doc.documentElement).appendChild(script);
} catch (ex) {
}
};
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
// The DOM filterer is the heart of uBO's cosmetic filtering.
vAPI.domFilterer = (function() {
@ -167,6 +179,10 @@ var cosmeticFiltersActivated = function() {
// Probably no longer need to watch for style tags removal/tampering with fix
// to https://github.com/gorhill/uBlock/issues/963
// https://github.com/gorhill/uBlock/issues/2810
// With Firefox Nightly, it may happens style tags are injected before the
// head element is present.
var platformUserCSS = (function() {
if ( vAPI.userCSS instanceof Object ) {
return vAPI.userCSS;
@ -179,8 +195,9 @@ var platformUserCSS = (function() {
var style = document.createElement('style');
style.setAttribute('type', 'text/css');
style.textContent = css;
if ( document.head ) {
document.head.appendChild(style);
var parent = document.head || document.documentElement;
if ( parent !== null ) {
parent.appendChild(style);
}
this.styles.push(style);
if ( style.sheet ) {
@ -800,7 +817,6 @@ return domFilterer;
if ( !cfeDetails || !cfeDetails.ready ) {
vAPI.domWatcher = vAPI.domCollapser = vAPI.domFilterer =
vAPI.domSurveyor = vAPI.domIsLoaded = null;
vAPI.unlock();
return;
}
@ -836,7 +852,6 @@ return domFilterer;
// Library of resources is located at:
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
if ( cfeDetails.scripts ) {
elem = document.createElement('script');
// Have the injected script tag remove itself when execution completes:
// to keep DOM as clean as possible.
text = cfeDetails.scripts +
@ -848,8 +863,7 @@ return domFilterer;
" p.removeChild(c);\n" +
" }\n" +
"})();";
elem.appendChild(document.createTextNode(text));
parent.appendChild(elem);
vAPI.injectScriptlet(document, text);
vAPI.injectedScripts = text;
}
}
@ -885,7 +899,7 @@ return domFilterer;
vAPI.domWatcher = (function() {
var domLayoutObserver = null,
ignoreTags = { 'head': 1, 'link': 1, 'meta': 1, 'script': 1, 'style': 1 },
ignoreTags = new Set([ 'head', 'link', 'meta', 'script', 'style' ]),
addedNodeLists = [],
addedNodes = [],
removedNodes = false,
@ -894,30 +908,30 @@ vAPI.domWatcher = (function() {
var safeObserverHandler = function() {
safeObserverHandlerTimer.clear();
var i = addedNodeLists.length,
j = addedNodes.length,
nodeList, iNode, node;
while ( i-- ) {
nodeList = addedNodeLists[i];
iNode = nodeList.length;
while ( iNode-- ) {
node = nodeList[iNode];
if ( node.nodeType !== 1 ) {
continue;
if (
node.nodeType === 1 &&
ignoreTags.has(node.localName) === false &&
node.parentElement !== null
) {
addedNodes[j++] = node;
}
if ( ignoreTags[node.localName] === 1 ) {
continue;
}
addedNodes.push(node);
}
}
addedNodeLists.length = 0;
if ( addedNodes.length !== 0 || removedNodes ) {
listeners[0](addedNodes);
if ( listeners[1] ) {
listeners[1](addedNodes);
}
addedNodes.length = 0;
removedNodes = false;
if ( j === 0 && removedNodes === false ) { return; }
listeners[0](addedNodes);
if ( listeners[1] ) {
listeners[1](addedNodes);
}
addedNodes.length = 0;
removedNodes = false;
};
var safeObserverHandlerTimer = new vAPI.SafeAnimationFrame(safeObserverHandler);
@ -994,78 +1008,94 @@ vAPI.domWatcher = (function() {
/******************************************************************************/
vAPI.domCollapser = (function() {
var timer = null;
var pendingRequests = Object.create(null);
var roundtripRequests = [];
var resquestIdGenerator = 1,
processTimer,
toProcess = [],
toFilter = [],
toCollapse = new Map(),
cachedBlockedSet,
cachedBlockedSetHash,
cachedBlockedSetTimer;
var src1stProps = {
'embed': 'src',
'iframe': 'src',
'img': 'src',
'object': 'data'
};
var src2ndProps = {
'img': 'srcset'
};
var netSelectorCacheCount = 0;
var messaging = vAPI.messaging;
var tagToTypeMap = {
embed: 'object',
iframe: 'sub_frame',
img: 'image',
object: 'object'
};
var netSelectorCacheCount = 0,
messaging = vAPI.messaging;
// Because a while ago I have observed constructors are faster than
// literal object instanciations.
var RoundtripRequest = function(tag, attr, url) {
this.tag = tag;
this.attr = attr;
this.url = url;
this.collapse = false;
var cachedBlockedSetClear = function() {
cachedBlockedSet =
cachedBlockedSetHash =
cachedBlockedSetTimer = undefined;
};
// https://github.com/chrisaljoudi/uBlock/issues/174
// Do not remove fragment from src URL
var onProcessed = function(response) {
// This can happens if uBO is restarted.
if ( !response ) {
if ( !response ) { // This happens if uBO is disabled or restarted.
toCollapse.clear();
return;
}
var requests = response.result;
if ( requests === null || Array.isArray(requests) === false ) {
var targets = toCollapse.get(response.id);
if ( targets === undefined ) { return; }
toCollapse.delete(response.id);
if ( cachedBlockedSetHash !== response.hash ) {
cachedBlockedSet = new Set(response.blockedResources);
cachedBlockedSetHash = response.hash;
if ( cachedBlockedSetTimer !== undefined ) {
clearTimeout(cachedBlockedSetTimer);
}
cachedBlockedSetTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000);
}
if ( cachedBlockedSet === undefined || cachedBlockedSet.size === 0 ) {
return;
}
var selectors = [],
iframeLoadEventPatch = vAPI.iframeLoadEventPatch,
netSelectorCacheCountMax = response.netSelectorCacheCountMax,
aa = [ null ],
request, key, entry, target, value;
// https://github.com/gorhill/uBlock/issues/2256
var iframeLoadEventPatch = vAPI.iframeLoadEventPatch;
// Important: process in chronological order -- this ensures the
// cached selectors are the most useful ones.
for ( var i = 0, ni = requests.length; i < ni; i++ ) {
request = requests[i];
key = request.tag + ' ' + request.attr + ' ' + request.url;
entry = pendingRequests[key];
if ( entry === undefined ) {
tag, prop, src, value;
for ( var target of targets ) {
tag = target.localName;
prop = src1stProps[tag];
if ( prop === undefined ) { continue; }
src = target[prop];
if ( typeof src !== 'string' || src.length === 0 ) {
prop = src2ndProps[tag];
if ( prop === undefined ) { continue; }
src = target[prop];
if ( typeof src !== 'string' || src.length === 0 ) { continue; }
}
if ( cachedBlockedSet.has(tagToTypeMap[tag] + ' ' + src) === false ) {
continue;
}
delete pendingRequests[key];
// https://github.com/chrisaljoudi/uBlock/issues/869
if ( !request.collapse ) {
continue;
// https://github.com/chrisaljoudi/uBlock/issues/399
// Never remove elements from the DOM, just hide them
target.style.setProperty('display', 'none', 'important');
target.hidden = true;
// https://github.com/chrisaljoudi/uBlock/issues/1048
// Use attribute to construct CSS rule
if (
netSelectorCacheCount <= netSelectorCacheCountMax &&
(value = target.getAttribute(prop))
) {
selectors.push(tag + '[' + prop + '="' + value + '"]');
netSelectorCacheCount += 1;
}
if ( Array.isArray(entry) === false ) {
aa[0] = entry;
entry = aa;
}
for ( var j = 0, nj = entry.length; j < nj; j++ ) {
target = entry[j];
// https://github.com/chrisaljoudi/uBlock/issues/399
// Never remove elements from the DOM, just hide them
target.style.setProperty('display', 'none', 'important');
target.hidden = true;
// https://github.com/chrisaljoudi/uBlock/issues/1048
// Use attribute to construct CSS rule
if (
netSelectorCacheCount <= netSelectorCacheCountMax &&
(value = target.getAttribute(request.attr))
) {
selectors.push(request.tag + '[' + request.attr + '="' + value + '"]');
netSelectorCacheCount += 1;
}
if ( iframeLoadEventPatch ) { iframeLoadEventPatch(target); }
if ( iframeLoadEventPatch !== undefined ) {
iframeLoadEventPatch(target);
}
}
if ( selectors.length !== 0 ) {
@ -1082,9 +1112,10 @@ vAPI.domCollapser = (function() {
};
var send = function() {
timer = null;
processTimer = undefined;
toCollapse.set(resquestIdGenerator, toProcess);
// https://github.com/gorhill/uBlock/issues/1927
// Normalize hostname to avoid trailing dot of FQHN.
// Normalize hostname to avoid trailing dot of FQHN.
var pageHostname = window.location.hostname || '';
if (
pageHostname.length &&
@ -1092,62 +1123,34 @@ vAPI.domCollapser = (function() {
) {
pageHostname = pageHostname.slice(0, -1);
}
messaging.send(
'contentscript',
{
what: 'filterRequests',
pageURL: window.location.href,
pageHostname: pageHostname,
requests: roundtripRequests
}, onProcessed
);
roundtripRequests = [];
var msg = {
what: 'getCollapsibleBlockedRequests',
id: resquestIdGenerator,
pageURL: window.location.href,
pageHostname: pageHostname,
resources: toFilter,
hash: cachedBlockedSetHash
};
messaging.send('contentscript', msg, onProcessed);
toProcess = [];
toFilter = [];
resquestIdGenerator += 1;
};
var process = function(delay) {
if ( roundtripRequests.length === 0 ) {
return;
}
if ( toProcess.length === 0 ) { return; }
if ( delay === 0 ) {
clearTimeout(timer);
if ( processTimer !== undefined ) {
clearTimeout(processTimer);
}
send();
} else if ( timer === null ) {
timer = vAPI.setTimeout(send, delay || 20);
} else if ( processTimer === undefined ) {
processTimer = vAPI.setTimeout(send, delay || 20);
}
};
var add = function(target) {
var tag = target.localName;
var prop = src1stProps[tag];
if ( prop === undefined ) {
return;
}
// https://github.com/chrisaljoudi/uBlock/issues/174
// Do not remove fragment from src URL
var src = target[prop];
if ( typeof src !== 'string' || src.length === 0 ) {
prop = src2ndProps[tag];
if ( prop === undefined ) {
return;
}
src = target[prop];
if ( typeof src !== 'string' || src.length === 0 ) {
return;
}
}
if ( src.lastIndexOf('http', 0) !== 0 ) {
return;
}
var key = tag + ' ' + prop + ' ' + src,
entry = pendingRequests[key];
if ( entry === undefined ) {
pendingRequests[key] = target;
roundtripRequests.push(new RoundtripRequest(tag, prop, src));
} else if ( Array.isArray(entry) ) {
entry.push(target);
} else {
pendingRequests[key] = [ entry, target ];
}
toProcess[toProcess.length] = target;
};
var addMany = function(targets) {
@ -1177,12 +1180,7 @@ vAPI.domCollapser = (function() {
// and which scripts are selectively looked-up from:
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/resources.txt
if ( vAPI.injectedScripts ) {
var scriptTag = document.createElement('script');
scriptTag.appendChild(document.createTextNode(vAPI.injectedScripts));
var parent = iframe.contentDocument && iframe.contentDocument.head;
if ( parent ) {
parent.appendChild(scriptTag);
}
vAPI.injectScriptlet(iframe.contentDocument, vAPI.injectedScripts);
}
};
@ -1198,19 +1196,12 @@ vAPI.domCollapser = (function() {
primeLocalIFrame(iframe);
return;
}
if ( src.lastIndexOf('http', 0) !== 0 ) {
return;
}
var key = 'iframe' + ' ' + 'src' + ' ' + src,
entry = pendingRequests[key];
if ( entry === undefined ) {
pendingRequests[key] = iframe;
roundtripRequests.push(new RoundtripRequest('iframe', 'src', src));
} else if ( Array.isArray(entry) ) {
entry.push(iframe);
} else {
pendingRequests[key] = [ entry, iframe ];
}
if ( src.lastIndexOf('http', 0) !== 0 ) { return; }
toFilter[toFilter.length] = {
type: 'sub_frame',
url: iframe.src
};
add(iframe);
};
var addIFrames = function(iframes) {
@ -1221,8 +1212,10 @@ vAPI.domCollapser = (function() {
};
var onResourceFailed = function(ev) {
vAPI.domCollapser.add(ev.target);
vAPI.domCollapser.process();
if ( tagToTypeMap[ev.target.localName] !== undefined ) {
vAPI.domCollapser.add(ev.target);
vAPI.domCollapser.process();
}
};
var domChangedHandler = function(nodes) {
@ -1247,7 +1240,6 @@ vAPI.domCollapser = (function() {
// - Future requests not blocked yet
// - Elements dynamically added to the page
// - Elements which resource URL changes
// https://github.com/chrisaljoudi/uBlock/issues/7
// Preferring getElementsByTagName over querySelectorAll:
// http://jsperf.com/queryselectorall-vs-getelementsbytagname/145
@ -1270,8 +1262,8 @@ vAPI.domCollapser = (function() {
vAPI.shutdown.add(function() {
document.removeEventListener('error', onResourceFailed, true);
vAPI.domWatcher.removeListener(domChangedHandler);
if ( timer !== null ) {
clearTimeout(timer);
if ( processTimer !== undefined ) {
clearTimeout(processTimer);
}
});
};
@ -1559,7 +1551,7 @@ vAPI.domSurveyor = (function() {
v = node.id;
if ( typeof v !== 'string' ) { continue; }
v = '#' + v.trim();
if ( !qq.has(v) && v.length !== 1 ) {
if ( qq.has(v) === false && v.length !== 1 ) {
ll[lli] = v; lli++; qq.add(v);
}
}
@ -1569,9 +1561,9 @@ vAPI.domSurveyor = (function() {
node = nodes[i];
vv = node.className;
if ( typeof vv !== 'string' ) { continue; }
if ( !rews.test(vv) ) {
if ( rews.test(vv) === false ) {
v = '.' + vv;
if ( !qq.has(v) && v.length !== 1 ) {
if ( qq.has(v) === false && v.length !== 1 ) {
ll[lli] = v; lli++; qq.add(v);
}
} else {
@ -1579,7 +1571,7 @@ vAPI.domSurveyor = (function() {
j = vv.length;
while ( j-- ) {
v = '.' + vv[j];
if ( !qq.has(v) ) {
if ( qq.has(v) === false ) {
ll[lli] = v; lli++; qq.add(v);
}
}
@ -1623,9 +1615,7 @@ vAPI.domSurveyor = (function() {
vAPI.domIsLoaded = function(ev) {
// This can happen on Firefox. For instance:
// https://github.com/gorhill/uBlock/issues/1893
if ( window.location === null ) {
return;
}
if ( window.location === null ) { return; }
var slowLoad = ev instanceof Event;
if ( slowLoad ) {
@ -1690,3 +1680,5 @@ vAPI.domIsLoaded = function(ev) {
/******************************************************************************/
/******************************************************************************/
/******************************************************************************/
} // <<<<<<<< end of HUGE-IF-BLOCK

View file

@ -27,6 +27,12 @@
/******************************************************************************/
if ( vAPI.contextMenu === undefined ) {
return {
update: function() {}
};
}
var µb = µBlock;
/******************************************************************************/

View file

@ -1529,6 +1529,15 @@ FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) {
return;
}
// https://github.com/gorhill/uBlock/issues/2835
// Do not inject scriptlets if the site is under an `allow` rule.
if (
µb.userSettings.advancedUserEnabled === true &&
µb.sessionFirewall.evaluateCellZY(hostname, hostname, '*') === 2
) {
return;
}
// Exceptions should be rare, so we check for exception only if there are
// scriptlets returned.
var exceptions = [], j, token;

View file

@ -701,16 +701,22 @@ var netFilteringManager = (function() {
};
var parseStaticInputs = function() {
var filter = '';
var options = [];
var block = selectValue('select.static.action') === '';
var filter = '',
options = [],
block = selectValue('select.static.action') === '';
if ( !block ) {
filter = '@@';
}
var value = selectValue('select.static.url');
if ( value !== '' ) {
filter += '||' + value;
if ( value.slice(-1) === '/' ) {
value += '*';
} else if ( /[/?]/.test(value) === false ) {
value += '^';
}
value = '||' + value;
}
filter += value;
value = selectValue('select.static.type');
if ( value !== '' ) {
options.push(uglyTypeFromSelector('static'));
@ -974,7 +980,7 @@ var netFilteringManager = (function() {
if ( pos === -1 ) {
pos = path.length;
}
urls.unshift(rootURL + path.slice(0, pos));
urls.unshift(rootURL + path.slice(0, pos + 1));
}
var query = matches[4] || '';
if ( query !== '') {
@ -1042,7 +1048,7 @@ var netFilteringManager = (function() {
var rePlaceholder = /\{\{[^}]+?\}\}/g;
var nodes = [];
var match, pos = 0;
var select, option, i, value;
var select, option, n, i, value;
for (;;) {
match = rePlaceholder.exec(template);
if ( match === null ) {
@ -1088,8 +1094,8 @@ var netFilteringManager = (function() {
case '{{url}}':
select = document.createElement('select');
select.className = 'static url';
for ( i = 0; i < targetURLs.length; i++ ) {
value = targetURLs[i].replace(/^[a-z]+:\/\//, '');
for ( i = 0, n = targetURLs.length; i < n; i++ ) {
value = targetURLs[i].replace(/^[a-z-]+:\/\//, '');
option = document.createElement('option');
option.setAttribute('value', value);
option.textContent = shortenLongString(value, 128);
@ -1221,8 +1227,8 @@ var reverseLookupManager = (function() {
if ( Array.isArray(lists) === false || lists.length === 0 ) {
return null;
}
var node;
var p = document.createElement('p');
var node,
p = document.createElement('p');
reSentence1.lastIndex = 0;
var matches = reSentence1.exec(sentence1Template);
@ -1231,7 +1237,10 @@ var reverseLookupManager = (function() {
} else {
node = uDom.nodeFromSelector('#filterFinderDialogSentence1 > span').cloneNode(true);
node.childNodes[0].textContent = sentence1Template.slice(0, matches.index);
node.childNodes[1].textContent = filter;
// https://github.com/gorhill/uBlock/issues/2753
node.childNodes[1].textContent = filter.length <= 1024
? filter
: filter.slice(0, 1023) + '…';
node.childNodes[2].textContent = sentence1Template.slice(reSentence1.lastIndex);
}
p.appendChild(node);
@ -1267,9 +1276,7 @@ var reverseLookupManager = (function() {
for ( var filter in response ) {
var p = nodeFromFilter(filter, response[filter]);
if ( p === null ) {
continue;
}
if ( p === null ) { continue; }
dialog.appendChild(p);
}

View file

@ -452,56 +452,6 @@ vAPI.messaging.listen('popupPanel', onMessage);
/******************************************************************************/
var µb = µBlock;
/******************************************************************************/
var tagNameToRequestTypeMap = {
'embed': 'object',
'iframe': 'sub_frame',
'img': 'image',
'object': 'object'
};
/******************************************************************************/
// Evaluate many requests.
// https://github.com/gorhill/uBlock/issues/1782
// Treat `data:` URIs as 1st-party resources.
var filterRequests = function(pageStore, details) {
var requests = details.requests;
if ( µb.userSettings.collapseBlocked === false ) {
return requests;
}
//console.debug('messaging.js/contentscript-end.js: processing %d requests', requests.length);
var hostnameFromURI = µb.URI.hostnameFromURI,
redirectEngine = µb.redirectEngine,
punycodeURL = vAPI.punycodeURL;
// Create evaluation context
var context = pageStore.createContextFromFrameHostname(details.pageHostname),
request,
i = requests.length;
while ( i-- ) {
request = requests[i];
context.requestURL = punycodeURL(request.url);
context.requestHostname = hostnameFromURI(context.requestURL);
context.requestType = tagNameToRequestTypeMap[request.tag];
if ( pageStore.filterRequest(context) !== 1 ) { continue; }
// Redirected? (We do not hide redirected resources.)
request.collapse = redirectEngine.matches(context) !== true;
}
context.dispose();
return requests;
};
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
@ -510,14 +460,29 @@ var onMessage = function(request, sender, callback) {
}
// Sync
var response;
var pageStore;
var µb = µBlock,
response,
pageStore;
if ( sender && sender.tab ) {
pageStore = µb.pageStoreFromTabId(sender.tab.id);
}
switch ( request.what ) {
case 'getCollapsibleBlockedRequests':
response = {
id: request.id,
hash: request.hash,
netSelectorCacheCountMax: µb.cosmeticFilteringEngine.netSelectorCacheCountMax
};
if (
µb.userSettings.collapseBlocked &&
pageStore &&
pageStore.getNetFilteringSwitch()
) {
pageStore.getBlockedResources(request, response);
}
break;
case 'retrieveContentScriptParameters':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = {
@ -541,15 +506,6 @@ var onMessage = function(request, sender, callback) {
}
break;
case 'filterRequests':
if ( pageStore && pageStore.getNetFilteringSwitch() ) {
response = {
result: filterRequests(pageStore, request),
netSelectorCacheCountMax: µb.cosmeticFilteringEngine.netSelectorCacheCountMax
};
}
break;
default:
return vAPI.messaging.UNHANDLED;
}

View file

@ -1,7 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2016 Raymond Hill
Copyright (C) 2014-2017 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
@ -43,59 +43,22 @@ var µb = µBlock;
/******************************************************************************/
// To mitigate memory churning
var netFilteringResultCacheEntryJunkyard = [];
var netFilteringResultCacheEntryJunkyardMax = 200;
/******************************************************************************/
var NetFilteringResultCacheEntry = function(result, type, logData) {
this.init(result, type, logData);
};
/******************************************************************************/
NetFilteringResultCacheEntry.prototype.init = function(result, type, logData) {
this.result = result;
this.type = type;
this.time = Date.now();
this.logData = logData;
return this;
};
/******************************************************************************/
NetFilteringResultCacheEntry.prototype.dispose = function() {
this.result = this.type = '';
this.logData = undefined;
if ( netFilteringResultCacheEntryJunkyard.length < netFilteringResultCacheEntryJunkyardMax ) {
netFilteringResultCacheEntryJunkyard.push(this);
}
};
/******************************************************************************/
NetFilteringResultCacheEntry.factory = function(result, type, logData) {
if ( netFilteringResultCacheEntryJunkyard.length ) {
return netFilteringResultCacheEntryJunkyard.pop().init(result, type, logData);
}
return new NetFilteringResultCacheEntry(result, type, logData);
};
/******************************************************************************/
/******************************************************************************/
// To mitigate memory churning
var netFilteringCacheJunkyard = [];
var netFilteringCacheJunkyardMax = 10;
var netFilteringCacheJunkyard = [],
netFilteringCacheJunkyardMax = 10;
/******************************************************************************/
var NetFilteringResultCache = function() {
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
this.init();
};
/******************************************************************************/
NetFilteringResultCache.prototype.shelfLife = 15 * 1000;
/******************************************************************************/
NetFilteringResultCache.factory = function() {
var entry = netFilteringCacheJunkyard.pop();
if ( entry === undefined ) {
@ -109,18 +72,16 @@ NetFilteringResultCache.factory = function() {
/******************************************************************************/
NetFilteringResultCache.prototype.init = function() {
this.urls = Object.create(null);
this.count = 0;
this.shelfLife = 15 * 1000;
this.blocked = new Map();
this.results = new Map();
this.hash = 0;
this.timer = null;
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
};
/******************************************************************************/
NetFilteringResultCache.prototype.dispose = function() {
this.empty();
this.boundPruneAsyncCallback = null;
if ( netFilteringCacheJunkyard.length < netFilteringCacheJunkyardMax ) {
netFilteringCacheJunkyard.push(this);
}
@ -129,33 +90,42 @@ NetFilteringResultCache.prototype.dispose = function() {
/******************************************************************************/
NetFilteringResultCache.prototype.add = function(context, result, logData) {
var url = context.requestURL,
type = context.requestType,
key = type + ' ' + url,
entry = this.urls[key];
if ( entry !== undefined ) {
entry.result = result;
entry.type = type;
entry.time = Date.now();
entry.logData = logData;
return;
}
this.urls[key] = NetFilteringResultCacheEntry.factory(result, type, logData);
if ( this.count === 0 ) {
NetFilteringResultCache.prototype.rememberResult = function(context, result, logData) {
if ( this.results.size === 0 ) {
this.pruneAsync();
}
this.count += 1;
var key = context.pageHostname + ' ' + context.requestType + ' ' + context.requestURL;
this.results.set(key, {
result: result,
logData: logData,
tstamp: Date.now()
});
if ( result !== 1 ) { return; }
var now = Date.now();
this.blocked.set(key, now);
this.hash = now;
};
/******************************************************************************/
NetFilteringResultCache.prototype.rememberBlock = function(details) {
if ( this.blocked.size === 0 ) {
this.pruneAsync();
}
var now = Date.now();
this.blocked.set(
details.pageHostname + ' ' + details.requestType + ' ' + details.requestURL,
now
);
this.hash = now;
};
/******************************************************************************/
NetFilteringResultCache.prototype.empty = function() {
for ( var key in this.urls ) {
this.urls[key].dispose();
}
this.urls = Object.create(null);
this.count = 0;
this.blocked.clear();
this.results.clear();
this.hash = 0;
if ( this.timer !== null ) {
clearTimeout(this.timer);
this.timer = null;
@ -164,36 +134,6 @@ NetFilteringResultCache.prototype.empty = function() {
/******************************************************************************/
NetFilteringResultCache.prototype.compareEntries = function(a, b) {
return this.urls[b].time - this.urls[a].time;
};
/******************************************************************************/
NetFilteringResultCache.prototype.prune = function() {
var keys = Object.keys(this.urls).sort(this.compareEntries.bind(this));
var obsolete = Date.now() - this.shelfLife;
var key, entry;
var i = keys.length;
while ( i-- ) {
key = keys[i];
entry = this.urls[key];
if ( entry.time > obsolete ) {
break;
}
entry.dispose();
delete this.urls[key];
}
this.count -= keys.length - i - 1;
if ( this.count > 0 ) {
this.pruneAsync();
}
};
// https://www.youtube.com/watch?v=hcVpbsDyOhM
/******************************************************************************/
NetFilteringResultCache.prototype.pruneAsync = function() {
if ( this.timer === null ) {
this.timer = vAPI.setTimeout(this.boundPruneAsyncCallback, this.shelfLife * 2);
@ -202,13 +142,46 @@ NetFilteringResultCache.prototype.pruneAsync = function() {
NetFilteringResultCache.prototype.pruneAsyncCallback = function() {
this.timer = null;
this.prune();
var obsolete = Date.now() - this.shelfLife,
entry;
for ( entry of this.blocked ) {
if ( entry[1] <= obsolete ) {
this.results.delete(entry[0]);
this.blocked.delete(entry[0]);
}
}
for ( entry of this.results ) {
if ( entry[1].tstamp <= obsolete ) {
this.results.delete(entry[0]);
}
}
if ( this.blocked.size !== 0 || this.results.size !== 0 ) {
this.pruneAsync();
}
};
/******************************************************************************/
NetFilteringResultCache.prototype.lookup = function(context) {
return this.urls[context.requestType + ' ' + context.requestURL] || undefined;
NetFilteringResultCache.prototype.lookupResult = function(context) {
return this.results.get(
context.pageHostname + ' ' +
context.requestType + ' ' +
context.requestURL
);
};
/******************************************************************************/
NetFilteringResultCache.prototype.lookupAllBlocked = function(hostname) {
var result = [],
pos;
for ( var entry of this.blocked ) {
pos = entry[0].indexOf(' ');
if ( entry[0].slice(0, pos) === hostname ) {
result[result.length] = entry[0].slice(pos + 1);
}
}
return result;
};
/******************************************************************************/
@ -610,24 +583,39 @@ PageStore.prototype.journalProcess = function(fromTimer) {
PageStore.prototype.filterRequest = function(context) {
this.logData = undefined;
var requestType = context.requestType;
// We want to short-term cache filtering results of collapsible types,
// because they are likely to be reused, from network request handler and
// from content script handler.
if ( 'image media object sub_frame'.indexOf(requestType) === -1 ) {
return this.filterRequestNoCache(context);
}
if ( this.getNetFilteringSwitch() === false ) {
this.netFilteringCache.add(context, 0);
return 0;
}
var entry = this.netFilteringCache.lookup(context);
if ( entry !== undefined ) {
this.logData = entry.logData;
return entry.result;
var requestType = context.requestType;
if ( requestType === 'csp_report' ) {
if ( this.internalRedirectionCount !== 0 ) {
if ( µb.logger.isEnabled() ) {
this.logData = { result: 1, source: 'global', raw: 'no-spurious-csp-report' };
}
return 1;
}
}
if ( requestType === 'font' ) {
this.remoteFontCount += 1;
if ( µb.hnSwitches.evaluateZ('no-remote-fonts', context.rootHostname) !== false ) {
if ( µb.logger.isEnabled() ) {
this.logData = µb.hnSwitches.toLogData();
}
return 1;
}
}
var cacheableResult = this.cacheableResults[requestType] === true;
if ( cacheableResult ) {
var entry = this.netFilteringCache.lookupResult(context);
if ( entry !== undefined ) {
this.logData = entry.logData;
return entry.result;
}
}
// Dynamic URL filtering.
@ -638,13 +626,13 @@ PageStore.prototype.filterRequest = function(context) {
// Dynamic hostname/type filtering.
if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
result = µb.sessionFirewall.evaluateCellZY( context.rootHostname, context.requestHostname, requestType);
result = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, requestType);
if ( result !== 0 && result !== 3 && µb.logger.isEnabled() ) {
this.logData = µb.sessionFirewall.toLogData();
}
}
}
// Static filtering: lowest filtering precedence.
// Static filtering has lowest precedence.
if ( result === 0 || result === 3 ) {
result = µb.staticNetFilteringEngine.matchString(context);
if ( result !== 0 && µb.logger.isEnabled() ) {
@ -652,11 +640,26 @@ PageStore.prototype.filterRequest = function(context) {
}
}
this.netFilteringCache.add(context, result, this.logData);
if ( cacheableResult ) {
this.netFilteringCache.rememberResult(context, result, this.logData);
} else if ( result === 1 && this.collapsibleResources[requestType] === true ) {
this.netFilteringCache.rememberBlock(context, true);
}
return result;
};
PageStore.prototype.cacheableResults = {
sub_frame: true
};
PageStore.prototype.collapsibleResources = {
image: true,
media: true,
object: true,
sub_frame: true
};
/******************************************************************************/
// The caller is responsible to check whether filtering is enabled or not.
@ -689,67 +692,28 @@ PageStore.prototype.filterLargeMediaElement = function(size) {
return 1;
};
// https://www.youtube.com/watch?v=drW8p_dTLD4
/******************************************************************************/
PageStore.prototype.filterRequestNoCache = function(context) {
this.logData = undefined;
if ( this.getNetFilteringSwitch() === false ) {
return 0;
}
var requestType = context.requestType;
if ( requestType === 'csp_report' ) {
if ( this.internalRedirectionCount !== 0 ) {
if ( µb.logger.isEnabled() ) {
this.logData = { result: 1, source: 'global', raw: 'no-spurious-csp-report' };
}
return 1;
PageStore.prototype.getBlockedResources = function(request, response) {
var resources = request.resources;
if ( Array.isArray(resources) && resources.length !== 0 ) {
var context = this.createContextFromFrameHostname(request.pageHostname);
for ( var resource of resources ) {
context.requestType = resource.type;
context.requestHostname = µb.URI.hostnameFromURI(resource.url);
context.requestURL = resource.url;
this.filterRequest(context);
}
}
if ( requestType === 'font' ) {
this.remoteFontCount += 1;
if ( µb.hnSwitches.evaluateZ('no-remote-fonts', context.rootHostname) !== false ) {
if ( µb.logger.isEnabled() ) {
this.logData = µb.hnSwitches.toLogData();
}
return 1;
}
if ( this.netFilteringCache.hash === response.hash ) {
return;
}
var result = 0;
// Dynamic URL filtering.
if ( result === 0 ) {
result = µb.sessionURLFiltering.evaluateZ(context.rootHostname, context.requestURL, requestType);
if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.sessionURLFiltering.toLogData();
}
}
// Dynamic hostname/type filtering.
if ( result === 0 && µb.userSettings.advancedUserEnabled ) {
result = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, requestType);
if ( result !== 0 && result !== 3 && µb.logger.isEnabled() ) {
this.logData = µb.sessionFirewall.toLogData();
}
}
// Static filtering has lowest precedence.
if ( result === 0 || result === 3 ) {
result = µb.staticNetFilteringEngine.matchString(context);
if ( result !== 0 && µb.logger.isEnabled() ) {
this.logData = µb.staticNetFilteringEngine.toLogData();
}
}
return result;
response.hash = this.netFilteringCache.hash;
response.blockedResources = this.netFilteringCache.lookupAllBlocked(request.pageHostname);
};
// https://www.youtube.com/watch?v=drW8p_dTLD4
/******************************************************************************/
return {

View file

@ -999,7 +999,6 @@ var onHideTooltip = function() {
uDom('#switch').on('click', toggleNetFilteringSwitch);
uDom('#gotoZap').on('click', gotoZap);
uDom('#gotoPick').on('click', gotoPick);
uDom('a[href]').on('click', gotoURL);
uDom('h2').on('click', toggleFirewallPane);
uDom('#refresh').on('click', reloadTab);
uDom('.hnSwitch').on('click', toggleHostnameSwitch);
@ -1009,6 +1008,8 @@ var onHideTooltip = function() {
uDom('body').on('mouseenter', '[data-tip]', onShowTooltip)
.on('mouseleave', '[data-tip]', onHideTooltip);
uDom('a[href]').on('click', gotoURL);
})();
/******************************************************************************/

View file

@ -1546,6 +1546,7 @@ pickerRoot.style.cssText = [
'left: 0',
'margin: 0',
'max-height: none',
'max-width: none',
'opacity: 1',
'outline: 0',
'padding: 0',

View file

@ -127,7 +127,10 @@ var onVersionReady = function(lastVersion) {
/******************************************************************************/
var onSelfieReady = function(selfie) {
if ( selfie === null || selfie.magic !== µb.systemSettings.selfieMagic ) {
if (
selfie instanceof Object === false ||
selfie.magic !== µb.systemSettings.selfieMagic
) {
return false;
}
if ( publicSuffixList.fromSelfie(selfie.publicSuffixList) !== true ) {
@ -221,13 +224,13 @@ var onFirstFetchReady = function(fetched) {
onNetWhitelistReady(fetched.netWhitelist);
onVersionReady(fetched.version);
// If we have a selfie, skip loading PSL, filters
if ( onSelfieReady(fetched.selfie) ) {
onAllReady();
return;
}
µb.loadPublicSuffixList(onPSLReady);
// If we have a selfie, skip loading PSL, filter lists
vAPI.cacheStorage.get('selfie', function(bin) {
if ( bin instanceof Object && onSelfieReady(bin.selfie) ) {
return onAllReady();
}
µb.loadPublicSuffixList(onPSLReady);
});
};
/******************************************************************************/
@ -266,7 +269,6 @@ var onSelectedFilterListsLoaded = function() {
'lastBackupFile': '',
'lastBackupTime': 0,
'netWhitelist': µb.netWhitelistDefault,
'selfie': null,
'selfieMagic': '',
'version': '0.0.0.0'
};

View file

@ -200,7 +200,11 @@ var rawToRegexStr = function(s, anchor) {
.replace(me.escape3, '')
.replace(me.escape4, '[^ ]*?');
if ( anchor & 0x4 ) {
reStr = '[0-9a-z.-]*?' + reStr;
reStr = (
reStr.startsWith('\\.') ?
rawToRegexStr.reTextHostnameAnchor2 :
rawToRegexStr.reTextHostnameAnchor1
) + reStr;
} else if ( anchor & 0x2 ) {
reStr = '^' + reStr;
}
@ -213,6 +217,8 @@ rawToRegexStr.escape1 = /[.+?${}()|[\]\\]/g;
rawToRegexStr.escape2 = /\^/g;
rawToRegexStr.escape3 = /^\*|\*$/g;
rawToRegexStr.escape4 = /\*/g;
rawToRegexStr.reTextHostnameAnchor1 = '^[a-z-]+://(?:[^/?#]+\\.)?';
rawToRegexStr.reTextHostnameAnchor2 = '^[a-z-]+://(?:[^/?#]+)?';
var filterFingerprinter = µb.CompiledLineWriter.fingerprint;
@ -449,7 +455,7 @@ FilterPlainHostname.prototype.match = function() {
FilterPlainHostname.prototype.logData = function() {
return {
raw: '||' + this.s + '^',
regex: rawToRegexStr(this.s, 0x4),
regex: rawToRegexStr(this.s + '^'),
compiled: this.compile()
};
};
@ -623,14 +629,13 @@ FilterGenericHnAnchored.prototype.match = function(url) {
if ( this.re === null ) {
this.re = new RegExp(rawToRegexStr(this.s, this.anchor));
}
var matchStart = url.search(this.re);
return matchStart !== -1 && isHnAnchored(url, matchStart);
return this.re.test(url);
};
FilterGenericHnAnchored.prototype.logData = function() {
var out = {
raw: '||' + this.s,
regex: this.re.source,
regex: rawToRegexStr(this.s, this.anchor & ~0x4),
compiled: this.compile()
};
return out;
@ -693,23 +698,30 @@ registerFilterClass(FilterGenericHnAndRightAnchored);
/******************************************************************************/
var FilterRegex = function(s) {
this.re = new RegExp(s, 'i');
this.re = s;
};
FilterRegex.prototype.match = function(url) {
if ( typeof this.re === 'string' ) {
this.re = new RegExp(this.re, 'i');
}
return this.re.test(url);
};
FilterRegex.prototype.logData = function() {
var s = typeof this.re === 'string' ? this.re : this.re.source;
return {
raw: '/' + this.re.source + '/',
regex: this.re.source,
raw: '/' + s + '/',
regex: s,
compiled: this.compile()
};
};
FilterRegex.prototype.compile = function() {
return [ this.fid, this.re.source ];
return [
this.fid,
typeof this.re === 'string' ? this.re : this.re.source
];
};
FilterRegex.compile = function(details) {
@ -1794,6 +1806,10 @@ FilterParser.prototype.parse = function(raw) {
// Hostname-anchored with no wildcard always have a token index of 0.
var reHostnameToken = /^[0-9a-z]+/;
var reGoodToken = /[%0-9a-z]{2,}/g;
var reRegexToken = /[%0-9A-Za-z]{2,}/g;
var reRegexTokenAbort = /[([]/;
var reRegexBadPrefix = /(^|[^\\]\.|[*?{}\\])$/;
var reRegexBadSuffix = /^([^\\]\.|\\[dw]|[([{}?*]|$)/;
var badTokens = new Set([
'com',
@ -1808,9 +1824,10 @@ var badTokens = new Set([
'www'
]);
var findFirstGoodToken = function(s) {
FilterParser.prototype.findFirstGoodToken = function() {
reGoodToken.lastIndex = 0;
var matches, lpos,
var s = this.f,
matches, lpos,
badTokenMatch = null;
while ( (matches = reGoodToken.exec(s)) !== null ) {
// https://github.com/gorhill/uBlock/issues/997
@ -1833,19 +1850,48 @@ var findFirstGoodToken = function(s) {
return badTokenMatch;
};
FilterParser.prototype.extractTokenFromRegex = function() {
reRegexToken.lastIndex = 0;
var s = this.f,
matches, prefix;
while ( (matches = reRegexToken.exec(s)) !== null ) {
prefix = s.slice(0, matches.index);
if ( reRegexTokenAbort.test(prefix) ) { return; }
if (
reRegexBadPrefix.test(prefix) ||
reRegexBadSuffix.test(s.slice(reRegexToken.lastIndex))
) {
continue;
}
this.token = matches[0].toLowerCase();
this.tokenHash = µb.urlTokenizer.tokenHashFromString(this.token);
this.tokenBeg = matches.index;
if ( badTokens.has(this.token) === false ) { break; }
}
};
/******************************************************************************/
// https://github.com/chrisaljoudi/uBlock/issues/1038
// Single asterisk will match any URL.
// https://github.com/gorhill/uBlock/issues/2781
// For efficiency purpose, try to extract a token from a regex-based filter.
FilterParser.prototype.makeToken = function() {
// https://github.com/chrisaljoudi/uBlock/issues/1038
// Single asterisk will match any URL.
if ( this.isRegex || this.f === '*' ) { return; }
if ( this.isRegex ) {
this.extractTokenFromRegex();
return;
}
if ( this.f === '*' ) { return; }
var matches = null;
if ( (this.anchor & 0x4) !== 0 && this.f.indexOf('*') === -1 ) {
matches = reHostnameToken.exec(this.f);
}
if ( matches === null ) {
matches = findFirstGoodToken(this.f);
matches = this.findFirstGoodToken();
}
if ( matches !== null ) {
this.token = matches[0];

View file

@ -467,7 +467,7 @@
listKey = importedListKeys[i];
entry = {
content: 'filters',
contentURL: importedListKeys[i],
contentURL: listKey,
external: true,
group: 'custom',
submitter: 'user',
@ -477,6 +477,31 @@
this.assets.registerAssetSource(listKey, entry);
}
// Convert a no longer existing stock list into an imported list.
var customListFromStockList = function(assetKey) {
var oldEntry = oldAvailableLists[assetKey];
if ( oldEntry === undefined || oldEntry.off === true ) { return; }
var listURL = oldEntry.contentURL;
if ( Array.isArray(listURL) ) {
listURL = listURL[0];
}
var newEntry = {
content: 'filters',
contentURL: listURL,
external: true,
group: 'custom',
submitter: 'user',
title: oldEntry.title || ''
};
newAvailableLists[listURL] = newEntry;
µb.assets.registerAssetSource(listURL, newEntry);
importedListKeys.push(listURL);
µb.userSettings.externalLists += '\n' + listURL;
µb.userSettings.externalLists = µb.userSettings.externalLists.trim();
vAPI.storage.set({ externalLists: µb.userSettings.externalLists });
µb.saveSelectedFilterLists([ listURL ], true);
};
// Final steps:
// - reuse existing list metadata if any;
// - unregister unreferenced imported filter lists if any.
@ -487,8 +512,13 @@
for ( assetKey in oldAvailableLists ) {
oldEntry = oldAvailableLists[assetKey];
newEntry = newAvailableLists[assetKey];
// List no longer exists. If a stock list, try to convert to
// imported list if it was selected.
if ( newEntry === undefined ) {
µb.removeFilterList(assetKey);
if ( assetKey.indexOf('://') === -1 ) {
customListFromStockList(assetKey);
}
continue;
}
if ( oldEntry.entryCount !== undefined ) {

View file

@ -650,11 +650,11 @@ vAPI.tabs.onPopupUpdated = (function() {
}
var re = new RegExp(logData.regex),
matches = re.exec(popunderURL);
if ( matches === null ) { return ''; }
if ( matches === null ) { return 0; }
var beg = matches.index,
end = beg + matches[0].length,
pos = popunderURL.indexOf(popunderHostname);
if ( pos === -1 ) { return ''; }
if ( pos === -1 ) { return 0; }
// https://github.com/gorhill/uBlock/issues/1471
// We test whether the opener hostname as at least one character
// within matched portion of URL.
@ -749,17 +749,19 @@ vAPI.tabs.onPopupUpdated = (function() {
}
// Log only for when there was a hit against an actual filter (allow or block).
// https://github.com/gorhill/uBlock/issues/2776
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(
popupType === 'popup' ? openerTabId : targetTabId,
'net',
logData,
result !== 0 ? logData : undefined,
popupType,
popupType === 'popup' ? targetURL : openerURL,
µb.URI.hostnameFromURI(context.rootURL),
µb.URI.hostnameFromURI(context.rootURL)
);
}
logData = undefined;
// Not blocked
if ( result !== 1 ) {
@ -901,7 +903,7 @@ vAPI.tabs.registerListeners();
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return;
}
tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 666);
tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 701);
};
})();

View file

@ -355,7 +355,7 @@ var onBeforeBehindTheSceneRequest = function(details) {
// working properly, etc.
// So we filter if and only if the "advanced user" mode is selected
if ( µb.userSettings.advancedUserEnabled ) {
result = pageStore.filterRequestNoCache(context);
result = pageStore.filterRequest(context);
}
pageStore.journalAddRequest(context.requestHostname, result);
@ -410,15 +410,24 @@ var onHeadersReceived = function(details) {
return foilLargeMediaElement(pageStore, details);
}
// https://github.com/gorhill/uBO-Extra/issues/19
// Turns out scripts must also be considered as potential embedded
// contexts (as workers) and as such we may need to inject content
// security policy directives.
if ( requestType === 'main_frame' || requestType === 'sub_frame' ) {
// https://github.com/gorhill/uBlock/issues/2813
// Disable the blocking of large media elements if the document is itself
// a media element: the resource was not prevented from loading so no
// point to further block large media elements for the current document.
if ( requestType === 'main_frame' ) {
if ( reMediaContentTypes.test(headerValueFromName('content-type', details.responseHeaders)) ) {
pageStore.allowLargeMediaElementsUntil = Date.now() + 86400000;
}
return injectCSP(pageStore, details);
}
if ( requestType === 'sub_frame' ) {
return injectCSP(pageStore, details);
}
};
var reMediaContentTypes = /^(?:audio|image|video)\//;
/******************************************************************************/
var injectCSP = function(pageStore, details) {
@ -441,7 +450,7 @@ var injectCSP = function(pageStore, details) {
context.requestType = 'inline-script';
context.requestURL = requestURL;
if ( pageStore.filterRequestNoCache(context) === 1 ) {
if ( pageStore.filterRequest(context) === 1 ) {
cspSubsets[0] = "script-src 'unsafe-eval' * blob: data:";
// https://bugs.chromium.org/p/chromium/issues/detail?id=669086
// TODO: remove when most users are beyond Chromium v56
@ -603,6 +612,11 @@ var headerIndexFromName = function(headerName, headers) {
return -1;
};
var headerValueFromName = function(headerName, headers) {
var i = headerIndexFromName(headerName, headers);
return i !== -1 ? headers[i].value : '';
};
/******************************************************************************/
vAPI.net.onBeforeRequest = {

View file

@ -37,6 +37,8 @@ Naming convention from https://en.wikipedia.org/wiki/URI_scheme#Examples
/******************************************************************************/
var punycode = self.punycode;
// Favorite regex tool: http://regex101.com/
// Ref: <http://tools.ietf.org/html/rfc3986#page-50>
@ -52,6 +54,7 @@ var reAuthorityFromURI = /^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
var reOriginFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]+)/;
var reCommonHostnameFromURL = /^https?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])\//;
var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/;
var reMustNormalizeHostname = /[^0-9a-z._-]/;
// These are to parse authority field, not parsed by above official regex
// IPv6 is seen as an exception: a non-compatible IPv6 is first tried, and
@ -61,11 +64,11 @@ var rePathFromURI = /^(?:[^:\/?#]+:)?(?:\/\/[^\/?#]*)?([^?#]*)/;
// https://github.com/gorhill/httpswitchboard/issues/211
// "While a hostname may not contain other characters, such as the
// "underscore character (_), other DNS names may contain the underscore"
var reHostPortFromAuthority = /^(?:[^@]*@)?([0-9a-z._-]*)(:\d*)?$/i;
var reHostPortFromAuthority = /^(?:[^@]*@)?([^:]*)(:\d*)?$/;
var reIPv6PortFromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]*\])(:\d*)?$/i;
var reHostFromNakedAuthority = /^[0-9a-z._-]+[0-9a-z]$/i;
var reHostFromAuthority = /^(?:[^@]*@)?([0-9a-z._-]+)(?::\d*)?$/i;
var reHostFromAuthority = /^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
var reIPv6FromAuthority = /^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
// Coarse (but fast) tests
@ -250,35 +253,34 @@ URI.authorityFromURI = function(uri) {
// The most used function, so it better be fast.
// https://github.com/gorhill/uBlock/issues/1559
// See http://en.wikipedia.org/wiki/FQDN
// https://bugzilla.mozilla.org/show_bug.cgi?id=1360285
// Revisit punycode dependency when above issue is fixed in Firefox.
URI.hostnameFromURI = function(uri) {
var matches = reCommonHostnameFromURL.exec(uri);
if ( matches ) {
return matches[1];
}
if ( matches !== null ) { return matches[1]; }
matches = reAuthorityFromURI.exec(uri);
if ( !matches ) {
return '';
}
if ( matches === null ) { return ''; }
var authority = matches[1].slice(2);
// Assume very simple authority (most common case for µBlock)
if ( reHostFromNakedAuthority.test(authority) ) {
return authority.toLowerCase();
}
matches = reHostFromAuthority.exec(authority);
if ( !matches ) {
if ( matches === null ) {
matches = reIPv6FromAuthority.exec(authority);
if ( !matches ) {
return '';
}
if ( matches === null ) { return ''; }
}
// http://en.wikipedia.org/wiki/FQDN
// Also:
// - https://github.com/gorhill/uBlock/issues/1559
var hostname = matches[1];
while ( hostname.endsWith('.') ) {
hostname = hostname.slice(0, -1);
}
return hostname.toLowerCase();
if ( reMustNormalizeHostname.test(hostname) ) {
hostname = punycode.toASCII(hostname.toLowerCase());
}
return hostname;
};
/******************************************************************************/

View file

@ -17,8 +17,8 @@
<p id="basicTools">
<span id="gotoZap" class="fa tool" data-i18n-tip="popupTipZapper" data-tip-position="under">&#xf0e7;</span>
<span id="gotoPick" class="fa tool" data-i18n-tip="popupTipPicker" data-tip-position="under">&#xf1fb;</span>
<a href="logger-ui.html" class="fa tool enabled" data-i18n-tip="popupTipLog" data-tip-position="under">&#xf022;</a>
<a href="dashboard.html" class="fa tool enabled" data-i18n-tip="popupTipDashboard" data-tip-position="under">&#xf085;</a>
<a href="logger-ui.html" class="fa tool enabled" data-i18n-tip="popupTipLog" data-tip-position="under" target="uBOLogger">&#xf022;</a>
<a href="dashboard.html" class="fa tool enabled" data-i18n-tip="popupTipDashboard" data-tip-position="under" target="uBODashboard">&#xf085;</a>
</p>
<h2 id="dfToggler" data-i18n="popupBlockedRequestPrompt">&nbsp;</h2>
<p class="statName">
@ -33,10 +33,10 @@
<h2 data-i18n="popupHitDomainCountPrompt">&nbsp;</h2>
<p class="statValue" id="popupHitDomainCount">&nbsp;</p>
<div id="extraTools">
<span id="no-popups" class="hnSwitch fa" data-i18n-tip="popupTipNoPopups">&#xf0c5;<span class="badge"></span><span></span></span>
<span id="no-large-media" class="hnSwitch fa" data-i18n-tip="popupTipNoLargeMedia">&#xf008;<span class="badge"></span><span></span></span>
<span id="no-cosmetic-filtering" class="hnSwitch fa" data-i18n-tip="popupTipNoCosmeticFiltering">&#xf070;<span class="badge"></span><span></span></span>
<span id="no-remote-fonts" class="hnSwitch fa" data-i18n-tip="popupTipNoRemoteFonts">&#xf031;<span class="badge"></span><span></span></span>
<span id="no-popups" class="hnSwitch fa" data-i18n-tip="popupTipNoPopups">&#xf0c5;<span class="badge"></span><span><svg viewBox="0 0 20 20"><path d="M1,1 19,19M1,19 19,1" /></svg></span></span>
<span id="no-large-media" class="hnSwitch fa" data-i18n-tip="popupTipNoLargeMedia">&#xf008;<span class="badge"></span><span><svg viewBox="0 0 20 20"><path d="M1,1 19,19M1,19 19,1" /></svg></span></span>
<span id="no-cosmetic-filtering" class="hnSwitch fa" data-i18n-tip="popupTipNoCosmeticFiltering">&#xf070;<span class="badge"></span><span><svg viewBox="0 0 20 20"><path d="M1,1 19,19M1,19 19,1" /></svg></span></span>
<span id="no-remote-fonts" class="hnSwitch fa" data-i18n-tip="popupTipNoRemoteFonts">&#xf031;<span class="badge"></span><span><svg viewBox="0 0 20 20"><path d="M1,1 19,19M1,19 19,1" /></svg></span></span>
</div>
</div><!-- DO NOT REMOVE --><div class="tooltipContainer">
<div id="firewallContainer" class="minimized">

View file

@ -0,0 +1,83 @@
#!/usr/bin/env python3
import os
import json
import re
import sys
from io import open as uopen
from collections import OrderedDict
if len(sys.argv) == 1 or not sys.argv[1]:
raise SystemExit('Build dir missing.')
proj_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], '..')
build_dir = os.path.abspath(sys.argv[1])
# Import data from chromium platform
chromium_manifest = {}
webext_manifest = {}
chromium_manifest_file = os.path.join(proj_dir, 'platform', 'chromium', 'manifest.json')
with open(chromium_manifest_file) as f1:
chromium_manifest = json.load(f1)
# WebExtension part
webext_manifest_file = os.path.join(build_dir, 'webextension', 'manifest.json')
with open(webext_manifest_file) as f2:
webext_manifest = json.load(f2)
webext_manifest['version'] = chromium_manifest['version']
with open(webext_manifest_file, 'w') as f2:
json.dump(webext_manifest, f2, indent=2, separators=(',', ': '), sort_keys=True)
f2.write('\n')
# Legacy part
descriptions = OrderedDict({})
source_locale_dir = os.path.join(build_dir, 'webextension', '_locales')
for alpha2 in sorted(os.listdir(source_locale_dir)):
locale_path = os.path.join(source_locale_dir, alpha2, 'messages.json')
with uopen(locale_path, encoding='utf-8') as f:
strings = json.load(f, object_pairs_hook=OrderedDict)
alpha2 = alpha2.replace('_', '-')
descriptions[alpha2] = strings['extShortDesc']['message']
webext_manifest['author'] = chromium_manifest['author'];
webext_manifest['name'] = chromium_manifest['name'] + '/webext-hybrid'
webext_manifest['homepage'] = 'https://github.com/gorhill/uBlock'
webext_manifest['description'] = descriptions['en']
del descriptions['en']
match = re.search('^(\d+\.\d+\.\d+)(\.\d+)$', chromium_manifest['version'])
if match:
buildtype = int(match.group(2)[1:])
if buildtype < 100:
builttype = 'b' + str(buildtype)
else:
builttype = 'rc' + str(buildtype - 100)
webext_manifest['version'] = match.group(1) + builttype
webext_manifest['localized'] = []
t = ' '
t3 = 3 * t
for alpha2 in descriptions:
if alpha2 == 'en':
continue
webext_manifest['localized'].append(
'\n' + t*2 + '<em:localized><Description>\n' +
t3 + '<em:locale>' + alpha2 + '</em:locale>\n' +
t3 + '<em:name>' + webext_manifest['name'] + '</em:name>\n' +
t3 + '<em:description>' + descriptions[alpha2] + '</em:description>\n' +
t3 + '<em:creator>' + webext_manifest['author'] + '</em:creator>\n' +
# t3 + '<translator>' + ??? + '</translator>\n' +
t3 + '<em:homepageURL>' + webext_manifest['homepage'] + '</em:homepageURL>\n' +
t*2 + '</Description></em:localized>'
)
webext_manifest['localized'] = '\n'.join(webext_manifest['localized'])
install_rdf = os.path.join(build_dir, 'install.rdf')
with uopen(install_rdf, 'r+t', encoding='utf-8', newline='\n') as f:
install_rdf = f.read()
f.seek(0)
f.write(install_rdf.format(**webext_manifest))
f.truncate()

51
tools/make-webext-hybrid.sh Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env bash
#
# This script assumes a linux environment
echo "*** uBlock0.webext-hybrid: Creating web store package"
echo "*** uBlock0.webext-hybrid: Copying files"
DES=dist/build/uBlock0.webext-hybrid
rm -rf $DES
mkdir -p $DES/webextension
bash ./tools/make-assets.sh $DES/webextension
cp -R src/css $DES/webextension/
cp -R src/img $DES/webextension/
cp -R src/js $DES/webextension/
cp -R src/lib $DES/webextension/
cp -R src/_locales $DES/webextension/
cp -R $DES/webextension/_locales/nb $DES/webextension/_locales/no
cp src/*.html $DES/webextension/
cp platform/chromium/*.js $DES/webextension/js/
cp -R platform/chromium/img $DES/webextension/
cp platform/chromium/*.html $DES/webextension/
cp platform/chromium/*.json $DES/webextension/
cp LICENSE.txt $DES/webextension/
cp platform/webext/manifest.json $DES/webextension/
cp platform/webext/background.html $DES/webextension/
cp platform/webext/options_ui.html $DES/webextension/
cp platform/webext/polyfill.js $DES/webextension/js/
cp platform/webext/vapi-usercss.js $DES/webextension/js/
cp platform/webext/vapi-cachestorage.js $DES/webextension/js/
cp platform/webext/from-legacy.js $DES/webextension/js/
rm $DES/webextension/js/options_ui.js
cp platform/webext/bootstrap.js $DES/
cp platform/webext/chrome.manifest $DES/
cp platform/webext/install.rdf $DES/
mv $DES/webextension/img/icon_128.png $DES/icon.png
echo "*** uBlock0.webext-hybrid: Generating meta..."
python tools/make-webext-hybrid-meta.py $DES/
if [ "$1" = all ]; then
echo "*** uBlock0.webext-hybrid: Creating package..."
pushd $DES > /dev/null
zip ../$(basename $DES).xpi -qr *
popd > /dev/null
fi
echo "*** uBlock0.webext-hybrid: Package done."

View file

@ -4,8 +4,6 @@ import os
import json
import re
import sys
from io import open as uopen
from collections import OrderedDict
if len(sys.argv) == 1 or not sys.argv[1]:
raise SystemExit('Build dir missing.')
@ -13,7 +11,7 @@ if len(sys.argv) == 1 or not sys.argv[1]:
proj_dir = os.path.join(os.path.split(os.path.abspath(__file__))[0], '..')
build_dir = os.path.abspath(sys.argv[1])
# Import data from chromium platform
# Import version number from chromium platform
chromium_manifest = {}
webext_manifest = {}
@ -21,33 +19,10 @@ chromium_manifest_file = os.path.join(proj_dir, 'platform', 'chromium', 'manifes
with open(chromium_manifest_file) as f1:
chromium_manifest = json.load(f1)
# WebExtension part
webext_manifest_file = os.path.join(build_dir, 'webextension', 'manifest.json')
webext_manifest_file = os.path.join(build_dir, 'manifest.json')
with open(webext_manifest_file) as f2:
webext_manifest = json.load(f2)
webext_manifest['version'] = chromium_manifest['version']
with open(webext_manifest_file, 'w') as f2:
json.dump(webext_manifest, f2, indent=2, separators=(',', ': '), sort_keys=True)
f2.write('\n')
# Legacy part
descriptions = OrderedDict({})
source_locale_dir = os.path.join(build_dir, 'webextension', '_locales')
for alpha2 in sorted(os.listdir(source_locale_dir)):
locale_path = os.path.join(source_locale_dir, alpha2, 'messages.json')
with uopen(locale_path, encoding='utf-8') as f:
strings = json.load(f, object_pairs_hook=OrderedDict)
alpha2 = alpha2.replace('_', '-')
descriptions[alpha2] = strings['extShortDesc']['message']
webext_manifest['author'] = chromium_manifest['author'];
webext_manifest['name'] = chromium_manifest['name'] + '/webext';
webext_manifest['homepage'] = 'https://github.com/gorhill/uBlock'
webext_manifest['description'] = descriptions['en']
del descriptions['en']
match = re.search('^(\d+\.\d+\.\d+)(\.\d+)$', chromium_manifest['version'])
if match:
buildtype = int(match.group(2)[1:])
@ -56,28 +31,9 @@ if match:
else:
builttype = 'rc' + str(buildtype - 100)
webext_manifest['version'] = match.group(1) + builttype
else:
webext_manifest['version'] = chromium_manifest['version']
webext_manifest['localized'] = []
t = ' '
t3 = 3 * t
for alpha2 in descriptions:
if alpha2 == 'en':
continue
webext_manifest['localized'].append(
'\n' + t*2 + '<em:localized><Description>\n' +
t3 + '<em:locale>' + alpha2 + '</em:locale>\n' +
t3 + '<em:name>' + webext_manifest['name'] + '</em:name>\n' +
t3 + '<em:description>' + descriptions[alpha2] + '</em:description>\n' +
t3 + '<em:creator>' + webext_manifest['author'] + '</em:creator>\n' +
# t3 + '<translator>' + ??? + '</translator>\n' +
t3 + '<em:homepageURL>' + webext_manifest['homepage'] + '</em:homepageURL>\n' +
t*2 + '</Description></em:localized>'
)
webext_manifest['localized'] = '\n'.join(webext_manifest['localized'])
install_rdf = os.path.join(build_dir, 'install.rdf')
with uopen(install_rdf, 'r+t', encoding='utf-8', newline='\n') as f:
install_rdf = f.read()
f.seek(0)
f.write(install_rdf.format(**webext_manifest))
f.truncate()
with open(webext_manifest_file, 'w') as f2:
json.dump(webext_manifest, f2, indent=2, separators=(',', ': '), sort_keys=True)
f2.write('\n')

View file

@ -7,31 +7,30 @@ echo "*** uBlock0.webext: Copying files"
DES=dist/build/uBlock0.webext
rm -rf $DES
mkdir -p $DES/webextension
mkdir -p $DES
bash ./tools/make-assets.sh $DES/webextension
bash ./tools/make-assets.sh $DES
cp -R src/css $DES/webextension/
cp -R src/img $DES/webextension/
cp -R src/js $DES/webextension/
cp -R src/lib $DES/webextension/
cp -R src/_locales $DES/webextension/
cp -R $DES/webextension/_locales/nb $DES/webextension/_locales/no
cp src/*.html $DES/webextension/
cp platform/chromium/*.js $DES/webextension/js/
cp -R platform/chromium/img $DES/webextension/
cp platform/chromium/*.html $DES/webextension/
cp platform/chromium/*.json $DES/webextension/
cp platform/webext/polyfill.js $DES/webextension/js/
cp LICENSE.txt $DES/webextension/
cp platform/webext/background.html $DES/webextension/
cp platform/webext/from-legacy.js $DES/webextension/js/
cp platform/webext/manifest.json $DES/webextension/
cp platform/webext/bootstrap.js $DES/
cp platform/webext/chrome.manifest $DES/
cp platform/webext/install.rdf $DES/
mv $DES/webextension/img/icon_128.png $DES/icon.png
cp -R src/css $DES/
cp -R src/img $DES/
cp -R src/js $DES/
cp -R src/lib $DES/
cp -R src/_locales $DES/
cp -R $DES/_locales/nb $DES/_locales/no
cp src/*.html $DES/
cp platform/chromium/*.js $DES/js/
cp -R platform/chromium/img $DES/
cp platform/chromium/*.html $DES/
cp platform/chromium/*.json $DES/
cp LICENSE.txt $DES/
cp platform/webext/manifest.json $DES/
cp platform/webext/options_ui.html $DES/
cp platform/webext/polyfill.js $DES/js/
cp platform/webext/vapi-webrequest.js $DES/js/
cp platform/webext/vapi-cachestorage.js $DES/js/
cp platform/webext/vapi-usercss.js $DES/js/
rm $DES/js/options_ui.js
echo "*** uBlock0.webext: Generating meta..."
python tools/make-webext-meta.py $DES/