Big fixes to Safari vAPI stuff

This commit is contained in:
Chris 2015-01-25 00:43:39 -07:00
parent da7398d585
commit 16f3a205e0
3 changed files with 98 additions and 150 deletions

View File

@ -19,9 +19,6 @@
Home: https://github.com/gorhill/uBlock
*/
/* global ytspf */
'use strict';
/******************************************************************************/
// Adding new URL requires to whitelist it in the background script too (addContentScriptFromURL)
@ -30,7 +27,7 @@
// from the extension scope won't be accessible in the sitePatch function.
self.vAPI = self.vAPI || {};
var vAPI = self.vAPI = self.vAPI || {};
if ( /^www\.youtube(-nocookie)?\.com/.test(location.host) ) {
vAPI.sitePatch = function() {

View File

@ -29,8 +29,6 @@
'use strict';
/******************************************************************************/
var vAPI = self.vAPI = self.vAPI || {};
vAPI.safari = true;
@ -590,104 +588,84 @@ vAPI.net = {};
/******************************************************************************/
// Fast `contains`
Array.prototype.contains = function(a) {
var b = this.length;
while(b --) {
if(this[b] === a) {
return true;
}
}
return false;
};
/******************************************************************************/
vAPI.net.registerListeners = function() {
var µb = µBlock;
// Since it's not used
this.onBeforeSendHeaders = null;
this.onHeadersReceived = null;
// Until Safari has more specific events, those are instead handled
// in the onBeforeRequestAdapter; clean them up so they're garbage-collected
vAPI.net.onBeforeSendHeaders = null;
vAPI.net.onHeadersReceived = null;
var onBeforeRequest = this.onBeforeRequest;
var onBeforeRequest = vAPI.net.onBeforeRequest,
onBeforeRequestClient = onBeforeRequest.callback,
blockableTypes = onBeforeRequest.types;
if ( !Array.isArray(onBeforeRequest.types) ) {
onBeforeRequest.types = [];
}
onBeforeRequest = onBeforeRequest.callback;
this.onBeforeRequest.callback = function(e) {
var block;
if ( e.name !== 'canLoad' ) {
return;
}
// No stopPropagation if it was called from beforeNavigate event
if ( e.stopPropagation ) {
e.stopPropagation();
}
if ( e.message.isURLWhiteListed ) {
// https://github.com/gorhill/uBlock/issues/595
// Do not access µb.netWhitelist directly
e.message = !µb.getNetFilteringSwitch(e.message.isURLWhiteListed);
return e.message;
}
// When the URL changes, but the document doesn't
if ( e.message.type === 'popstate' ) {
vAPI.tabs.onUpdated(
vAPI.tabs.getTabId(e.target),
{url: e.message.url},
{url: e.message.url}
);
return;
} else if ( e.message.type === 'popup' ) {
// blocking unwanted pop-ups
if ( e.message.url === 'about:blank' ) {
vAPI.tabs.popupCandidate = vAPI.tabs.getTabId(e.target);
e.message = true;
} else {
e.message = !vAPI.tabs.onPopup({
url: e.message.url,
tabId: 0,
sourceTabId: vAPI.tabs.getTabId(e.target)
});
}
return;
}
if ( e.message.navigatedToNew ) {
vAPI.tabs.onNavigation({
url: e.message.url,
frameId: 0,
tabId: vAPI.tabs.getTabId(e.target)
});
return;
}
block = vAPI.net.onBeforeRequest;
if ( block.types.indexOf(e.message.type) === -1 ) {
return true;
}
e.message.hostname = µb.URI.hostnameFromURI(e.message.url);
e.message.tabId = vAPI.tabs.getTabId(e.target);
block = onBeforeRequest(e.message);
// Truthy return value will allow the request,
// except when redirectUrl is present
if ( block && typeof block === 'object' ) {
if ( block.cancel === true ) {
e.message = false;
} else if ( e.message.type === 'script'
&& typeof block.redirectUrl === 'string' ) {
e.message = block.redirectUrl;
} else {
e.message = true;
}
} else {
e.message = true;
}
return e.message;
var onBeforeRequestAdapter = function(e) {
if(e.name !== "canLoad") {
return;
}
e.stopPropagation && e.stopPropagation();
switch(e.message.type) {
case "isWhiteListed":
e.message = !µb.getNetFilteringSwitch(e.message.url);
break;
case "navigatedToNew":
vAPI.tabs.onNavigation({
url: e.message.url,
frameId: 0,
tabId: vAPI.tabs.getTabId(e.target)
});
break;
case "popup":
if(e.message.url === 'about:blank') {
vAPI.tabs.popupCandidate = vAPI.tabs.getTabId(e.target);
e.message = true;
}
else {
e.message = !vAPI.tabs.onPopup({
url: e.message.url,
tabId: 0,
sourceTabId: vAPI.tabs.getTabId(e.target)
});
}
break;
case "popstate":
vAPI.tabs.onUpdated(vAPI.tabs.getTabId(e.target),
{url: e.message.url},
{url: e.message.url});
break;
default:
if(!blockableTypes.contains(e.message.type)) {
e.message = true;
return;
}
e.message.hostname = µb.URI.hostnameFromURI(e.message.url);
e.message.tabId = vAPI.tabs.getTabId(e.target);
var blockVerdict = onBeforeRequestClient(e.message);
if(blockVerdict && blockVerdict.cancel) {
e.message = false;
}
else {
e.message = true;
}
}
return;
};
safari.application.addEventListener(
'message',
this.onBeforeRequest.callback,
true
);
safari.application.addEventListener("message", onBeforeRequestAdapter, true);
};
/******************************************************************************/

View File

@ -27,9 +27,7 @@
'use strict';
/******************************************************************************/
self.vAPI = self.vAPI || {};
var vAPI = self.vAPI = self.vAPI || {};
vAPI.safari = true;
/******************************************************************************/
@ -177,7 +175,7 @@ vAPI.messaging = {
/******************************************************************************/
vAPI.canExecuteContentScript = function() {
return /^https?:/.test(location.protocol);
return (/^https?:/.test(location.protocol) && typeof safari === 'object');
};
/******************************************************************************/
@ -185,35 +183,34 @@ vAPI.canExecuteContentScript = function() {
// This file can be included into extensin pages,
// but the following code should run only in content pages.
if ( location.protocol === 'safari-extension:' ) {
if ( location.protocol === 'safari-extension:' || typeof safari !== 'object' ) {
return;
}
/******************************************************************************/
var frameId = window === window.top ? 0 : Date.now() % 1E5;
var parentFrameId = frameId ? 0 : -1;
var beforeLoadEvent = new Event('beforeload'); // Helper event to message background
var parentFrameId = (frameId ? 0 : -1);
var beforeLoadEvent = new Event("beforeload"); // Helper event to message background
// Inform that we've navigated
if(frameId === 0) {
safari.self.tab.canLoad(beforeLoadEvent, {
url: location.href,
type: 'main_frame',
navigatedToNew: true
type: "navigatedToNew"
});
}
var linkHelper = document.createElement('a');
var linkHelper = document.createElement("a");
var nodeTypes = {
'frame': 'sub_frame',
'iframe': 'sub_frame',
'script': 'script',
'img': 'image',
'input': 'image',
'object': 'object',
'embed': 'object',
'link': 'stylesheet'
"frame": "sub_frame",
"iframe": "sub_frame",
"script": "script",
"img": "image",
"input": "image",
"object": "object",
"embed": "object",
"link": "stylesheet"
};
var shouldBlockDetailedRequest = function(details) {
linkHelper.href = details.url;
@ -224,7 +221,7 @@ var shouldBlockDetailedRequest = function(details) {
return !(safari.self.tab.canLoad(beforeLoadEvent, details));
}
var onBeforeLoad = function(e) {
if(e.url.lastIndexOf('data:', 0) === 0) {
if(e.url.lastIndexOf("data:", 0) === 0) {
return;
}
linkHelper.href = e.url;
@ -240,28 +237,6 @@ var onBeforeLoad = function(e) {
var response = safari.self.tab.canLoad(e, details);
if(!response) {
e.preventDefault();
return false;
}
// Local mirroring, response should be a data: URL here
if(typeof response !== 'string') {
return;
}
// Okay, we're mirroring...
e.preventDefault();
// Content Security Policy with disallowed inline scripts may break things
details = document.createElement('script');
details.textContent = atob(response.slice(response.indexOf(',', 20) + 1));
if ( e.target.hasAttribute('defer') && document.readyState === 'loading' ) {
var jsOnLoad = function(ev) {
this.removeEventListener(ev.type, jsOnLoad, true);
this.body.removeChild(this.body.appendChild(details));
};
document.addEventListener('DOMContentLoaded', jsOnLoad, true);
} else {
e.target.parentNode.insertBefore(details, e.target);
details.parentNode.removeChild(details);
}
};
@ -270,7 +245,7 @@ document.addEventListener('beforeload', onBeforeLoad, true);
/******************************************************************************/
// block pop-ups, intercept xhr requests, and apply site patches
var firstMutation = function() {
document.removeEventListener('DOMSubtreeModified', firstMutation, true);
document.removeEventListener('DOMContentLoaded', firstMutation, true);
firstMutation = false;
var randEventName = uniqueId();
@ -302,7 +277,7 @@ var firstMutation = function() {
'};'
];
if ( frameId === 0 ) {
if(frameId === 0) {
tmpScript.push(
'var pS = history.pushState, rS = history.replaceState,',
'onpopstate = function(e) {',
@ -322,22 +297,20 @@ var firstMutation = function() {
);
}
var block = safari.self.tab.canLoad(beforeLoadEvent, {
isURLWhiteListed: location.href
var whiteListed = safari.self.tab.canLoad(beforeLoadEvent, {
type: "isWhiteListed",
url: location.href
});
if ( vAPI.sitePatch && !block ) {
tmpScript.push('(' + vAPI.sitePatch + ')();');
if(vAPI.sitePatch && !whiteListed) {
tmpScript.push('(' + vAPI.sitePatch + ')();');
}
tmpScript.push('})();');
tmpJS.textContent = tmpScript.join('');
document.documentElement.removeChild(
document.documentElement.appendChild(tmpJS)
);
document.documentElement.removeChild(document.documentElement.appendChild(tmpJS));
};
document.addEventListener('DOMSubtreeModified', firstMutation, true);
document.addEventListener('DOMContentLoaded', firstMutation, true);
/******************************************************************************/