aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeathamns <deathamns@gmail.com>2014-11-02 17:20:06 +0100
committerDeathamns <deathamns@gmail.com>2014-11-09 17:40:59 +0100
commitd114bf21e7a731600f4469bf3e1cd529534e0f4a (patch)
treeeeedd638db116999de144d67efd3e59ce462d5b8
parente6e3f676e2fa663f3e529cf4df382219af9bd089 (diff)
downloaduBlock-d114bf21e7a731600f4469bf3e1cd529534e0f4a.zip
uBlock-d114bf21e7a731600f4469bf3e1cd529534e0f4a.tar.gz
uBlock-d114bf21e7a731600f4469bf3e1cd529534e0f4a.tar.bz2
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.
-rw-r--r--src/.jshintrc1
-rw-r--r--src/js/element-picker.js2
-rw-r--r--src/js/sitepatch-safari.js91
-rw-r--r--src/js/vapi-appinfo.js2
-rw-r--r--src/js/vapi-background.js36
-rw-r--r--src/js/vapi-client.js36
-rw-r--r--src/js/vapi-common.js8
7 files changed, 148 insertions, 28 deletions
diff --git a/src/.jshintrc b/src/.jshintrc
index b3ee961..618c34f 100644
--- a/src/.jshintrc
+++ b/src/.jshintrc
@@ -13,6 +13,7 @@
"newcap": false,
"-W058": true, // suppress "Missing '()' invoking a constructor" message
"globals": {
+ "self": false,
"vAPI": false,
"chrome": false,
"safari": false,
diff --git a/src/js/element-picker.js b/src/js/element-picker.js
index 9700a27..fa29f6a 100644
--- a/src/js/element-picker.js
+++ b/src/js/element-picker.js
@@ -108,7 +108,7 @@
};
}
-}(this));
+}(self));
/******************************************************************************/
/******************************************************************************/
diff --git a/src/js/sitepatch-safari.js b/src/js/sitepatch-safari.js
new file mode 100644
index 0000000..e4eee90
--- /dev/null
+++ b/src/js/sitepatch-safari.js
@@ -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
+}*/
diff --git a/src/js/vapi-appinfo.js b/src/js/vapi-appinfo.js
index dc4684d..2dea72c 100644
--- a/src/js/vapi-appinfo.js
+++ b/src/js/vapi-appinfo.js
@@ -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',
diff --git a/src/js/vapi-background.js b/src/js/vapi-background.js
index 4344530..c56d806 100644
--- a/src/js/vapi-background.js
+++ b/src/js/vapi-background.js
@@ -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 } };
}
})();
diff --git a/src/js/vapi-client.js b/src/js/vapi-client.js
index 1fc4535..135a95d 100644
--- a/src/js/vapi-client.js
+++ b/src/js/vapi-client.js
@@ -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;
}
diff --git a/src/js/vapi-common.js b/src/js/vapi-common.js
index 3ece2ad..a44aa74 100644
--- a/src/js/vapi-common.js
+++ b/src/js/vapi-common.js
@@ -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;
};