mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-10 09:07:54 +01:00
Site-patching possibility for Safari
Safari's extension API doesn't provide a way to intercept requests initiated by plugins, so those cases need special care (or at least the popular sites). This commit adds a new JS file (sitepatch-safari.js), which will store the patches (if it's possible to create one) for specific sites. As an example, this commit includes a technique for removing in-video ads from YouTube videos.
This commit is contained in:
parent
e6e3f676e2
commit
d114bf21e7
7 changed files with 148 additions and 28 deletions
|
@ -13,6 +13,7 @@
|
|||
"newcap": false,
|
||||
"-W058": true, // suppress "Missing '()' invoking a constructor" message
|
||||
"globals": {
|
||||
"self": false,
|
||||
"vAPI": false,
|
||||
"chrome": false,
|
||||
"safari": false,
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
};
|
||||
}
|
||||
|
||||
}(this));
|
||||
}(self));
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
|
91
src/js/sitepatch-safari.js
Normal file
91
src/js/sitepatch-safari.js
Normal file
|
@ -0,0 +1,91 @@
|
|||
// Only for Safari
|
||||
// Adding new URL requires to whitelist it in the background script too (addContentScriptFromURL)
|
||||
// Note that the sitePach function will be converted to a string, and injected
|
||||
// into the web-page in order to run in that scope. Because of this, variables
|
||||
// from the extension scope won't be accessible in the sitePatch function.
|
||||
'use strict';
|
||||
|
||||
self.vAPI = self.vAPI || {};
|
||||
|
||||
if (/^www\.youtube(-nocookie)?\.com/.test(location.host)) {
|
||||
vAPI.sitePatch = function() {
|
||||
window.addEventListener('load', function onWindowLoad() {
|
||||
this.removeEventListener('load', onWindowLoad, true);
|
||||
var spf = this._spf_state;
|
||||
|
||||
if (spf && (spf = spf.config)) {
|
||||
spf['navigate-limit'] = 0;
|
||||
spf['navigate-part-received-callback'] = function(url) {
|
||||
window.location.href = url;
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
|
||||
|
||||
// based on ExtendTube's ad removing solution
|
||||
var p, yt = {}, config_ = {}, ytplayer = {}, playerConfig = { args: {} };
|
||||
|
||||
Object.defineProperties(yt, {
|
||||
'playerConfig': {
|
||||
get: function() { return playerConfig; },
|
||||
set: function(data) {
|
||||
if (data && typeof data === 'object'
|
||||
&& data.args && typeof data.args === 'object') {
|
||||
var nope = /ad\d?_|afv|watermark|adsense|xfp/;
|
||||
|
||||
for (var prop in data.args) {
|
||||
if (nope.test(prop) && !/policy/.test(prop)) {
|
||||
delete data.args[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerConfig = data;
|
||||
|
||||
var playerRoot = document.querySelector('[data-swf-config]');
|
||||
if (playerRoot)
|
||||
playerRoot.dataset.swfConfig = JSON.stringify(yt.playerConfig);
|
||||
}
|
||||
},
|
||||
'config_': {
|
||||
get: function() { return config_; },
|
||||
set: function(value) { config_ = value; }
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(config_, 'PLAYER_CONFIG', {
|
||||
get: function() { return yt.playerConfig; },
|
||||
set: function(value) { yt.playerConfig = value; }
|
||||
});
|
||||
|
||||
Object.defineProperty(ytplayer, 'config', {
|
||||
get: function() { return playerConfig; },
|
||||
set: function(value) { yt.playerConfig = value; }
|
||||
});
|
||||
|
||||
if (window.yt) {
|
||||
for (p in window.yt) { yt[p] = window.yt[p]; }
|
||||
window.yt = yt;
|
||||
}
|
||||
else {
|
||||
Object.defineProperty(window, 'yt', {
|
||||
get: function() { return yt; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
|
||||
if (window.ytplayer) {
|
||||
for (p in window.ytplayer) { ytplayer[p] = window.ytplayer[p]; }
|
||||
window.ytplayer = ytplayer;
|
||||
}
|
||||
else {
|
||||
Object.defineProperty(window, 'ytplayer', {
|
||||
get: function() { return ytplayer; },
|
||||
set: function() {}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
/*else if (check url) {
|
||||
vAPI.sitePatch do something
|
||||
}*/
|
|
@ -1,7 +1,7 @@
|
|||
// can be included anywhere if it's needed
|
||||
'use strict';
|
||||
|
||||
window.vAPI = window.vAPI || {};
|
||||
self.vAPI = self.vAPI || {};
|
||||
|
||||
vAPI.app = {
|
||||
/**/name: 'µBlock',
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.vAPI = window.vAPI || {};
|
||||
self.vAPI = self.vAPI || {};
|
||||
|
||||
if (window.chrome) {
|
||||
var chrome = window.chrome;
|
||||
if (self.chrome) {
|
||||
var chrome = self.chrome;
|
||||
|
||||
vAPI.chrome = true;
|
||||
|
||||
|
@ -272,9 +272,22 @@ if (window.chrome) {
|
|||
chrome.contextMenus.remove(this.menuId);
|
||||
}
|
||||
};
|
||||
} else if (window.safari) {
|
||||
} else if (self.safari) {
|
||||
vAPI.safari = true;
|
||||
|
||||
// addContentScriptFromURL allows whitelisting,
|
||||
// so load sitepaching this way, instead of adding it to the Info.plist
|
||||
safari.extension.addContentScriptFromURL(
|
||||
safari.extension.baseURI + 'js/sitepatch-safari.js',
|
||||
[
|
||||
'http://www.youtube.com/*',
|
||||
'https://www.youtube.com/*',
|
||||
'http://www.youtube-nocookie.com/*',
|
||||
'https://www.youtube-nocookie.com/*'
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
vAPI.storage = {
|
||||
_storage: safari.extension.settings,
|
||||
QUOTA_BYTES: 52428800, // copied from Info.plist
|
||||
|
@ -709,6 +722,8 @@ if (window.chrome) {
|
|||
|
||||
onBeforeRequest = onBeforeRequest.callback;
|
||||
this.onBeforeRequest.callback = function(e) {
|
||||
var block;
|
||||
|
||||
if (e.name !== 'canLoad') {
|
||||
return;
|
||||
}
|
||||
|
@ -718,6 +733,13 @@ if (window.chrome) {
|
|||
e.stopPropagation();
|
||||
}
|
||||
|
||||
if (e.message.isWhiteListed) {
|
||||
block = µBlock.URI.hostnameFromURI(e.message.isWhiteListed);
|
||||
block = µBlock.URI.domainFromHostname(block) || block;
|
||||
e.message = !!µBlock.netWhitelist[block];
|
||||
return e.message;
|
||||
}
|
||||
|
||||
if (e.message.middleClickURL) {
|
||||
vAPI.lastMiddleClick = e.message;
|
||||
return;
|
||||
|
@ -739,7 +761,7 @@ if (window.chrome) {
|
|||
return;
|
||||
}
|
||||
|
||||
var block = vAPI.net.onBeforeRequest;
|
||||
block = vAPI.net.onBeforeRequest;
|
||||
|
||||
if (block.types.indexOf(e.message.type) < 0) {
|
||||
return true;
|
||||
|
@ -897,7 +919,7 @@ if (window.chrome) {
|
|||
};
|
||||
}
|
||||
|
||||
if (!window.chrome) {
|
||||
window.chrome = { runtime: { lastError: null } };
|
||||
if (!self.chrome) {
|
||||
self.chrome = { runtime: { lastError: null } };
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.vAPI = window.vAPI || {};
|
||||
self.vAPI = self.vAPI || {};
|
||||
|
||||
// since this is common across vendors
|
||||
var messagingConnector = function(response) {
|
||||
|
@ -48,7 +48,7 @@ var messagingConnector = function(response) {
|
|||
}
|
||||
};
|
||||
|
||||
if (window.chrome) {
|
||||
if (self.chrome) {
|
||||
vAPI.chrome = true;
|
||||
vAPI.messaging = {
|
||||
port: null,
|
||||
|
@ -102,7 +102,7 @@ if (window.chrome) {
|
|||
return this.channels[name];
|
||||
}
|
||||
};
|
||||
} else if (window.safari) {
|
||||
} else if (self.safari) {
|
||||
vAPI.safari = true;
|
||||
|
||||
// relevant?
|
||||
|
@ -121,7 +121,7 @@ if (window.chrome) {
|
|||
this.channels['vAPI'] = {
|
||||
listener: function(msg) {
|
||||
if (msg.cmd === 'runScript' && msg.details.code) {
|
||||
Function(msg.details.code).call(window);
|
||||
Function(msg.details.code).call(self);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -272,13 +272,13 @@ if (window.chrome) {
|
|||
|
||||
document.addEventListener('beforeload', onBeforeLoad, true);
|
||||
|
||||
// blocking pop-ups and intercepting xhr requests
|
||||
// block pop-ups, intercept xhr requests, and apply site patches
|
||||
var firstMutation = function() {
|
||||
document.removeEventListener('DOMSubtreeModified', firstMutation, true);
|
||||
firstMutation = null;
|
||||
var randomEventName = parseInt(Math.random() * 1e15, 10).toString(36);
|
||||
var randEventName = parseInt(Math.random() * 1e15, 10).toString(36);
|
||||
|
||||
window.addEventListener(randomEventName, function(e) {
|
||||
window.addEventListener(randEventName, function(e) {
|
||||
var result = onBeforeLoad(beforeLoadEvent, e.detail);
|
||||
|
||||
if (result === false) {
|
||||
|
@ -289,13 +289,11 @@ if (window.chrome) {
|
|||
// the extension context is unable to reach the page context,
|
||||
// also this only works when Content Security Policy allows inline scripts
|
||||
var tmpJS = document.createElement('script');
|
||||
tmpJS.textContent = ["(function() {",
|
||||
var tmpScript = ["(function() {",
|
||||
"var block = function(u, t) {",
|
||||
"var e = document.createEvent('CustomEvent'),",
|
||||
"d = {url: u, type: t};",
|
||||
"e.initCustomEvent(",
|
||||
"'" + randomEventName + "', !1, !1, d",
|
||||
");",
|
||||
"e.initCustomEvent('" + randEventName + "', !1, !1, d);",
|
||||
"dispatchEvent(e);",
|
||||
"return d.url === !1;",
|
||||
"}, wo = open, xo = XMLHttpRequest.prototype.open;",
|
||||
|
@ -304,8 +302,16 @@ if (window.chrome) {
|
|||
"};",
|
||||
"XMLHttpRequest.prototype.open = function(m, u) {",
|
||||
"return block(u, 'xmlhttprequest') ? null : xo.apply(this, [].slice.call(arguments));",
|
||||
"};",
|
||||
"})();"].join('');
|
||||
"};"
|
||||
];
|
||||
|
||||
if (vAPI.sitePatch
|
||||
&& !safari.self.tab.canLoad(beforeLoadEvent, {isWhiteListed: location.href})) {
|
||||
tmpScript.push('(' + vAPI.sitePatch + ')();');
|
||||
}
|
||||
|
||||
tmpScript.push("})();");
|
||||
tmpJS.textContent = tmpScript.join('');
|
||||
document.documentElement.removeChild(document.documentElement.appendChild(tmpJS));
|
||||
};
|
||||
|
||||
|
@ -342,9 +348,9 @@ if (window.chrome) {
|
|||
safari.self.tab.setContextMenuEventUserInfo(e, details);
|
||||
};
|
||||
|
||||
window.addEventListener('contextmenu', onContextMenu, true);
|
||||
self.addEventListener('contextmenu', onContextMenu, true);
|
||||
|
||||
window.addEventListener('mouseup', function(e) {
|
||||
self.addEventListener('mouseup', function(e) {
|
||||
if (e.button !== 1) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.vAPI = window.vAPI || {};
|
||||
self.vAPI = self.vAPI || {};
|
||||
|
||||
// http://www.w3.org/International/questions/qa-scripts#directions
|
||||
var setScriptDirection = function(langugae) {
|
||||
|
@ -38,8 +38,8 @@ vAPI.download = function(details) {
|
|||
}
|
||||
};
|
||||
|
||||
if (window.chrome) {
|
||||
var chrome = window.chrome;
|
||||
if (self.chrome) {
|
||||
var chrome = self.chrome;
|
||||
|
||||
vAPI.getURL = function(path) {
|
||||
return chrome.runtime.getURL(path);
|
||||
|
@ -50,7 +50,7 @@ if (window.chrome) {
|
|||
};
|
||||
|
||||
setScriptDirection(vAPI.i18n('@@ui_locale'));
|
||||
} else if (window.safari) {
|
||||
} else if (self.safari) {
|
||||
vAPI.getURL = function(path) {
|
||||
return safari.extension.baseURI + path;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue