mirror of https://github.com/gorhill/uBlock.git
dom inspector: ébauche
This commit is contained in:
parent
2fd7a73c62
commit
631443768f
|
@ -519,6 +519,62 @@ vAPI.messaging = {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// This allows to avoid creating a closure for every single message which
|
||||||
|
// expects an answer. Having a closure created each time a message is processed
|
||||||
|
// has been always bothering me. Another benefit of the implementation here
|
||||||
|
// is to reuse the callback proxy object, so less memory churning.
|
||||||
|
//
|
||||||
|
// https://developers.google.com/speed/articles/optimizing-javascript
|
||||||
|
// "Creating a closure is significantly slower then creating an inner
|
||||||
|
// function without a closure, and much slower than reusing a static
|
||||||
|
// function"
|
||||||
|
//
|
||||||
|
// http://hacksoflife.blogspot.ca/2015/01/the-four-horsemen-of-performance.html
|
||||||
|
// "the dreaded 'uniformly slow code' case where every function takes 1%
|
||||||
|
// of CPU and you have to make one hundred separate performance optimizations
|
||||||
|
// to improve performance at all"
|
||||||
|
//
|
||||||
|
// http://jsperf.com/closure-no-closure/2
|
||||||
|
|
||||||
|
var CallbackWrapper = function(port, request) {
|
||||||
|
// No need to bind every single time
|
||||||
|
this.callback = this.proxy.bind(this);
|
||||||
|
this.messaging = vAPI.messaging;
|
||||||
|
this.init(port, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
CallbackWrapper.junkyard = [];
|
||||||
|
|
||||||
|
CallbackWrapper.factory = function(port, request) {
|
||||||
|
var wrapper = CallbackWrapper.junkyard.pop();
|
||||||
|
if ( wrapper ) {
|
||||||
|
wrapper.init(port, request);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
return new CallbackWrapper(port, request);
|
||||||
|
};
|
||||||
|
|
||||||
|
CallbackWrapper.prototype.init = function(port, request) {
|
||||||
|
this.port = port;
|
||||||
|
this.request = request;
|
||||||
|
};
|
||||||
|
|
||||||
|
CallbackWrapper.prototype.proxy = function(response) {
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/383
|
||||||
|
if ( this.messaging.ports.hasOwnProperty(this.port.name) ) {
|
||||||
|
this.port.postMessage({
|
||||||
|
requestId: this.request.requestId,
|
||||||
|
channelName: this.request.channelName,
|
||||||
|
msg: response !== undefined ? response : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Mark for reuse
|
||||||
|
this.port = this.request = null;
|
||||||
|
CallbackWrapper.junkyard.push(this);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.messaging.listen = function(listenerName, callback) {
|
vAPI.messaging.listen = function(listenerName, callback) {
|
||||||
this.listeners[listenerName] = callback;
|
this.listeners[listenerName] = callback;
|
||||||
};
|
};
|
||||||
|
@ -605,58 +661,25 @@ vAPI.messaging.broadcast = function(message) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// This allows to avoid creating a closure for every single message which
|
vAPI.messaging.send = function(tabId, channelName, message) {
|
||||||
// expects an answer. Having a closure created each time a message is processed
|
var port;
|
||||||
// has been always bothering me. Another benefit of the implementation here
|
var chromiumTabId = toChromiumTabId(tabId);
|
||||||
// is to reuse the callback proxy object, so less memory churning.
|
for ( var portName in this.ports ) {
|
||||||
//
|
if ( this.ports.hasOwnProperty(portName) === false ) {
|
||||||
// https://developers.google.com/speed/articles/optimizing-javascript
|
continue;
|
||||||
// "Creating a closure is significantly slower then creating an inner
|
|
||||||
// function without a closure, and much slower than reusing a static
|
|
||||||
// function"
|
|
||||||
//
|
|
||||||
// http://hacksoflife.blogspot.ca/2015/01/the-four-horsemen-of-performance.html
|
|
||||||
// "the dreaded 'uniformly slow code' case where every function takes 1%
|
|
||||||
// of CPU and you have to make one hundred separate performance optimizations
|
|
||||||
// to improve performance at all"
|
|
||||||
//
|
|
||||||
// http://jsperf.com/closure-no-closure/2
|
|
||||||
|
|
||||||
var CallbackWrapper = function(port, request) {
|
|
||||||
// No need to bind every single time
|
|
||||||
this.callback = this.proxy.bind(this);
|
|
||||||
this.messaging = vAPI.messaging;
|
|
||||||
this.init(port, request);
|
|
||||||
};
|
|
||||||
|
|
||||||
CallbackWrapper.junkyard = [];
|
|
||||||
|
|
||||||
CallbackWrapper.factory = function(port, request) {
|
|
||||||
var wrapper = CallbackWrapper.junkyard.pop();
|
|
||||||
if ( wrapper ) {
|
|
||||||
wrapper.init(port, request);
|
|
||||||
return wrapper;
|
|
||||||
}
|
}
|
||||||
return new CallbackWrapper(port, request);
|
port = this.ports[portName];
|
||||||
};
|
if ( chromiumTabId !== 0 && port.sender.tab.id !== chromiumTabId ) {
|
||||||
|
continue;
|
||||||
CallbackWrapper.prototype.init = function(port, request) {
|
}
|
||||||
this.port = port;
|
port.postMessage({
|
||||||
this.request = request;
|
channelName: channelName,
|
||||||
};
|
msg: message
|
||||||
|
|
||||||
CallbackWrapper.prototype.proxy = function(response) {
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/383
|
|
||||||
if ( this.messaging.ports.hasOwnProperty(this.port.name) ) {
|
|
||||||
this.port.postMessage({
|
|
||||||
requestId: this.request.requestId,
|
|
||||||
channelName: this.request.channelName,
|
|
||||||
msg: response !== undefined ? response : null
|
|
||||||
});
|
});
|
||||||
|
if ( chromiumTabId !== 0 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Mark for reuse
|
|
||||||
this.port = this.request = null;
|
|
||||||
CallbackWrapper.junkyard.push(this);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -73,41 +73,140 @@ vAPI.shutdown = (function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var MessagingListeners = function(callback) {
|
||||||
|
this.listeners = [];
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
this.listeners.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingListeners.prototype.add = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( this.listeners.indexOf(callback) !== -1 ) {
|
||||||
|
throw new Error('Duplicate listener.');
|
||||||
|
}
|
||||||
|
this.listeners.push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingListeners.prototype.remove = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( this.listeners.indexOf(callback) === -1 ) {
|
||||||
|
throw new Error('Listener not found.');
|
||||||
|
}
|
||||||
|
this.listeners.splice(this.listeners.indexOf(callback), 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingListeners.prototype.process = function(msg) {
|
||||||
|
var listeners = this.listeners;
|
||||||
|
var n = listeners.length;
|
||||||
|
for ( var i = 0; i < n; i++ ) {
|
||||||
|
listeners[i](msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
var messagingConnector = function(response) {
|
var messagingConnector = function(response) {
|
||||||
if ( !response ) {
|
if ( !response ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var channels = vAPI.messaging.channels;
|
var messaging = vAPI.messaging;
|
||||||
var channel, listener;
|
var channels = messaging.channels;
|
||||||
|
var channel;
|
||||||
|
|
||||||
|
// Sent to all channels
|
||||||
if ( response.broadcast === true && !response.channelName ) {
|
if ( response.broadcast === true && !response.channelName ) {
|
||||||
for ( channel in channels ) {
|
for ( channel in channels ) {
|
||||||
if ( channels.hasOwnProperty(channel) === false ) {
|
if ( channels[channel] instanceof MessagingChannel === false ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
listener = channels[channel].listener;
|
channels[channel].listeners.process(response.msg);
|
||||||
if ( typeof listener === 'function' ) {
|
|
||||||
listener(response.msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response to specific message previously sent
|
||||||
if ( response.requestId ) {
|
if ( response.requestId ) {
|
||||||
listener = vAPI.messaging.listeners[response.requestId];
|
var listener = messaging.pending[response.requestId];
|
||||||
delete vAPI.messaging.listeners[response.requestId];
|
delete messaging.pending[response.requestId];
|
||||||
delete response.requestId;
|
delete response.requestId; // TODO: why?
|
||||||
}
|
if ( listener ) {
|
||||||
|
|
||||||
if ( !listener ) {
|
|
||||||
channel = channels[response.channelName];
|
|
||||||
listener = channel && channel.listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( typeof listener === 'function' ) {
|
|
||||||
listener(response.msg);
|
listener(response.msg);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent to a specific channel
|
||||||
|
channel = channels[response.channelName];
|
||||||
|
if ( channel instanceof MessagingChannel ) {
|
||||||
|
channel.listeners.process(response.msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var MessagingChannel = function(name, callback) {
|
||||||
|
this.channelName = name;
|
||||||
|
this.listeners = new MessagingListeners(callback);
|
||||||
|
this.refCount = 1;
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
if ( messaging.port === null ) {
|
||||||
|
messaging.setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.send = function(message, callback) {
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
if ( messaging.port === null ) {
|
||||||
|
messaging.setup();
|
||||||
|
}
|
||||||
|
var requestId;
|
||||||
|
if ( callback ) {
|
||||||
|
requestId = messaging.requestId++;
|
||||||
|
messaging.pending[requestId] = callback;
|
||||||
|
}
|
||||||
|
messaging.port.postMessage({
|
||||||
|
channelName: this.channelName,
|
||||||
|
requestId: requestId,
|
||||||
|
msg: message
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.close = function() {
|
||||||
|
this.refCount -= 1;
|
||||||
|
if ( this.refCount !== 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
delete messaging.channels[this.channelName];
|
||||||
|
if ( Object.keys(messaging.channels).length === 0 ) {
|
||||||
|
messaging.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.addListener = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.listeners.add(callback);
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
if ( messaging.port === null ) {
|
||||||
|
messaging.setup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.removeListener = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.listeners.remove(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -115,7 +214,7 @@ var messagingConnector = function(response) {
|
||||||
vAPI.messaging = {
|
vAPI.messaging = {
|
||||||
port: null,
|
port: null,
|
||||||
channels: {},
|
channels: {},
|
||||||
listeners: {},
|
pending: {},
|
||||||
requestId: 1,
|
requestId: 1,
|
||||||
|
|
||||||
setup: function() {
|
setup: function() {
|
||||||
|
@ -131,43 +230,21 @@ vAPI.messaging = {
|
||||||
this.port.onMessage.removeListener(messagingConnector);
|
this.port.onMessage.removeListener(messagingConnector);
|
||||||
this.port = null;
|
this.port = null;
|
||||||
this.channels = {};
|
this.channels = {};
|
||||||
this.listeners = {};
|
this.pending = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
channel: function(channelName, callback) {
|
channel: function(channelName, callback) {
|
||||||
if ( !channelName ) {
|
if ( !channelName ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var channel = this.channels[channelName];
|
||||||
this.channels[channelName] = {
|
if ( channel instanceof MessagingChannel ) {
|
||||||
channelName: channelName,
|
channel.addListener(callback);
|
||||||
listener: typeof callback === 'function' ? callback : null,
|
channel.refCount += 1;
|
||||||
send: function(message, callback) {
|
} else {
|
||||||
if ( vAPI.messaging.port === null ) {
|
channel = this.channels[channelName] = new MessagingChannel(channelName, callback);
|
||||||
vAPI.messaging.setup();
|
|
||||||
}
|
}
|
||||||
|
return channel;
|
||||||
message = {
|
|
||||||
channelName: this.channelName,
|
|
||||||
msg: message
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( callback ) {
|
|
||||||
message.requestId = vAPI.messaging.requestId++;
|
|
||||||
vAPI.messaging.listeners[message.requestId] = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
vAPI.messaging.port.postMessage(message);
|
|
||||||
},
|
|
||||||
close: function() {
|
|
||||||
delete vAPI.messaging.channels[this.channelName];
|
|
||||||
if ( Object.keys(vAPI.messaging.channels).length === 0 ) {
|
|
||||||
vAPI.messaging.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.channels[channelName];
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -90,7 +90,11 @@ vAPI.closePopup = function() {
|
||||||
// This storage is optional, but it is nice to have, for a more polished user
|
// This storage is optional, but it is nice to have, for a more polished user
|
||||||
// experience.
|
// experience.
|
||||||
|
|
||||||
vAPI.localStorage = window.localStorage;
|
// This can throw in some contexts (like in devtool).
|
||||||
|
try {
|
||||||
|
vAPI.localStorage = window.localStorage;
|
||||||
|
} catch (ex) {
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const {classes: Cc} = Components;
|
||||||
|
|
||||||
// Accessing the context of the background page:
|
// Accessing the context of the background page:
|
||||||
// var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=ublock0]').contentWindow;
|
// var win = Services.appShell.hiddenDOMWindow.document.querySelector('iframe[src*=ublock0]').contentWindow;
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ let version;
|
||||||
const hostName = 'ublock0';
|
const hostName = 'ublock0';
|
||||||
const restartListener = {
|
const restartListener = {
|
||||||
get messageManager() {
|
get messageManager() {
|
||||||
return Components.classes['@mozilla.org/parentprocessmessagemanager;1']
|
return Cc['@mozilla.org/parentprocessmessagemanager;1']
|
||||||
.getService(Components.interfaces.nsIMessageListenerManager);
|
.getService(Components.interfaces.nsIMessageListenerManager);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ function startup(data, reason) {
|
||||||
version = data.version;
|
version = data.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
let appShell = Components.classes['@mozilla.org/appshell/appShellService;1']
|
let appShell = Cc['@mozilla.org/appshell/appShellService;1']
|
||||||
.getService(Components.interfaces.nsIAppShellService);
|
.getService(Components.interfaces.nsIAppShellService);
|
||||||
|
|
||||||
let onReady = function(e) {
|
let onReady = function(e) {
|
||||||
|
@ -96,7 +98,7 @@ function startup(data, reason) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ww = Components.classes['@mozilla.org/embedcomp/window-watcher;1']
|
let ww = Cc['@mozilla.org/embedcomp/window-watcher;1']
|
||||||
.getService(Components.interfaces.nsIWindowWatcher);
|
.getService(Components.interfaces.nsIWindowWatcher);
|
||||||
|
|
||||||
ww.registerNotification({
|
ww.registerNotification({
|
||||||
|
@ -141,7 +143,7 @@ function shutdown(data, reason) {
|
||||||
|
|
||||||
function install(/*aData, aReason*/) {
|
function install(/*aData, aReason*/) {
|
||||||
// https://bugzil.la/719376
|
// https://bugzil.la/719376
|
||||||
Components.classes['@mozilla.org/intl/stringbundle;1']
|
Cc['@mozilla.org/intl/stringbundle;1']
|
||||||
.getService(Components.interfaces.nsIStringBundleService)
|
.getService(Components.interfaces.nsIStringBundleService)
|
||||||
.flushBundles();
|
.flushBundles();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1026,7 +1026,6 @@ var tabWatcher = (function() {
|
||||||
tabContainer.removeEventListener('TabSelect', onSelect);
|
tabContainer.removeEventListener('TabSelect', onSelect);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close extension tabs
|
|
||||||
var browser, URI, tabId;
|
var browser, URI, tabId;
|
||||||
for ( var tab of tabBrowser.tabs ) {
|
for ( var tab of tabBrowser.tabs ) {
|
||||||
browser = tabWatcher.browserFromTarget(tab);
|
browser = tabWatcher.browserFromTarget(tab);
|
||||||
|
@ -1034,6 +1033,7 @@ var tabWatcher = (function() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
URI = browser.currentURI;
|
URI = browser.currentURI;
|
||||||
|
// Close extension tabs
|
||||||
if ( URI.schemeIs('chrome') && URI.host === location.host ) {
|
if ( URI.schemeIs('chrome') && URI.host === location.host ) {
|
||||||
vAPI.tabs._remove(tab, getTabBrowser(this));
|
vAPI.tabs._remove(tab, getTabBrowser(this));
|
||||||
}
|
}
|
||||||
|
@ -1239,6 +1239,35 @@ vAPI.messaging.setup = function(defaultHandler) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
vAPI.messaging.send = function(tabId, channelName, message) {
|
||||||
|
var ffTabId = tabId || '';
|
||||||
|
var targetId = location.host + ':broadcast';
|
||||||
|
var payload = JSON.stringify({ channelName: channelName, msg: message });
|
||||||
|
|
||||||
|
if ( ffTabId === '' ) {
|
||||||
|
this.globalMessageManager.broadcastAsyncMessage(targetId, payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var browser = tabWatcher.browserFromTabId(ffTabId);
|
||||||
|
if ( browser === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var messageManager = browser.messageManager || null;
|
||||||
|
if ( messageManager === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( messageManager.sendAsyncMessage ) {
|
||||||
|
messageManager.sendAsyncMessage(targetId, payload);
|
||||||
|
} else {
|
||||||
|
messageManager.broadcastAsyncMessage(targetId, payload);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.messaging.broadcast = function(message) {
|
vAPI.messaging.broadcast = function(message) {
|
||||||
this.globalMessageManager.broadcastAsyncMessage(
|
this.globalMessageManager.broadcastAsyncMessage(
|
||||||
location.host + ':broadcast',
|
location.host + ':broadcast',
|
||||||
|
|
|
@ -67,66 +67,158 @@ vAPI.shutdown = (function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var MessagingListeners = function(callback) {
|
||||||
|
this.listeners = [];
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
this.listeners.push(callback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingListeners.prototype.add = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( this.listeners.indexOf(callback) !== -1 ) {
|
||||||
|
throw new Error('Duplicate listener.');
|
||||||
|
}
|
||||||
|
this.listeners.push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingListeners.prototype.remove = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( this.listeners.indexOf(callback) === -1 ) {
|
||||||
|
throw new Error('Listener not found.');
|
||||||
|
}
|
||||||
|
this.listeners.splice(this.listeners.indexOf(callback), 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingListeners.prototype.process = function(msg) {
|
||||||
|
var listeners = this.listeners;
|
||||||
|
var n = listeners.length;
|
||||||
|
for ( var i = 0; i < n; i++ ) {
|
||||||
|
listeners[i](msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
var messagingConnector = function(response) {
|
var messagingConnector = function(response) {
|
||||||
if ( !response ) {
|
if ( !response ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var channels = vAPI.messaging.channels;
|
var messaging = vAPI.messaging;
|
||||||
var channel, listener;
|
var channels = messaging.channels;
|
||||||
|
var channel;
|
||||||
|
|
||||||
|
// Sent to all channels
|
||||||
if ( response.broadcast === true && !response.channelName ) {
|
if ( response.broadcast === true && !response.channelName ) {
|
||||||
for ( channel in channels ) {
|
for ( channel in channels ) {
|
||||||
if ( channels.hasOwnProperty(channel) === false ) {
|
if ( channels[channel] instanceof MessagingChannel === false ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
listener = channels[channel].listener;
|
channels[channel].listeners.process(response.msg);
|
||||||
if ( typeof listener === 'function' ) {
|
|
||||||
listener(response.msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Response to specific message previously sent
|
||||||
if ( response.requestId ) {
|
if ( response.requestId ) {
|
||||||
listener = vAPI.messaging.listeners[response.requestId];
|
var listener = messaging.pending[response.requestId];
|
||||||
delete vAPI.messaging.listeners[response.requestId];
|
delete messaging.pending[response.requestId];
|
||||||
delete response.requestId;
|
delete response.requestId; // TODO: why?
|
||||||
}
|
if ( listener ) {
|
||||||
|
|
||||||
if ( !listener ) {
|
|
||||||
channel = channels[response.channelName];
|
|
||||||
listener = channel && channel.listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( typeof listener === 'function' ) {
|
|
||||||
listener(response.msg);
|
listener(response.msg);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sent to a specific channel
|
||||||
|
channel = channels[response.channelName];
|
||||||
|
if ( channel instanceof MessagingChannel ) {
|
||||||
|
channel.listeners.process(response.msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var MessagingChannel = function(name, callback) {
|
||||||
|
this.channelName = name;
|
||||||
|
this.listeners = new MessagingListeners(callback);
|
||||||
|
this.refCount = 1;
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
if ( !messaging.connected ) {
|
||||||
|
messaging.setup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.send = function(message, callback) {
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
if ( !messaging.connected ) {
|
||||||
|
messaging.setup();
|
||||||
|
}
|
||||||
|
var requestId;
|
||||||
|
if ( callback ) {
|
||||||
|
requestId = messaging.requestId++;
|
||||||
|
messaging.pending[requestId] = callback;
|
||||||
|
}
|
||||||
|
sendAsyncMessage('ublock0:background', {
|
||||||
|
channelName: self._sandboxId_ + '|' + this.channelName,
|
||||||
|
requestId: requestId,
|
||||||
|
msg: message
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.close = function() {
|
||||||
|
this.refCount -= 1;
|
||||||
|
if ( this.refCount !== 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete vAPI.messaging.channels[this.channelName];
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.addListener = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.listeners.add(callback);
|
||||||
|
var messaging = vAPI.messaging;
|
||||||
|
if ( !messaging.connected ) {
|
||||||
|
messaging.setup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MessagingChannel.prototype.removeListener = function(callback) {
|
||||||
|
if ( typeof callback !== 'function' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.listeners.remove(callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.messaging = {
|
vAPI.messaging = {
|
||||||
channels: {},
|
channels: {},
|
||||||
listeners: {},
|
pending: {},
|
||||||
requestId: 1,
|
requestId: 1,
|
||||||
|
connected: false,
|
||||||
|
connector: function(msg) {
|
||||||
|
messagingConnector(JSON.parse(msg));
|
||||||
|
},
|
||||||
|
|
||||||
setup: function() {
|
setup: function() {
|
||||||
this.connector = function(msg) {
|
|
||||||
messagingConnector(JSON.parse(msg));
|
|
||||||
};
|
|
||||||
|
|
||||||
addMessageListener(this.connector);
|
addMessageListener(this.connector);
|
||||||
|
this.connected = true;
|
||||||
this.channels['vAPI'] = {};
|
this.channels['vAPI'] = new MessagingChannel('vAPI', function(msg) {
|
||||||
this.channels['vAPI'].listener = function(msg) {
|
|
||||||
if ( msg.cmd === 'injectScript' ) {
|
if ( msg.cmd === 'injectScript' ) {
|
||||||
var details = msg.details;
|
var details = msg.details;
|
||||||
|
|
||||||
if ( !details.allFrames && window !== window.top ) {
|
if ( !details.allFrames && window !== window.top ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: investigate why this happens, and if this happens
|
// TODO: investigate why this happens, and if this happens
|
||||||
// legitimately (content scripts not injected I suspect, so
|
// legitimately (content scripts not injected I suspect, so
|
||||||
// that would make this legitimate).
|
// that would make this legitimate).
|
||||||
|
@ -135,55 +227,35 @@ vAPI.messaging = {
|
||||||
self.injectScript(details.file);
|
self.injectScript(details.file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
close: function() {
|
close: function() {
|
||||||
if ( !this.connector ) {
|
if ( !this.connected ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeMessageListener();
|
removeMessageListener();
|
||||||
this.connector = null;
|
this.connected = false;
|
||||||
this.channels = {};
|
this.channels = {};
|
||||||
this.listeners = {};
|
this.pending = {};
|
||||||
},
|
},
|
||||||
|
|
||||||
channel: function(channelName, callback) {
|
channel: function(channelName, callback) {
|
||||||
if ( !channelName ) {
|
if ( !channelName ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var channel = this.channels[channelName];
|
||||||
this.channels[channelName] = {
|
if ( channel instanceof MessagingChannel ) {
|
||||||
channelName: channelName,
|
channel.addListener(callback);
|
||||||
listener: typeof callback === 'function' ? callback : null,
|
channel.refCount += 1;
|
||||||
send: function(message, callback) {
|
} else {
|
||||||
if ( !vAPI.messaging.connector ) {
|
channel = this.channels[channelName] = new MessagingChannel(channelName, callback);
|
||||||
vAPI.messaging.setup();
|
|
||||||
}
|
}
|
||||||
|
return channel;
|
||||||
message = {
|
|
||||||
channelName: self._sandboxId_ + '|' + this.channelName,
|
|
||||||
msg: message
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( callback ) {
|
|
||||||
message.requestId = vAPI.messaging.requestId++;
|
|
||||||
vAPI.messaging.listeners[message.requestId] = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendAsyncMessage('ublock0:background', message);
|
|
||||||
},
|
|
||||||
close: function() {
|
|
||||||
delete vAPI.messaging.channels[this.channelName];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.channels[channelName];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleListener: function({type, persisted}) {
|
toggleListener: function({type, persisted}) {
|
||||||
if ( !vAPI.messaging.connector ) {
|
if ( !vAPI.messaging.connected ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,27 @@ body {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: black;
|
color: black;
|
||||||
-moz-box-sizing: border-box;
|
-moz-box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
justify-content: flex-start;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow-x: hidden;
|
overflow: hidden;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100vw;
|
||||||
}
|
}
|
||||||
#toolbar {
|
input:focus {
|
||||||
|
background-color: #ffe;
|
||||||
|
}
|
||||||
|
.permatoolbar {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 0;
|
border: 0;
|
||||||
box-sizing: border-box;
|
font-size: 120%;
|
||||||
left: 0;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0.5em 1em;
|
padding: 0.25em 0.5em;
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
#toolbar .button {
|
.permatoolbar .button {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: none;
|
border: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -31,112 +34,195 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
}
|
}
|
||||||
#toolbar .button.disabled {
|
.permatoolbar .button.disabled {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
#toolbar .button:hover {
|
.permatoolbar .button.active {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.permatoolbar .button:hover {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
}
|
}
|
||||||
#toolbar > div {
|
.permatoolbar > div {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#toolbar > div:first-of-type {
|
.permatoolbar > div > * {
|
||||||
font-size: 120%;
|
|
||||||
}
|
|
||||||
#toolbar > div > * {
|
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
#pageSelector {
|
#pageSelector {
|
||||||
width: 28em;
|
width: 28em;
|
||||||
padding: 0.2em 0;
|
padding: 0.2em 0;
|
||||||
}
|
}
|
||||||
body #compactViewToggler.button:before {
|
|
||||||
content: '\f102';
|
#domInspector {
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
display: none;
|
||||||
|
max-height: 40%;
|
||||||
|
min-height: 40%;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
body.compactView #compactViewToggler.button:before {
|
#domInspector.enabled {
|
||||||
content: '\f103';
|
display: block;
|
||||||
}
|
}
|
||||||
#filterButton {
|
#domInspector > ul:first-child {
|
||||||
opacity: 0.25;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
body.f #filterButton {
|
#domInspector ul {
|
||||||
opacity: 1;
|
background-color: #fff;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 1em;
|
||||||
}
|
}
|
||||||
#filterInput.bad {
|
#domInspector li {
|
||||||
|
list-style-type: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
#domInspector li.isCosmeticHide,
|
||||||
|
#domInspector li.isCosmeticHide ul,
|
||||||
|
#domInspector li.isCosmeticHide li {
|
||||||
background-color: #fee;
|
background-color: #fee;
|
||||||
}
|
}
|
||||||
#maxEntries {
|
#domInspector li > * {
|
||||||
margin: 0 2em;
|
margin-right: 1em;
|
||||||
}
|
}
|
||||||
input:focus {
|
#domInspector li > span:first-child {
|
||||||
background-color: #ffe;
|
color: #000;
|
||||||
|
cursor: default;
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 0;
|
||||||
|
opacity: 0.5;
|
||||||
|
visibility: hidden;
|
||||||
|
width: 1em;
|
||||||
}
|
}
|
||||||
#content {
|
#domInspector li > span:first-child:hover {
|
||||||
font: 13px sans-serif;
|
opacity: 1;
|
||||||
width: 100%;
|
}
|
||||||
|
#domInspector li > *:last-child {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
#domInspector li > span:first-child:before {
|
||||||
|
content: '\a0';
|
||||||
|
}
|
||||||
|
#domInspector li.branch > span:first-child:before {
|
||||||
|
content: '\25b8';
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
#domInspector li.branch.show > span:first-child:before {
|
||||||
|
content: '\25be';
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
#domInspector li.branch.hasCosmeticHide > span:first-child:before {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
#domInspector li > code {
|
||||||
|
cursor: pointer;
|
||||||
|
font: 12px/1.4 monospace;
|
||||||
|
}
|
||||||
|
#domInspector li > code.off {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
#domInspector li > span {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
#domInspector li > code.filter {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
#domInspector li > ul {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
#domInspector li.show > ul {
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content table {
|
#events {
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
font: 13px sans-serif;
|
||||||
|
min-height: 60%;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#events .permatoolbar {
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
#events #compactViewToggler.button:before {
|
||||||
|
content: '\f102';
|
||||||
|
}
|
||||||
|
#events.compactView #compactViewToggler.button:before {
|
||||||
|
content: '\f103';
|
||||||
|
}
|
||||||
|
#events #filterButton {
|
||||||
|
opacity: 0.25;
|
||||||
|
}
|
||||||
|
#events.f #filterButton {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#events #filterInput.bad {
|
||||||
|
background-color: #fee;
|
||||||
|
}
|
||||||
|
#events #maxEntries {
|
||||||
|
margin: 0 2em;
|
||||||
|
}
|
||||||
|
#events table {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
#content table > colgroup > col:nth-of-type(1) {
|
#events table > colgroup > col:nth-of-type(1) {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
#content table > colgroup > col:nth-of-type(2) {
|
#events table > colgroup > col:nth-of-type(2) {
|
||||||
width: 2.5em;
|
width: 2.5em;
|
||||||
}
|
}
|
||||||
#content table > colgroup > col:nth-of-type(3) {
|
#events table > colgroup > col:nth-of-type(3) {
|
||||||
width: 20%;
|
width: 20%;
|
||||||
}
|
}
|
||||||
#content table > colgroup > col:nth-of-type(4) {
|
#events table > colgroup > col:nth-of-type(4) {
|
||||||
width: 2.5em;
|
width: 2.5em;
|
||||||
}
|
}
|
||||||
#content table > colgroup > col:nth-of-type(5) {
|
#events table > colgroup > col:nth-of-type(5) {
|
||||||
width: 6em;
|
width: 6em;
|
||||||
}
|
}
|
||||||
#content table > colgroup > col:nth-of-type(6) {
|
#events table > colgroup > col:nth-of-type(6) {
|
||||||
width: calc(100% - 16em - 20%);
|
width: calc(100% - 16em - 20%);
|
||||||
}
|
}
|
||||||
body.f #content table tr.f {
|
#events.f table tr.f {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content tr.cat_info {
|
#events tr.cat_info {
|
||||||
color: #00f;
|
color: #00f;
|
||||||
}
|
}
|
||||||
#content tr.blocked {
|
#events tr.blocked {
|
||||||
background-color: rgba(192, 0, 0, 0.1);
|
background-color: rgba(192, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
body.colorBlind #content tr.blocked {
|
body.colorBlind #events tr.blocked {
|
||||||
background-color: rgba(0, 19, 110, 0.1);
|
background-color: rgba(0, 19, 110, 0.1);
|
||||||
}
|
}
|
||||||
#content tr.nooped {
|
#events tr.nooped {
|
||||||
background-color: rgba(108, 108, 108, 0.1);
|
background-color: rgba(108, 108, 108, 0.1);
|
||||||
}
|
}
|
||||||
body.colorBlind #content tr.nooped {
|
body.colorBlind #events tr.nooped {
|
||||||
background-color: rgba(96, 96, 96, 0.1);
|
background-color: rgba(96, 96, 96, 0.1);
|
||||||
}
|
}
|
||||||
#content tr.allowed {
|
#events tr.allowed {
|
||||||
background-color: rgba(0, 160, 0, 0.1);
|
background-color: rgba(0, 160, 0, 0.1);
|
||||||
}
|
}
|
||||||
body.colorBlind #content tr.allowed {
|
body.colorBlind #events tr.allowed {
|
||||||
background-color: rgba(255, 194, 57, 0.1)
|
background-color: rgba(255, 194, 57, 0.1)
|
||||||
}
|
}
|
||||||
#content tr.cb {
|
#events tr.cb {
|
||||||
background-color: rgba(255, 255, 0, 0.1);
|
background-color: rgba(255, 255, 0, 0.1);
|
||||||
}
|
}
|
||||||
#content tr.maindoc {
|
#events tr.maindoc {
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
color: white;
|
color: white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
body #content td {
|
body #events td {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
min-width: 0.5em;
|
min-width: 0.5em;
|
||||||
|
@ -146,87 +232,87 @@ body #content td {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
#content tr td {
|
#events tr td {
|
||||||
border-top: 1px solid #ccc;
|
border-top: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
#content tr td:first-of-type {
|
#events tr td:first-of-type {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
}
|
}
|
||||||
#content tr td:last-of-type {
|
#events tr td:last-of-type {
|
||||||
border-right: none;
|
border-right: none;
|
||||||
}
|
}
|
||||||
body.compactView #content td {
|
#events.compactView td {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content tr td:nth-of-type(1) {
|
#events tr td:nth-of-type(1) {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#content tr td:nth-of-type(2) {
|
#events tr td:nth-of-type(2) {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#content tr.tab_bts > td:nth-of-type(2):before {
|
#events tr.tab_bts > td:nth-of-type(2):before {
|
||||||
content: '\f070';
|
content: '\f070';
|
||||||
font: 1em FontAwesome;
|
font: 1em FontAwesome;
|
||||||
}
|
}
|
||||||
#content tr.tab:not(.canMtx) {
|
#events tr.tab:not(.canMtx) {
|
||||||
opacity: 0.3;
|
opacity: 0.3;
|
||||||
}
|
}
|
||||||
#content tr.tab:not(.canMtx):hover {
|
#events tr.tab:not(.canMtx):hover {
|
||||||
opacity: 0.7;
|
opacity: 0.7;
|
||||||
}
|
}
|
||||||
#content tr.tab:not(.canMtx) > td:nth-of-type(2):before {
|
#events tr.tab:not(.canMtx) > td:nth-of-type(2):before {
|
||||||
content: '\f00d';
|
content: '\f00d';
|
||||||
font: 1em FontAwesome;
|
font: 1em FontAwesome;
|
||||||
}
|
}
|
||||||
body:not(.popupOn) #content tr.canMtx td:nth-of-type(2) {
|
body:not(.popupOn) #events tr.canMtx td:nth-of-type(2) {
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
body:not(.popupOn) #content tr.canMtx td:nth-of-type(2):hover {
|
body:not(.popupOn) #events tr.canMtx td:nth-of-type(2):hover {
|
||||||
background: #ccc;
|
background: #ccc;
|
||||||
}
|
}
|
||||||
#content tr.canLookup td:nth-of-type(3) {
|
#events tr.canLookup td:nth-of-type(3) {
|
||||||
cursor: zoom-in;
|
cursor: zoom-in;
|
||||||
}
|
}
|
||||||
#content tr.cat_net td:nth-of-type(4),
|
#events tr.cat_net td:nth-of-type(4),
|
||||||
#content tr.cat_cosmetic td:nth-of-type(4) {
|
#events tr.cat_cosmetic td:nth-of-type(4) {
|
||||||
font: 12px monospace;
|
font: 12px monospace;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
#content tr.cat_net td:nth-of-type(4) {
|
#events tr.cat_net td:nth-of-type(4) {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
#content tr.cat_net td:nth-of-type(4):hover {
|
#events tr.cat_net td:nth-of-type(4):hover {
|
||||||
background: #ccc;
|
background: #ccc;
|
||||||
}
|
}
|
||||||
#content tr.cat_net td:nth-of-type(6) > span > b {
|
#events tr.cat_net td:nth-of-type(6) > span > b {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
#content tr td:nth-of-type(6) b {
|
#events tr td:nth-of-type(6) b {
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
#content tr.blocked td:nth-of-type(6) b {
|
#events tr.blocked td:nth-of-type(6) b {
|
||||||
background-color: rgba(192, 0, 0, 0.2);
|
background-color: rgba(192, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
body.colorBlind #content tr.blocked td:nth-of-type(6) b {
|
body.colorBlind #events tr.blocked td:nth-of-type(6) b {
|
||||||
background-color: rgba(0, 19, 110, 0.2);
|
background-color: rgba(0, 19, 110, 0.2);
|
||||||
}
|
}
|
||||||
#content tr.nooped td:nth-of-type(6) b {
|
#events tr.nooped td:nth-of-type(6) b {
|
||||||
background-color: rgba(108, 108, 108, 0.2);
|
background-color: rgba(108, 108, 108, 0.2);
|
||||||
}
|
}
|
||||||
body.colorBlind #content tr.nooped td:nth-of-type(6) b {
|
body.colorBlind #events tr.nooped td:nth-of-type(6) b {
|
||||||
background-color: rgba(96, 96, 96, 0.2);
|
background-color: rgba(96, 96, 96, 0.2);
|
||||||
}
|
}
|
||||||
#content tr.allowed td:nth-of-type(6) b {
|
#events tr.allowed td:nth-of-type(6) b {
|
||||||
background-color: rgba(0, 160, 0, 0.2);
|
background-color: rgba(0, 160, 0, 0.2);
|
||||||
}
|
}
|
||||||
body.colorBlind #content tr.allowed td:nth-of-type(6) b {
|
body.colorBlind #events tr.allowed td:nth-of-type(6) b {
|
||||||
background-color: rgba(255, 194, 57, 0.2);
|
background-color: rgba(255, 194, 57, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +328,7 @@ body.colorBlind #content tr.allowed td:nth-of-type(6) b {
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 200;
|
z-index: 200;
|
||||||
}
|
}
|
||||||
body.popupOn #popupContainer {
|
#events.popupOn #popupContainer {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
#popupContainer > div {
|
#popupContainer > div {
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
}
|
}
|
||||||
html, body {
|
html, body {
|
||||||
background: transparent !important;
|
background: transparent !important;
|
||||||
margin: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
font: 12px sans-serif;
|
font: 12px sans-serif;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
ul, li, div {
|
ul, li, div {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -43,9 +43,6 @@ button:not(:disabled):hover {
|
||||||
#create:not(:disabled) {
|
#create:not(:disabled) {
|
||||||
background-color: #ffdca8;
|
background-color: #ffdca8;
|
||||||
}
|
}
|
||||||
aside {
|
|
||||||
background-color: #eee;
|
|
||||||
}
|
|
||||||
section {
|
section {
|
||||||
border: 0;
|
border: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -80,7 +77,7 @@ ul {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
aside > ul {
|
aside > ul {
|
||||||
height: 12em;
|
height: 16em;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
aside > ul > li:first-of-type {
|
aside > ul > li:first-of-type {
|
||||||
|
@ -129,16 +126,20 @@ svg > path + path {
|
||||||
fill: rgba(255,31,31,0.25);
|
fill: rgba(255,31,31,0.25);
|
||||||
}
|
}
|
||||||
aside {
|
aside {
|
||||||
|
background-color: #eee;
|
||||||
bottom: 4px;
|
bottom: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
visibility: hidden;
|
||||||
|
height: calc(40% - 4px);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
display: none;
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 4px;
|
right: 4px;
|
||||||
width: 30em;
|
width: calc(40% - 4px);
|
||||||
}
|
}
|
||||||
body.paused > aside {
|
body.paused > aside {
|
||||||
opacity: 0.2;
|
opacity: 0.1;
|
||||||
display: block;
|
visibility: visible;
|
||||||
|
z-index: 100;
|
||||||
}
|
}
|
||||||
body.paused > aside:hover {
|
body.paused > aside:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
|
|
@ -32,15 +32,24 @@
|
||||||
|
|
||||||
// Adjust top padding of content table, to match that of toolbar height.
|
// Adjust top padding of content table, to match that of toolbar height.
|
||||||
|
|
||||||
uDom.nodeFromId('content').style.setProperty(
|
(function() {
|
||||||
|
var parent = uDom.nodeFromSelector('body > .permatoolbar');
|
||||||
|
var child = parent.firstElementChild;
|
||||||
|
var size = child.clientHeight + 'px';
|
||||||
|
parent.style.setProperty('min-height', size);
|
||||||
|
|
||||||
|
parent = uDom.nodeFromId('events');
|
||||||
|
parent.querySelector('table').style.setProperty(
|
||||||
'margin-top',
|
'margin-top',
|
||||||
uDom.nodeFromId('toolbar').offsetHeight + 'px'
|
parent.querySelector('.permatoolbar').clientHeight + 'px'
|
||||||
);
|
);
|
||||||
|
})();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var messager = vAPI.messaging.channel('logger-ui.js');
|
var messager = vAPI.messaging.channel('logger-ui.js');
|
||||||
var tbody = document.querySelector('#content tbody');
|
|
||||||
|
var tbody = document.querySelector('#events tbody');
|
||||||
var trJunkyard = [];
|
var trJunkyard = [];
|
||||||
var tdJunkyard = [];
|
var tdJunkyard = [];
|
||||||
var firstVarDataCol = 2; // currently, column 2 (0-based index)
|
var firstVarDataCol = 2; // currently, column 2 (0-based index)
|
||||||
|
@ -89,6 +98,14 @@ var dateOptions = {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var removeAllChildren = function(node) {
|
||||||
|
while ( node.firstChild ) {
|
||||||
|
node.removeChild(node.firstChild);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
var classNameFromTabId = function(tabId) {
|
var classNameFromTabId = function(tabId) {
|
||||||
if ( tabId === noTabId ) {
|
if ( tabId === noTabId ) {
|
||||||
return 'tab_bts';
|
return 'tab_bts';
|
||||||
|
@ -109,6 +126,427 @@ var tabIdFromClassName = function(className) {
|
||||||
return matches[1];
|
return matches[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// DOM inspector
|
||||||
|
|
||||||
|
(function domInspector() {
|
||||||
|
// Don't bother if the browser is not modern enough.
|
||||||
|
if ( typeof Map === undefined ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var enabled = false;
|
||||||
|
var state = '';
|
||||||
|
var fingerprint = '';
|
||||||
|
var fingerprintTimer = null;
|
||||||
|
var currentTabId = '';
|
||||||
|
var currentSelector = '';
|
||||||
|
var inspector = uDom.nodeFromId('domInspector');
|
||||||
|
var tabSelector = uDom.nodeFromId('pageSelector');
|
||||||
|
|
||||||
|
var inlineElementTags = [
|
||||||
|
'a', 'abbr', 'acronym',
|
||||||
|
'b', 'bdo', 'big', 'br', 'button',
|
||||||
|
'cite', 'code',
|
||||||
|
'del', 'dfn',
|
||||||
|
'em',
|
||||||
|
'font',
|
||||||
|
'i', 'img', 'input', 'ins',
|
||||||
|
'kbd',
|
||||||
|
'label',
|
||||||
|
'map',
|
||||||
|
'object',
|
||||||
|
'q',
|
||||||
|
'samp', 'select', 'small', 'span', 'strong', 'sub', 'sup',
|
||||||
|
'textarea', 'tt',
|
||||||
|
'u',
|
||||||
|
'var'
|
||||||
|
].reduce(function(p, c) {
|
||||||
|
p[c] = true;
|
||||||
|
return p;
|
||||||
|
}, Object.create(null));
|
||||||
|
|
||||||
|
var startTimer = function() {
|
||||||
|
if ( fingerprintTimer === null ) {
|
||||||
|
fingerprintTimer = vAPI.setTimeout(fetchFingerprint, 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var stopTimer = function() {
|
||||||
|
if ( fingerprintTimer !== null ) {
|
||||||
|
clearTimeout(fingerprintTimer);
|
||||||
|
fingerprintTimer = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var injectHighlighter = function(tabId) {
|
||||||
|
if ( tabId === '' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
messager.send({
|
||||||
|
what: 'scriptlet',
|
||||||
|
tabId: tabId,
|
||||||
|
scriptlet: 'dom-highlight'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var removeHighlighter = function(tabId) {
|
||||||
|
if ( tabId === '' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
messager.send({
|
||||||
|
what: 'sendMessageTo',
|
||||||
|
tabId: tabId,
|
||||||
|
channelName: 'scriptlets',
|
||||||
|
msg: {
|
||||||
|
what: 'dom-highlight',
|
||||||
|
action: 'shutdown'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var nodeFromDomEntry = function(entry) {
|
||||||
|
var node;
|
||||||
|
var li = document.createElement('li');
|
||||||
|
// expander/collapser
|
||||||
|
node = document.createElement('span');
|
||||||
|
li.appendChild(node);
|
||||||
|
// selector
|
||||||
|
node = document.createElement('code');
|
||||||
|
node.textContent = entry.sel;
|
||||||
|
li.appendChild(node);
|
||||||
|
// descendant count
|
||||||
|
if ( entry.cnt !== 0 ) {
|
||||||
|
node = document.createElement('span');
|
||||||
|
node.textContent = entry.cnt.toLocaleString();
|
||||||
|
li.appendChild(node);
|
||||||
|
}
|
||||||
|
// cosmetic filter
|
||||||
|
if ( entry.filter !== undefined ) {
|
||||||
|
node = document.createElement('code');
|
||||||
|
node.classList.add('filter');
|
||||||
|
node.textContent = entry.filter;
|
||||||
|
li.appendChild(node);
|
||||||
|
li.classList.add('isCosmeticHide');
|
||||||
|
}
|
||||||
|
return li;
|
||||||
|
};
|
||||||
|
|
||||||
|
var appendListItem = function(ul, li) {
|
||||||
|
ul.appendChild(li);
|
||||||
|
// Ancestor nodes of a node which is affected by a cosmetic filter will
|
||||||
|
// be marked as "containing cosmetic filters", for user convenience.
|
||||||
|
if ( li.classList.contains('isCosmeticHide') === false ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (;;) {
|
||||||
|
li = li.parentElement.parentElement;
|
||||||
|
if ( li === null ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
li.classList.add('hasCosmeticHide');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var expandIfBlockElement = function(node) {
|
||||||
|
var ul = node.parentElement;
|
||||||
|
if ( ul === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var li = ul.parentElement;
|
||||||
|
if ( li === null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( li.classList.contains('show') ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tag = node.firstElementChild.textContent;
|
||||||
|
var pos = tag.search(/[^a-z0-9]/);
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
tag = tag.slice(0, pos);
|
||||||
|
}
|
||||||
|
if ( inlineElementTags[tag] !== undefined ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
li.classList.add('show');
|
||||||
|
};
|
||||||
|
|
||||||
|
var renderDOM = function(response) {
|
||||||
|
var ul = document.createElement('ul');
|
||||||
|
var lvl = 0;
|
||||||
|
var entries = response;
|
||||||
|
var n = entries.length;
|
||||||
|
var li, entry;
|
||||||
|
for ( var i = 0; i < n; i++ ) {
|
||||||
|
entry = entries[i];
|
||||||
|
if ( entry.lvl === lvl ) {
|
||||||
|
li = nodeFromDomEntry(entry);
|
||||||
|
appendListItem(ul, li);
|
||||||
|
//expandIfBlockElement(li);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( entry.lvl > lvl ) {
|
||||||
|
ul = document.createElement('ul');
|
||||||
|
li.appendChild(ul);
|
||||||
|
li.classList.add('branch');
|
||||||
|
li = nodeFromDomEntry(entry);
|
||||||
|
appendListItem(ul, li);
|
||||||
|
//expandIfBlockElement(li);
|
||||||
|
lvl = entry.lvl;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// entry.lvl < lvl
|
||||||
|
while ( entry.lvl < lvl ) {
|
||||||
|
ul = li.parentNode;
|
||||||
|
li = ul.parentNode;
|
||||||
|
ul = li.parentNode;
|
||||||
|
lvl -= 1;
|
||||||
|
}
|
||||||
|
li = nodeFromDomEntry(entry);
|
||||||
|
ul.appendChild(li);
|
||||||
|
}
|
||||||
|
while ( ul.parentNode !== null ) {
|
||||||
|
ul = ul.parentNode;
|
||||||
|
}
|
||||||
|
ul.firstElementChild.classList.add('show');
|
||||||
|
|
||||||
|
removeAllChildren(inspector);
|
||||||
|
inspector.appendChild(ul);
|
||||||
|
|
||||||
|
// Check for change at regular interval.
|
||||||
|
fingerprint = entries.length !== 0 ? entries[0].fp : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectorFromNode = function(node, nth) {
|
||||||
|
var selector = '';
|
||||||
|
var code;
|
||||||
|
if ( nth === undefined ) {
|
||||||
|
nth = 1;
|
||||||
|
}
|
||||||
|
while ( node !== null ) {
|
||||||
|
if ( node.localName === 'li' ) {
|
||||||
|
code = node.querySelector('code:nth-of-type(' + nth + ')');
|
||||||
|
if ( code !== null ) {
|
||||||
|
selector = code.textContent + ' > ' + selector;
|
||||||
|
if ( selector.indexOf('#') !== -1 ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nth = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node = node.parentElement;
|
||||||
|
}
|
||||||
|
return selector.slice(0, -3);
|
||||||
|
};
|
||||||
|
|
||||||
|
var onClick = function(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
|
||||||
|
if ( currentTabId === '' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var target = ev.target;
|
||||||
|
var parent = target.parentElement;
|
||||||
|
|
||||||
|
// Expand/collapse branch
|
||||||
|
if (
|
||||||
|
target.localName === 'span' &&
|
||||||
|
parent instanceof HTMLLIElement &&
|
||||||
|
parent.classList.contains('branch') &&
|
||||||
|
target === parent.firstElementChild
|
||||||
|
) {
|
||||||
|
target.parentElement.classList.toggle('show');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle selector
|
||||||
|
if ( target.localName === 'code' ) {
|
||||||
|
var original = target.classList.contains('filter') === false;
|
||||||
|
messager.send({
|
||||||
|
what: 'sendMessageTo',
|
||||||
|
tabId: currentTabId,
|
||||||
|
channelName: 'scriptlets',
|
||||||
|
msg: {
|
||||||
|
what: 'dom-highlight',
|
||||||
|
action: 'toggleNodes',
|
||||||
|
original: original,
|
||||||
|
target: original !== target.classList.toggle('off'),
|
||||||
|
selector: selectorFromNode(target, original ? 1 : 2)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlight and scrollto
|
||||||
|
if ( target.localName === 'code' ) {
|
||||||
|
messager.send({
|
||||||
|
what: 'sendMessageTo',
|
||||||
|
tabId: currentTabId,
|
||||||
|
channelName: 'scriptlets',
|
||||||
|
msg: {
|
||||||
|
what: 'dom-highlight',
|
||||||
|
action: 'highlight',
|
||||||
|
selector: selectorFromNode(target),
|
||||||
|
scrollTo: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var onMouseOver = (function() {
|
||||||
|
var mouseoverTarget = null;
|
||||||
|
var mouseoverTimer = null;
|
||||||
|
|
||||||
|
var timerHandler = function() {
|
||||||
|
mouseoverTimer = null;
|
||||||
|
messager.send({
|
||||||
|
what: 'sendMessageTo',
|
||||||
|
tabId: currentTabId,
|
||||||
|
channelName: 'scriptlets',
|
||||||
|
msg: {
|
||||||
|
what: 'dom-highlight',
|
||||||
|
action: 'highlight',
|
||||||
|
selector: selectorFromNode(mouseoverTarget),
|
||||||
|
scrollTo: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(ev) {
|
||||||
|
// Find closest `li`
|
||||||
|
var target = ev.target;
|
||||||
|
while ( target !== null ) {
|
||||||
|
if ( target.localName === 'li' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
target = target.parentElement;
|
||||||
|
}
|
||||||
|
if ( target === mouseoverTarget ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mouseoverTarget = target;
|
||||||
|
if ( mouseoverTimer === null ) {
|
||||||
|
mouseoverTimer = vAPI.setTimeout(timerHandler, 50);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
var onFingerprintFetched = function(response) {
|
||||||
|
if ( state !== 'fetchingFingerprint' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = '';
|
||||||
|
fingerprintTimer = null;
|
||||||
|
if ( !enabled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( response === fingerprint ) {
|
||||||
|
startTimer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fingerprint = response || '';
|
||||||
|
fetchDOM();
|
||||||
|
};
|
||||||
|
|
||||||
|
var fetchFingerprint = function() {
|
||||||
|
messager.send({
|
||||||
|
what: 'scriptlet',
|
||||||
|
tabId: currentTabId,
|
||||||
|
scriptlet: 'dom-fingerprint'
|
||||||
|
}, onFingerprintFetched);
|
||||||
|
state = 'fetchingFingerprint';
|
||||||
|
};
|
||||||
|
|
||||||
|
var onDOMFetched = function(response) {
|
||||||
|
if ( state !== 'fetchingDOM' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state = '';
|
||||||
|
if ( !enabled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( Array.isArray(response) && response.length !== 0 ) {
|
||||||
|
renderDOM(response);
|
||||||
|
injectHighlighter(currentTabId);
|
||||||
|
} else {
|
||||||
|
fingerprint = '';
|
||||||
|
}
|
||||||
|
startTimer();
|
||||||
|
};
|
||||||
|
|
||||||
|
var fetchDOM = function() {
|
||||||
|
if ( currentTabId === '' ) {
|
||||||
|
removeAllChildren(inspector);
|
||||||
|
startTimer();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
messager.send({
|
||||||
|
what: 'scriptlet',
|
||||||
|
tabId: currentTabId,
|
||||||
|
scriptlet: 'dom-layout'
|
||||||
|
}, onDOMFetched);
|
||||||
|
state = 'fetchingDOM';
|
||||||
|
};
|
||||||
|
|
||||||
|
var onTabIdChanged = function() {
|
||||||
|
if ( !enabled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var previousTabId = currentTabId;
|
||||||
|
var tabId = tabIdFromClassName(tabSelector.value) || '';
|
||||||
|
currentTabId = tabId !== 'bts' && tabId !== '' ? tabId : '';
|
||||||
|
if ( currentTabId !== previousTabId ) {
|
||||||
|
removeHighlighter(previousTabId);
|
||||||
|
}
|
||||||
|
if ( state === 'fetchingDOM' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetchDOM();
|
||||||
|
};
|
||||||
|
|
||||||
|
var toggleOn = function() {
|
||||||
|
if ( enabled ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enabled = true;
|
||||||
|
window.addEventListener('beforeunload', toggleOff);
|
||||||
|
inspector.addEventListener('click', onClick, true);
|
||||||
|
inspector.addEventListener('mouseover', onMouseOver, true);
|
||||||
|
tabSelector.addEventListener('change', onTabIdChanged);
|
||||||
|
onTabIdChanged();
|
||||||
|
inspector.classList.add('enabled');
|
||||||
|
};
|
||||||
|
|
||||||
|
var toggleOff = function() {
|
||||||
|
removeHighlighter(currentTabId);
|
||||||
|
window.removeEventListener('beforeunload', toggleOff);
|
||||||
|
inspector.removeEventListener('click', onClick, true);
|
||||||
|
inspector.removeEventListener('mouseover', onMouseOver, true);
|
||||||
|
tabSelector.removeEventListener('change', onTabIdChanged);
|
||||||
|
removeAllChildren(inspector);
|
||||||
|
stopTimer();
|
||||||
|
currentTabId = currentSelector = fingerprint = '';
|
||||||
|
enabled = false;
|
||||||
|
inspector.classList.remove('enabled');
|
||||||
|
};
|
||||||
|
|
||||||
|
var toggle = function() {
|
||||||
|
if ( uDom.nodeFromId('showdom').classList.toggle('active') ) {
|
||||||
|
toggleOn();
|
||||||
|
} else {
|
||||||
|
toggleOff();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
uDom('#showdom').on('click', toggle);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var regexFromURLFilteringResult = function(result) {
|
var regexFromURLFilteringResult = function(result) {
|
||||||
|
@ -238,14 +676,17 @@ var filterDecompiler = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter options
|
// Filter options
|
||||||
|
// Importance
|
||||||
if ( bits & 0x02 ) {
|
if ( bits & 0x02 ) {
|
||||||
opts.push('important');
|
opts.push('important');
|
||||||
}
|
}
|
||||||
|
// Party
|
||||||
if ( bits & 0x08 ) {
|
if ( bits & 0x08 ) {
|
||||||
opts.push('third-party');
|
opts.push('third-party');
|
||||||
} else if ( bits & 0x04 ) {
|
} else if ( bits & 0x04 ) {
|
||||||
opts.push('first-party');
|
opts.push('first-party');
|
||||||
}
|
}
|
||||||
|
// Type
|
||||||
var typeVal = bits >>> 4 & 0x0F;
|
var typeVal = bits >>> 4 & 0x0F;
|
||||||
if ( typeVal ) {
|
if ( typeVal ) {
|
||||||
opts.push(typeValToTypeName[typeVal]);
|
opts.push(typeValToTypeName[typeVal]);
|
||||||
|
@ -552,25 +993,14 @@ var renderLogEntries = function(response) {
|
||||||
// dynamically refreshed pages.
|
// dynamically refreshed pages.
|
||||||
truncateLog(maxEntries);
|
truncateLog(maxEntries);
|
||||||
|
|
||||||
|
// Follow waterfall if not observing top of waterfall.
|
||||||
var yDelta = tbody.offsetHeight - height;
|
var yDelta = tbody.offsetHeight - height;
|
||||||
if ( yDelta === 0 ) {
|
if ( yDelta === 0 ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
var container = uDom.nodeFromId('events');
|
||||||
// Chromium:
|
if ( container.scrollTop !== 0 ) {
|
||||||
// body.scrollTop = good value
|
container.scrollTop += yDelta;
|
||||||
// body.parentNode.scrollTop = 0
|
|
||||||
if ( document.body.scrollTop !== 0 ) {
|
|
||||||
document.body.scrollTop += yDelta;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Firefox:
|
|
||||||
// body.scrollTop = 0
|
|
||||||
// body.parentNode.scrollTop = good value
|
|
||||||
var parentNode = document.body.parentNode;
|
|
||||||
if ( parentNode && parentNode.scrollTop !== 0 ) {
|
|
||||||
parentNode.scrollTop += yDelta;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -648,7 +1078,7 @@ var truncateLog = function(size) {
|
||||||
if ( size === 0 ) {
|
if ( size === 0 ) {
|
||||||
size = 5000;
|
size = 5000;
|
||||||
}
|
}
|
||||||
var tbody = document.querySelector('#content tbody');
|
var tbody = document.querySelector('#events tbody');
|
||||||
size = Math.min(size, 10000);
|
size = Math.min(size, 10000);
|
||||||
var tr;
|
var tr;
|
||||||
while ( tbody.childElementCount > size ) {
|
while ( tbody.childElementCount > size ) {
|
||||||
|
@ -715,11 +1145,11 @@ var pageSelectorChanged = function() {
|
||||||
}
|
}
|
||||||
if ( tabClass !== '' ) {
|
if ( tabClass !== '' ) {
|
||||||
sheet.insertRule(
|
sheet.insertRule(
|
||||||
'#content table tr:not(.' + tabClass + ') { display: none; }',
|
'#events table tr:not(.' + tabClass + ') { display: none; }',
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
uDom('#refresh').toggleClass(
|
uDom('.needtab').toggleClass(
|
||||||
'disabled',
|
'disabled',
|
||||||
tabClass === '' || tabClass === 'tab_bts'
|
tabClass === '' || tabClass === 'tab_bts'
|
||||||
);
|
);
|
||||||
|
@ -780,12 +1210,6 @@ var netFilteringManager = (function() {
|
||||||
var targetPageDomain;
|
var targetPageDomain;
|
||||||
var targetFrameDomain;
|
var targetFrameDomain;
|
||||||
|
|
||||||
var removeAllChildren = function(node) {
|
|
||||||
while ( node.firstChild ) {
|
|
||||||
node.removeChild(node.firstChild);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var uglyTypeFromSelector = function(pane) {
|
var uglyTypeFromSelector = function(pane) {
|
||||||
var prettyType = selectValue('select.type.' + pane);
|
var prettyType = selectValue('select.type.' + pane);
|
||||||
if ( pane === 'static' ) {
|
if ( pane === 'static' ) {
|
||||||
|
@ -1495,10 +1919,10 @@ var rowFilterer = (function() {
|
||||||
var filterAll = function() {
|
var filterAll = function() {
|
||||||
// Special case: no filter
|
// Special case: no filter
|
||||||
if ( filters.length === 0 ) {
|
if ( filters.length === 0 ) {
|
||||||
uDom('#content tr').removeClass('f');
|
uDom('#events tr').removeClass('f');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var tbody = document.querySelector('#content tbody');
|
var tbody = document.querySelector('#events tbody');
|
||||||
var rows = tbody.rows;
|
var rows = tbody.rows;
|
||||||
var i = rows.length;
|
var i = rows.length;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
|
@ -1522,8 +1946,7 @@ var rowFilterer = (function() {
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var onFilterButton = function() {
|
var onFilterButton = function() {
|
||||||
var cl = document.body.classList;
|
uDom.nodeFromId('events').classList.toggle('f');
|
||||||
cl.toggle('f', cl.contains('f') === false);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
uDom('#filterButton').on('click', onFilterButton);
|
uDom('#filterButton').on('click', onFilterButton);
|
||||||
|
@ -1554,7 +1977,7 @@ var toJunkyard = function(trs) {
|
||||||
|
|
||||||
var clearBuffer = function() {
|
var clearBuffer = function() {
|
||||||
var tabId = uDom.nodeFromId('pageSelector').value || null;
|
var tabId = uDom.nodeFromId('pageSelector').value || null;
|
||||||
var tbody = document.querySelector('#content tbody');
|
var tbody = document.querySelector('#events tbody');
|
||||||
var tr = tbody.lastElementChild;
|
var tr = tbody.lastElementChild;
|
||||||
var trPrevious;
|
var trPrevious;
|
||||||
while ( tr !== null ) {
|
while ( tr !== null ) {
|
||||||
|
@ -1577,7 +2000,7 @@ var clearBuffer = function() {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var cleanBuffer = function() {
|
var cleanBuffer = function() {
|
||||||
var rows = uDom('#content tr.tab:not(.canMtx)').remove();
|
var rows = uDom('#events tr.tab:not(.canMtx)').remove();
|
||||||
var i = rows.length;
|
var i = rows.length;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
trJunkyard.push(rows.nodeAt(i));
|
trJunkyard.push(rows.nodeAt(i));
|
||||||
|
@ -1588,10 +2011,7 @@ var cleanBuffer = function() {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var toggleCompactView = function() {
|
var toggleCompactView = function() {
|
||||||
document.body.classList.toggle(
|
uDom.nodeFromId('events').classList.toggle('compactView');
|
||||||
'compactView',
|
|
||||||
document.body.classList.contains('compactView') === false
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -1604,7 +2024,7 @@ var popupManager = (function() {
|
||||||
var popupObserver = null;
|
var popupObserver = null;
|
||||||
var style = null;
|
var style = null;
|
||||||
var styleTemplate = [
|
var styleTemplate = [
|
||||||
'#content tr:not(.tab_{{tabId}}) {',
|
'#events tr:not(.tab_{{tabId}}) {',
|
||||||
'cursor: not-allowed;',
|
'cursor: not-allowed;',
|
||||||
'opacity: 0.2;',
|
'opacity: 0.2;',
|
||||||
'}'
|
'}'
|
||||||
|
@ -1659,11 +2079,15 @@ var popupManager = (function() {
|
||||||
style = uDom.nodeFromId('popupFilterer');
|
style = uDom.nodeFromId('popupFilterer');
|
||||||
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
|
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
|
||||||
|
|
||||||
document.body.classList.add('popupOn');
|
var parent = uDom.nodeFromId('events');
|
||||||
|
var rect = parent.getBoundingClientRect();
|
||||||
|
container.style.setProperty('top', rect.top + 'px');
|
||||||
|
container.style.setProperty('right', (rect.right - parent.clientWidth) + 'px');
|
||||||
|
parent.classList.add('popupOn');
|
||||||
};
|
};
|
||||||
|
|
||||||
var toggleOff = function() {
|
var toggleOff = function() {
|
||||||
document.body.classList.remove('popupOn');
|
uDom.nodeFromId('events').classList.remove('popupOn');
|
||||||
|
|
||||||
container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
|
container.querySelector('div > span:nth-of-type(1)').removeEventListener('click', toggleSize);
|
||||||
container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
|
container.querySelector('div > span:nth-of-type(2)').removeEventListener('click', toggleOff);
|
||||||
|
@ -1714,9 +2138,9 @@ uDom.onLoad(function() {
|
||||||
uDom('#clean').on('click', cleanBuffer);
|
uDom('#clean').on('click', cleanBuffer);
|
||||||
uDom('#clear').on('click', clearBuffer);
|
uDom('#clear').on('click', clearBuffer);
|
||||||
uDom('#maxEntries').on('change', onMaxEntriesChanged);
|
uDom('#maxEntries').on('change', onMaxEntriesChanged);
|
||||||
uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
|
uDom('#events table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
|
||||||
uDom('#content').on('click', 'tr.cat_net > td:nth-of-type(4)', netFilteringManager.toggleOn);
|
uDom('#events').on('click', 'tr.canLookup > td:nth-of-type(3)', reverseLookupManager.toggleOn);
|
||||||
uDom('#content').on('click', 'tr.canLookup > td:nth-of-type(3)', reverseLookupManager.toggleOn);
|
uDom('#events').on('click', 'tr.cat_net > td:nth-of-type(4)', netFilteringManager.toggleOn);
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/404
|
// https://github.com/gorhill/uBlock/issues/404
|
||||||
// Ensure page state is in sync with the state of its various widgets.
|
// Ensure page state is in sync with the state of its various widgets.
|
||||||
|
|
|
@ -62,10 +62,6 @@ var onMessage = function(request, sender, callback) {
|
||||||
µb.assets.get(request.url, callback);
|
µb.assets.get(request.url, callback);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 'reloadAllFilters':
|
|
||||||
µb.reloadAllFilters(callback);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 'listsFromNetFilter':
|
case 'listsFromNetFilter':
|
||||||
µb.staticFilteringReverseLookup.fromNetFilter(
|
µb.staticFilteringReverseLookup.fromNetFilter(
|
||||||
request.compiledFilter,
|
request.compiledFilter,
|
||||||
|
@ -82,6 +78,14 @@ var onMessage = function(request, sender, callback) {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 'reloadAllFilters':
|
||||||
|
µb.reloadAllFilters(callback);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 'scriptlet':
|
||||||
|
µb.scriptlets.inject(request.tabId, request.scriptlet, callback);
|
||||||
|
return;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -137,10 +141,6 @@ var onMessage = function(request, sender, callback) {
|
||||||
vAPI.tabs.open(request.details);
|
vAPI.tabs.open(request.details);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'scriptletGotoImageURL':
|
|
||||||
µb.scriptletGotoImageURL(request);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'reloadTab':
|
case 'reloadTab':
|
||||||
if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) {
|
if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) {
|
||||||
vAPI.tabs.reload(request.tabId);
|
vAPI.tabs.reload(request.tabId);
|
||||||
|
@ -150,10 +150,18 @@ var onMessage = function(request, sender, callback) {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'scriptletResponse':
|
||||||
|
µb.scriptlets.report(tabId, request.scriptlet, request.response);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'selectFilterLists':
|
case 'selectFilterLists':
|
||||||
µb.selectFilterLists(request.switches);
|
µb.selectFilterLists(request.switches);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'sendMessageTo':
|
||||||
|
vAPI.messaging.send(request.tabId, request.channelName, request.msg);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'toggleHostnameSwitch':
|
case 'toggleHostnameSwitch':
|
||||||
µb.toggleHostnameSwitch(request);
|
µb.toggleHostnameSwitch(request);
|
||||||
break;
|
break;
|
||||||
|
@ -350,7 +358,7 @@ var getPopupDataLazy = function(tabId, callback) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
µb.surveyCosmeticFilters(tabId, function() {
|
µb.scriptlets.inject(tabId, 'cosmetic-survey', function() {
|
||||||
r.hiddenElementCount = pageStore.hiddenElementCount;
|
r.hiddenElementCount = pageStore.hiddenElementCount;
|
||||||
callback(r);
|
callback(r);
|
||||||
});
|
});
|
||||||
|
@ -1373,6 +1381,8 @@ var logCosmeticFilters = function(tabId, details) {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onMessage = function(request, sender, callback) {
|
var onMessage = function(request, sender, callback) {
|
||||||
|
var tabId = sender && sender.tab ? sender.tab.id : 0;
|
||||||
|
|
||||||
// Async
|
// Async
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
default:
|
default:
|
||||||
|
@ -1381,13 +1391,8 @@ var onMessage = function(request, sender, callback) {
|
||||||
|
|
||||||
// Sync
|
// Sync
|
||||||
var response;
|
var response;
|
||||||
var tabId = sender && sender.tab ? sender.tab.id : 0;
|
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'gotoImageURL':
|
|
||||||
response = µb.scriptlets.gotoImageURL;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'liveCosmeticFilteringData':
|
case 'liveCosmeticFilteringData':
|
||||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||||
if ( pageStore ) {
|
if ( pageStore ) {
|
||||||
|
@ -1412,4 +1417,40 @@ vAPI.messaging.listen('scriptlets', onMessage);
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// devtools
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var onMessage = function(request, sender, callback) {
|
||||||
|
// Async
|
||||||
|
switch ( request.what ) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync
|
||||||
|
var response;
|
||||||
|
|
||||||
|
switch ( request.what ) {
|
||||||
|
default:
|
||||||
|
return vAPI.messaging.UNHANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.messaging.listen('devtools', onMessage);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -316,11 +316,13 @@ PageStore.prototype.init = function(tabId) {
|
||||||
'cosmetic-filtering'
|
'cosmetic-filtering'
|
||||||
);
|
);
|
||||||
if ( this.skipCosmeticFiltering && µb.logger.isEnabled() ) {
|
if ( this.skipCosmeticFiltering && µb.logger.isEnabled() ) {
|
||||||
|
// https://github.com/gorhill/uBlock/issues/370
|
||||||
|
// Log using `cosmetic-filtering`, not `elemhide`.
|
||||||
µb.logger.writeOne(
|
µb.logger.writeOne(
|
||||||
tabId,
|
tabId,
|
||||||
'net',
|
'net',
|
||||||
µb.staticNetFilteringEngine.toResultString(true),
|
µb.staticNetFilteringEngine.toResultString(true),
|
||||||
'elemhide',
|
'cosmetic-filtering',
|
||||||
tabContext.rawURL,
|
tabContext.rawURL,
|
||||||
this.tabHostname,
|
this.tabHostname,
|
||||||
this.tabHostname
|
this.tabHostname
|
||||||
|
|
|
@ -31,13 +31,11 @@
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/464
|
// https://github.com/gorhill/uBlock/issues/464
|
||||||
if ( document instanceof HTMLDocument === false ) {
|
if ( document instanceof HTMLDocument === false ) {
|
||||||
//console.debug('cosmetic-logger.js > not a HTLMDocument');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can happen
|
// This can happen
|
||||||
if ( typeof vAPI !== 'object' ) {
|
if ( typeof vAPI !== 'object' ) {
|
||||||
//console.debug('cosmetic-logger.js > vAPI not found');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +86,7 @@ localMessager.send({
|
||||||
matchedSelectors: matchedSelectors
|
matchedSelectors: matchedSelectors
|
||||||
}, function() {
|
}, function() {
|
||||||
localMessager.close();
|
localMessager.close();
|
||||||
|
localMessager = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -36,7 +36,7 @@ if ( document instanceof HTMLDocument === false ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can happen
|
// This can happen
|
||||||
if ( !vAPI ) {
|
if ( typeof vAPI !== 'object' ) {
|
||||||
//console.debug('cosmetic-off.js > no vAPI');
|
//console.debug('cosmetic-off.js > no vAPI');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ if ( document instanceof HTMLDocument === false ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This can happen
|
// This can happen
|
||||||
if ( !vAPI ) {
|
if ( typeof vAPI !== 'object' ) {
|
||||||
//console.debug('cosmetic-on.js > no vAPI');
|
//console.debug('cosmetic-on.js > no vAPI');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2015 Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global vAPI, HTMLDocument */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/464
|
||||||
|
if ( document instanceof HTMLDocument === false ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can happen
|
||||||
|
if ( typeof vAPI !== 'object' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Some kind of fingerprint for the DOM, without incurring too much overhead.
|
||||||
|
|
||||||
|
var url = window.location.href;
|
||||||
|
var pos = url.indexOf('#');
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
url = url.slice(0, pos);
|
||||||
|
}
|
||||||
|
var fingerprint = url + '{' + document.getElementsByTagName('*').length.toString() + '}';
|
||||||
|
|
||||||
|
var localMessager = vAPI.messaging.channel('scriptlets');
|
||||||
|
localMessager.send({
|
||||||
|
what: 'scriptletResponse',
|
||||||
|
scriptlet: 'dom-fingerprint',
|
||||||
|
response: fingerprint
|
||||||
|
}, function() {
|
||||||
|
localMessager.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
|
@ -0,0 +1,339 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2015 Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global vAPI, HTMLDocument */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/464
|
||||||
|
if ( document instanceof HTMLDocument === false ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can happen
|
||||||
|
if ( typeof vAPI !== 'object' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if ( document.querySelector('iframe.dom-highlight.' + vAPI.sessionId) !== null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var localMessager = null;
|
||||||
|
var svgOcean = null;
|
||||||
|
var svgIslands = null;
|
||||||
|
var svgRoot = null;
|
||||||
|
var pickerRoot = null;
|
||||||
|
var currentSelector = '';
|
||||||
|
|
||||||
|
var toggledNodes = new Map();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var highlightElements = function(elems, scrollTo) {
|
||||||
|
var wv = pickerRoot.contentWindow.innerWidth;
|
||||||
|
var hv = pickerRoot.contentWindow.innerHeight;
|
||||||
|
var ocean = ['M0 0h' + wv + 'v' + hv + 'h-' + wv, 'z'];
|
||||||
|
var islands = [];
|
||||||
|
var elem, rect, poly;
|
||||||
|
var xl, xr, yt, yb, w, h, ws;
|
||||||
|
var xlu = Number.MAX_VALUE, xru = 0, ytu = Number.MAX_VALUE, ybu = 0;
|
||||||
|
|
||||||
|
for ( var i = 0; i < elems.length; i++ ) {
|
||||||
|
elem = elems[i];
|
||||||
|
if ( elem === pickerRoot ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( typeof elem.getBoundingClientRect !== 'function' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect = elem.getBoundingClientRect();
|
||||||
|
xl = rect.left;
|
||||||
|
xr = rect.right;
|
||||||
|
w = rect.width;
|
||||||
|
yt = rect.top;
|
||||||
|
yb = rect.bottom;
|
||||||
|
h = rect.height;
|
||||||
|
|
||||||
|
ws = w.toFixed(1);
|
||||||
|
poly = 'M' + xl.toFixed(1) + ' ' + yt.toFixed(1) +
|
||||||
|
'h' + ws +
|
||||||
|
'v' + h.toFixed(1) +
|
||||||
|
'h-' + ws +
|
||||||
|
'z';
|
||||||
|
ocean.push(poly);
|
||||||
|
islands.push(poly);
|
||||||
|
|
||||||
|
if ( !scrollTo ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( xl < xlu ) { xlu = xl; }
|
||||||
|
if ( xr > xru ) { xru = xr; }
|
||||||
|
if ( yt < ytu ) { ytu = yt; }
|
||||||
|
if ( yb > ybu ) { ybu = yb; }
|
||||||
|
}
|
||||||
|
svgOcean.setAttribute('d', ocean.join(''));
|
||||||
|
svgIslands.setAttribute('d', islands.join('') || 'M0 0');
|
||||||
|
|
||||||
|
if ( !scrollTo ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highlighted area completely within viewport
|
||||||
|
if ( xlu >= 0 && xru <= wv && ytu >= 0 && ybu <= hv ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dx = 0, dy = 0;
|
||||||
|
|
||||||
|
if ( xru > wv ) {
|
||||||
|
dx = xru - wv;
|
||||||
|
xlu -= dx;
|
||||||
|
}
|
||||||
|
if ( xlu < 0 ) {
|
||||||
|
dx += xlu;
|
||||||
|
}
|
||||||
|
if ( ybu > hv ) {
|
||||||
|
dy = ybu - hv;
|
||||||
|
ytu -= dy;
|
||||||
|
}
|
||||||
|
if ( ytu < 0 ) {
|
||||||
|
dy += ytu;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( dx !== 0 || dy !== 0 ) {
|
||||||
|
window.scrollBy(dx, dy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var elementsFromSelector = function(filter) {
|
||||||
|
var out = [];
|
||||||
|
try {
|
||||||
|
out = document.querySelectorAll(filter);
|
||||||
|
} catch (ex) {
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var highlight = function(scrollTo) {
|
||||||
|
var elements = elementsFromSelector(currentSelector);
|
||||||
|
highlightElements(elements, scrollTo);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var onScrolled = function() {
|
||||||
|
highlight();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// original, target = what to do
|
||||||
|
// any, any = restore saved display property
|
||||||
|
// any, hidden = set display to `none`, remember original state
|
||||||
|
// hidden, any = remove display property, don't remember original state
|
||||||
|
// hidden, hidden = set display to `none`
|
||||||
|
|
||||||
|
var toggleNodes = function(selector, originalState, targetState) {
|
||||||
|
var nodes = document.querySelectorAll(selector);
|
||||||
|
var i = nodes.length;
|
||||||
|
if ( i === 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var node, value;
|
||||||
|
while ( i-- ) {
|
||||||
|
node = nodes[i];
|
||||||
|
if ( originalState ) { // any, ?
|
||||||
|
if ( targetState ) { // any, any
|
||||||
|
value = toggledNodes.get(node);
|
||||||
|
if ( value === undefined ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( value !== null ) {
|
||||||
|
node.style.removeProperty('display');
|
||||||
|
} else {
|
||||||
|
node.style.setProperty('display', value);
|
||||||
|
}
|
||||||
|
toggledNodes.delete(node);
|
||||||
|
} else { // any, hidden
|
||||||
|
toggledNodes.set(node, node.style.getPropertyValue('display') || null);
|
||||||
|
node.style.setProperty('display', 'none');
|
||||||
|
}
|
||||||
|
} else { // hidden, ?
|
||||||
|
if ( targetState ) { // hidden, any
|
||||||
|
node.style.setProperty('display', 'initial', 'important');
|
||||||
|
} else { // hidden, hidden
|
||||||
|
node.style.setProperty('display', 'none', 'important');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var resetToggledNodes = function() {
|
||||||
|
// Chromium does not support destructuring as of v43.
|
||||||
|
for ( var node of toggledNodes.keys() ) {
|
||||||
|
value = toggledNodes.get(node);
|
||||||
|
if ( value !== null ) {
|
||||||
|
node.style.removeProperty('display');
|
||||||
|
} else {
|
||||||
|
node.style.setProperty('display', value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toggledNodes.clear();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var shutdown = function() {
|
||||||
|
resetToggledNodes();
|
||||||
|
localMessager.removeListener(onMessage);
|
||||||
|
localMessager.close();
|
||||||
|
localMessager = null;
|
||||||
|
window.removeEventListener('scroll', onScrolled, true);
|
||||||
|
document.documentElement.removeChild(pickerRoot);
|
||||||
|
pickerRoot = svgRoot = svgOcean = svgIslands = null;
|
||||||
|
currentSelector = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var onMessage = function(msg) {
|
||||||
|
if ( msg.what !== 'dom-highlight' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch ( msg.action ) {
|
||||||
|
case 'highlight':
|
||||||
|
currentSelector = msg.selector;
|
||||||
|
highlight(msg.scrollTo);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'toggleNodes':
|
||||||
|
toggleNodes(msg.selector, msg.original, msg.target);
|
||||||
|
currentSelector = msg.selector;
|
||||||
|
highlight(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'shutdown':
|
||||||
|
shutdown();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
pickerRoot = document.createElement('iframe');
|
||||||
|
pickerRoot.classList.add(vAPI.sessionId);
|
||||||
|
pickerRoot.classList.add('dom-highlight');
|
||||||
|
pickerRoot.style.cssText = [
|
||||||
|
'background: transparent',
|
||||||
|
'border: 0',
|
||||||
|
'border-radius: 0',
|
||||||
|
'box-shadow: none',
|
||||||
|
'display: block',
|
||||||
|
'height: 100%',
|
||||||
|
'left: 0',
|
||||||
|
'margin: 0',
|
||||||
|
'opacity: 1',
|
||||||
|
'position: fixed',
|
||||||
|
'outline: 0',
|
||||||
|
'padding: 0',
|
||||||
|
'top: 0',
|
||||||
|
'visibility: visible',
|
||||||
|
'width: 100%',
|
||||||
|
'z-index: 2147483647',
|
||||||
|
''
|
||||||
|
].join(' !important;\n');
|
||||||
|
|
||||||
|
pickerRoot.onload = function() {
|
||||||
|
pickerRoot.onload = null;
|
||||||
|
var pickerDoc = this.contentDocument;
|
||||||
|
|
||||||
|
var style = pickerDoc.createElement('style');
|
||||||
|
style.textContent = [
|
||||||
|
'body {',
|
||||||
|
'background-color: transparent;',
|
||||||
|
'cursor: crosshair;',
|
||||||
|
'}',
|
||||||
|
'svg {',
|
||||||
|
'height: 100%;',
|
||||||
|
'left: 0;',
|
||||||
|
'position: fixed;',
|
||||||
|
'top: 0;',
|
||||||
|
'width: 100%;',
|
||||||
|
'}',
|
||||||
|
'svg > path:first-child {',
|
||||||
|
'fill: rgba(0,0,0,0.75);',
|
||||||
|
'fill-rule: evenodd;',
|
||||||
|
'}',
|
||||||
|
'svg > path + path {',
|
||||||
|
'fill: rgba(0,0,255,0.1);',
|
||||||
|
'stroke: #FFF;',
|
||||||
|
'stroke-width: 0.5px;',
|
||||||
|
'}',
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
|
pickerDoc.body.appendChild(style);
|
||||||
|
|
||||||
|
svgRoot = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
||||||
|
svgOcean = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||||
|
svgRoot.appendChild(svgOcean);
|
||||||
|
svgIslands = pickerDoc.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||||
|
svgRoot.appendChild(svgIslands);
|
||||||
|
pickerDoc.body.appendChild(svgRoot);
|
||||||
|
|
||||||
|
window.addEventListener('scroll', onScrolled, true);
|
||||||
|
|
||||||
|
localMessager = vAPI.messaging.channel('scriptlets');
|
||||||
|
localMessager.addListener(onMessage);
|
||||||
|
|
||||||
|
highlight();
|
||||||
|
};
|
||||||
|
|
||||||
|
document.documentElement.appendChild(pickerRoot);
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
|
@ -0,0 +1,406 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2015 Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* global vAPI, HTMLDocument */
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
/*! http://mths.be/cssescape v0.2.1 by @mathias | MIT license */
|
||||||
|
;(function(root) {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (!root.CSS) {
|
||||||
|
root.CSS = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
var CSS = root.CSS;
|
||||||
|
|
||||||
|
var InvalidCharacterError = function(message) {
|
||||||
|
this.message = message;
|
||||||
|
};
|
||||||
|
InvalidCharacterError.prototype = new Error();
|
||||||
|
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
|
||||||
|
|
||||||
|
if (!CSS.escape) {
|
||||||
|
// http://dev.w3.org/csswg/cssom/#serialize-an-identifier
|
||||||
|
CSS.escape = function(value) {
|
||||||
|
var string = String(value);
|
||||||
|
var length = string.length;
|
||||||
|
var index = -1;
|
||||||
|
var codeUnit;
|
||||||
|
var result = '';
|
||||||
|
var firstCodeUnit = string.charCodeAt(0);
|
||||||
|
while (++index < length) {
|
||||||
|
codeUnit = string.charCodeAt(index);
|
||||||
|
// Note: there’s no need to special-case astral symbols, surrogate
|
||||||
|
// pairs, or lone surrogates.
|
||||||
|
|
||||||
|
// If the character is NULL (U+0000), then throw an
|
||||||
|
// `InvalidCharacterError` exception and terminate these steps.
|
||||||
|
if (codeUnit === 0x0000) {
|
||||||
|
throw new InvalidCharacterError(
|
||||||
|
'Invalid character: the input contains U+0000.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
|
||||||
|
// U+007F, […]
|
||||||
|
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit == 0x007F ||
|
||||||
|
// If the character is the first character and is in the range [0-9]
|
||||||
|
// (U+0030 to U+0039), […]
|
||||||
|
(index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
|
||||||
|
// If the character is the second character and is in the range [0-9]
|
||||||
|
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
|
||||||
|
(
|
||||||
|
index == 1 &&
|
||||||
|
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
|
||||||
|
firstCodeUnit == 0x002D
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
|
||||||
|
result += '\\' + codeUnit.toString(16) + ' ';
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the character is not handled by one of the above rules and is
|
||||||
|
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
|
||||||
|
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
|
||||||
|
// U+005A), or [a-z] (U+0061 to U+007A), […]
|
||||||
|
if (
|
||||||
|
codeUnit >= 0x0080 ||
|
||||||
|
codeUnit == 0x002D ||
|
||||||
|
codeUnit == 0x005F ||
|
||||||
|
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
|
||||||
|
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
|
||||||
|
codeUnit >= 0x0061 && codeUnit <= 0x007A
|
||||||
|
) {
|
||||||
|
// the character itself
|
||||||
|
result += string.charAt(index);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, the escaped character.
|
||||||
|
// http://dev.w3.org/csswg/cssom/#escape-a-character
|
||||||
|
result += '\\' + string.charAt(index);
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}(self));
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/464
|
||||||
|
if ( document instanceof HTMLDocument === false ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This can happen
|
||||||
|
if ( typeof vAPI !== 'object' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var skipTagNames = {
|
||||||
|
'br': true,
|
||||||
|
'link': true,
|
||||||
|
'meta': true,
|
||||||
|
'script': true,
|
||||||
|
'style': true
|
||||||
|
};
|
||||||
|
|
||||||
|
var resourceAttrNames = {
|
||||||
|
'a': 'href',
|
||||||
|
'iframe': 'src',
|
||||||
|
'img': 'src',
|
||||||
|
'object': 'data'
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Collect all nodes which are directly affected by cosmetic filters: these
|
||||||
|
// will be reported in the layout data.
|
||||||
|
|
||||||
|
var nodeToCosmeticFilterMap = (function() {
|
||||||
|
var out = new WeakMap();
|
||||||
|
var styleTags = vAPI.styles || [];
|
||||||
|
var i = styleTags.length;
|
||||||
|
var selectors, styleText, j, selector, nodes, k;
|
||||||
|
while ( i-- ) {
|
||||||
|
styleText = styleTags[i].textContent;
|
||||||
|
selectors = styleText.slice(0, styleText.lastIndexOf('\n')).split(/,\n/);
|
||||||
|
j = selectors.length;
|
||||||
|
while ( j-- ) {
|
||||||
|
selector = selectors[j];
|
||||||
|
nodes = document.querySelectorAll(selector);
|
||||||
|
k = nodes.length;
|
||||||
|
while ( k-- ) {
|
||||||
|
out.set(nodes[k], selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var DomRoot = function() {
|
||||||
|
this.lvl = 0;
|
||||||
|
this.sel = 'body';
|
||||||
|
var url = window.location.href;
|
||||||
|
var pos = url.indexOf('#');
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
url = url.slice(0, pos);
|
||||||
|
}
|
||||||
|
this.src = url;
|
||||||
|
this.top = window === window.top;
|
||||||
|
this.cnt = 0;
|
||||||
|
this.fp = fingerprint();
|
||||||
|
};
|
||||||
|
|
||||||
|
var DomNode = function(level, selector, filter) {
|
||||||
|
this.lvl = level;
|
||||||
|
this.sel = selector;
|
||||||
|
this.cnt = 0;
|
||||||
|
this.filter = filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var hasManyMatches = function(node, selector) {
|
||||||
|
var fnName = matchesSelector;
|
||||||
|
if ( fnName === '' ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
var child = node.firstElementChild;
|
||||||
|
var match = false;
|
||||||
|
while ( child !== null ) {
|
||||||
|
if ( child[fnName](selector) ) {
|
||||||
|
if ( match ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
match = true;
|
||||||
|
}
|
||||||
|
child = child.nextElementSibling;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
var matchesSelector = (function() {
|
||||||
|
if ( typeof Element.prototype.matches === 'function' ) {
|
||||||
|
return 'matches';
|
||||||
|
}
|
||||||
|
if ( typeof Element.prototype.mozMatchesSelector === 'function' ) {
|
||||||
|
return 'mozMatchesSelector';
|
||||||
|
}
|
||||||
|
if ( typeof Element.prototype.webkitMatchesSelector === 'function' ) {
|
||||||
|
return 'webkitMatchesSelector';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var selectorFromNode = function(node) {
|
||||||
|
var str, attr, pos, sw, i;
|
||||||
|
var tag = node.localName;
|
||||||
|
var selector = CSS.escape(tag);
|
||||||
|
// Id
|
||||||
|
if ( typeof node.id === 'string' ) {
|
||||||
|
str = node.id.trim();
|
||||||
|
if ( str !== '' ) {
|
||||||
|
selector += '#' + CSS.escape(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Class
|
||||||
|
var cl = node.classList;
|
||||||
|
if ( cl ) {
|
||||||
|
for ( i = 0; i < cl.length; i++ ) {
|
||||||
|
selector += '.' + CSS.escape(cl[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Tag-specific attributes
|
||||||
|
if ( resourceAttrNames.hasOwnProperty(tag) ) {
|
||||||
|
attr = resourceAttrNames[tag];
|
||||||
|
str = node.getAttribute(attr) || '';
|
||||||
|
str = str.trim();
|
||||||
|
pos = str.indexOf('#');
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
str = str.slice(0, pos);
|
||||||
|
sw = '^';
|
||||||
|
} else {
|
||||||
|
sw = '';
|
||||||
|
}
|
||||||
|
if ( str !== '' ) {
|
||||||
|
selector += '[' + attr + sw + '="' + CSS.escape(str) + '"]';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The resulting selector must cause only one element to be selected. If
|
||||||
|
// it's not the case, further narrow using `nth-of-type` pseudo-class.
|
||||||
|
if ( hasManyMatches(node.parentElement, selector) ) {
|
||||||
|
i = 1;
|
||||||
|
while ( node.previousElementSibling ) {
|
||||||
|
node = node.previousElementSibling;
|
||||||
|
if ( node.localName === tag ) {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
selector += ':nth-of-type(' + i + ')';
|
||||||
|
}
|
||||||
|
return selector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var domNodeFactory = function(level, node) {
|
||||||
|
var localName = node.localName;
|
||||||
|
if ( skipTagNames.hasOwnProperty(localName) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// skip uBlock's own nodes
|
||||||
|
if ( node.classList.contains(vAPI.sessionId) ) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if ( level === 0 && localName === 'body' ) {
|
||||||
|
return new DomRoot();
|
||||||
|
}
|
||||||
|
var selector = selectorFromNode(node);
|
||||||
|
var filter = nodeToCosmeticFilterMap.get(node);
|
||||||
|
return new DomNode(level, selector, filter);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Some kind of fingerprint for the DOM, without incurring too much
|
||||||
|
// overhead.
|
||||||
|
|
||||||
|
var fingerprint = function() {
|
||||||
|
var url = window.location.href;
|
||||||
|
var pos = url.indexOf('#');
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
url = url.slice(0, pos);
|
||||||
|
}
|
||||||
|
return url + '{' + document.getElementsByTagName('*').length.toString() + '}';
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Collect layout data.
|
||||||
|
|
||||||
|
var domLayout = [];
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var dom = domLayout;
|
||||||
|
var stack = [];
|
||||||
|
var node = document.body;
|
||||||
|
var domNode;
|
||||||
|
var lvl = 0;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
domNode = domNodeFactory(lvl, node);
|
||||||
|
if ( domNode !== null ) {
|
||||||
|
dom.push(domNode);
|
||||||
|
}
|
||||||
|
// children
|
||||||
|
if ( node.firstElementChild !== null ) {
|
||||||
|
stack.push(node);
|
||||||
|
lvl += 1;
|
||||||
|
node = node.firstElementChild;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// sibling
|
||||||
|
if ( node.nextElementSibling === null ) {
|
||||||
|
do {
|
||||||
|
node = stack.pop();
|
||||||
|
if ( !node ) { break; }
|
||||||
|
lvl -= 1;
|
||||||
|
} while ( node.nextElementSibling === null );
|
||||||
|
if ( !node ) { break; }
|
||||||
|
}
|
||||||
|
node = node.nextElementSibling;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Descendant count for each node.
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var dom = domLayout;
|
||||||
|
var stack = [], ptr;
|
||||||
|
var lvl = 0;
|
||||||
|
var domNode, cnt;
|
||||||
|
var i = dom.length;
|
||||||
|
|
||||||
|
while ( i-- ) {
|
||||||
|
domNode = dom[i];
|
||||||
|
if ( domNode.lvl === lvl ) {
|
||||||
|
stack[ptr] += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ( domNode.lvl > lvl ) {
|
||||||
|
while ( lvl < domNode.lvl ) {
|
||||||
|
stack.push(0);
|
||||||
|
lvl += 1;
|
||||||
|
}
|
||||||
|
ptr = lvl - 1;
|
||||||
|
stack[ptr] += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// domNode.lvl < lvl
|
||||||
|
cnt = stack.pop();
|
||||||
|
domNode.cnt = cnt;
|
||||||
|
lvl -= 1;
|
||||||
|
ptr = lvl - 1;
|
||||||
|
stack[ptr] += cnt + 1;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var localMessager = vAPI.messaging.channel('scriptlets');
|
||||||
|
localMessager.send({
|
||||||
|
what: 'scriptletResponse',
|
||||||
|
scriptlet: 'dom-layout',
|
||||||
|
response: domLayout
|
||||||
|
}, function() {
|
||||||
|
localMessager.close();
|
||||||
|
localMessager = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
|
@ -296,7 +296,7 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.epickerTarget = targetElement || '';
|
this.epickerTarget = targetElement || '';
|
||||||
vAPI.tabs.injectScript(tabId, { file: 'js/scriptlets/element-picker.js' });
|
this.scriptlets.inject(tabId, 'element-picker');
|
||||||
if ( typeof vAPI.tabs.select === 'function' ) {
|
if ( typeof vAPI.tabs.select === 'function' ) {
|
||||||
vAPI.tabs.select(tabId);
|
vAPI.tabs.select(tabId);
|
||||||
}
|
}
|
||||||
|
@ -378,10 +378,10 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||||
|
|
||||||
// Take action if needed
|
// Take action if needed
|
||||||
if ( details.name === 'no-cosmetic-filtering' ) {
|
if ( details.name === 'no-cosmetic-filtering' ) {
|
||||||
vAPI.tabs.injectScript(details.tabId, {
|
this.scriptlets.injectDeep(
|
||||||
file: 'js/scriptlets/cosmetic-' + (details.state ? 'off' : 'on') + '.js',
|
details.tabId,
|
||||||
allFrames: true
|
details.state ? 'cosmetic-off' : 'cosmetic-on'
|
||||||
});
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,23 +391,12 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.surveyCosmeticFilters = function(tabId, callback) {
|
|
||||||
callback = callback || this.noopFunc;
|
|
||||||
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
|
||||||
callback();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
vAPI.tabs.injectScript(tabId, { file: 'js/scriptlets/cosmetic-survey.js' }, callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µBlock.logCosmeticFilters = (function() {
|
µBlock.logCosmeticFilters = (function() {
|
||||||
var tabIdToTimerMap = {};
|
var tabIdToTimerMap = {};
|
||||||
|
|
||||||
var injectNow = function(tabId) {
|
var injectNow = function(tabId) {
|
||||||
delete tabIdToTimerMap[tabId];
|
delete tabIdToTimerMap[tabId];
|
||||||
vAPI.tabs.injectScript(tabId, { file: 'js/scriptlets/cosmetic-logger.js' });
|
µBlock.scriptlets.inject(tabId, 'cosmetic-logger');
|
||||||
};
|
};
|
||||||
|
|
||||||
var injectAsync = function(tabId) {
|
var injectAsync = function(tabId) {
|
||||||
|
@ -425,13 +414,67 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.scriptletGotoImageURL = function(details) {
|
µBlock.scriptlets = (function() {
|
||||||
if ( vAPI.isBehindTheSceneTabId(details.tabId) ) {
|
var pendingEntries = Object.create(null);
|
||||||
|
|
||||||
|
var Entry = function(tabId, scriptlet, callback) {
|
||||||
|
this.tabId = tabId;
|
||||||
|
this.scriptlet = scriptlet;
|
||||||
|
this.callback = callback;
|
||||||
|
this.timer = vAPI.setTimeout(this.service.bind(this), 1000);
|
||||||
|
};
|
||||||
|
|
||||||
|
Entry.prototype.service = function(response) {
|
||||||
|
if ( this.timer !== null ) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
}
|
||||||
|
delete pendingEntries[makeKey(this.tabId, this.scriptlet)];
|
||||||
|
this.callback(response);
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeKey = function(tabId, scriptlet) {
|
||||||
|
return tabId + ' ' + scriptlet;
|
||||||
|
};
|
||||||
|
|
||||||
|
var report = function(tabId, scriptlet, response) {
|
||||||
|
var key = makeKey(tabId, scriptlet);
|
||||||
|
var entry = pendingEntries[key];
|
||||||
|
if ( entry === undefined ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.scriptlets.gotoImageURL = details.url;
|
entry.service(response);
|
||||||
vAPI.tabs.injectScript(details.tabId, { file: 'js/scriptlets/goto-img.js' });
|
};
|
||||||
};
|
|
||||||
|
var inject = function(tabId, scriptlet, callback) {
|
||||||
|
if ( typeof callback === 'function' ) {
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var key = makeKey(tabId, scriptlet);
|
||||||
|
if ( pendingEntries[key] !== undefined ) {
|
||||||
|
callback();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pendingEntries[key] = new Entry(tabId, scriptlet, callback);
|
||||||
|
}
|
||||||
|
vAPI.tabs.injectScript(tabId, { file: 'js/scriptlets/' + scriptlet + '.js' });
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: think about a callback mechanism.
|
||||||
|
var injectDeep = function(tabId, scriptlet) {
|
||||||
|
vAPI.tabs.injectScript(tabId, {
|
||||||
|
file: 'js/scriptlets/' + scriptlet + '.js',
|
||||||
|
allFrames: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
inject: inject,
|
||||||
|
injectDeep: injectDeep,
|
||||||
|
report: report
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,24 @@
|
||||||
<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
|
<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
|
||||||
<title data-i18n="statsPageName"></title>
|
<title data-i18n="statsPageName"></title>
|
||||||
</head>
|
</head>
|
||||||
<body class="compactView f">
|
<body>
|
||||||
|
|
||||||
<div id="toolbar">
|
<div class="permatoolbar">
|
||||||
<div>
|
<div>
|
||||||
<select id="pageSelector">
|
<select id="pageSelector">
|
||||||
<option value="" data-i18n="logAll">
|
<option value="" data-i18n="logAll">
|
||||||
<option value="tab_bts" data-i18n="logBehindTheScene">
|
<option value="tab_bts" data-i18n="logBehindTheScene">
|
||||||
</select>
|
</select>
|
||||||
<span id="refresh" class="button disabled fa"></span>
|
<span id="refresh" class="button disabled fa needtab"></span>
|
||||||
|
<span id="showdom" class="button fa"></span>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="domInspector">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="events" class="compactView f">
|
||||||
|
<div class="permatoolbar">
|
||||||
<div>
|
<div>
|
||||||
<span id="compactViewToggler" class="button fa"></span>
|
<span id="compactViewToggler" class="button fa"></span>
|
||||||
<span id="clean" class="button fa disabled"></span>
|
<span id="clean" class="button fa disabled"></span>
|
||||||
|
@ -24,19 +32,16 @@
|
||||||
<input id="maxEntries" type="text" size="5" title="logMaxEntriesTip">
|
<input id="maxEntries" type="text" size="5" title="logMaxEntriesTip">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="content">
|
|
||||||
<style id="tabFilterer" scoped></style>
|
<style id="tabFilterer" scoped></style>
|
||||||
<style id="popupFilterer" scoped></style>
|
<style id="popupFilterer" scoped></style>
|
||||||
<table>
|
<table>
|
||||||
<colgroup><col><col><col><col><col></colgroup>
|
<colgroup><col><col><col><col><col></colgroup>
|
||||||
<tbody></tbody>
|
<tbody></tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
<div id="popupContainer">
|
||||||
|
|
||||||
<div id="popupContainer">
|
|
||||||
<div><span></span> <span></span></div>
|
<div><span></span> <span></span></div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="templates" style="display: none;">
|
<div id="templates" style="display: none;">
|
||||||
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
|
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
|
||||||
|
@ -82,6 +87,9 @@
|
||||||
<div id="filterFinderDialog" class="modalDialog">
|
<div id="filterFinderDialog" class="modalDialog">
|
||||||
<div class="dialog"></div>
|
<div class="dialog"></div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="cosmeticFilteringDialog" class="modalDialog">
|
||||||
|
<div class="dialog"></div>
|
||||||
|
</div>
|
||||||
<div id="filterFinderDialogSentence1"><span><span></span><code></code><span></span></span></div>
|
<div id="filterFinderDialogSentence1"><span><span></span><code></code><span></span></span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue