mirror of https://github.com/gorhill/uBlock.git
vAPI.sessionId, element-picker dialog as iframe
vAPI.sessionId - random ID generated every time when a page loads. Having the dialog in an iframe lowers the chance of interference with the styling of the page, also avoids using innerHTML (AMO complaint).
This commit is contained in:
parent
ff82b32853
commit
8693ab738d
|
@ -25,14 +25,13 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
(function() {
|
(function(self) {
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var vAPI = self.vAPI = self.vAPI || {};
|
var vAPI = self.vAPI = self.vAPI || {};
|
||||||
|
|
||||||
var chrome = self.chrome;
|
var chrome = self.chrome;
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/456
|
// https://github.com/gorhill/uBlock/issues/456
|
||||||
|
@ -40,8 +39,10 @@ var chrome = self.chrome;
|
||||||
if ( vAPI.vapiClientInjected ) {
|
if ( vAPI.vapiClientInjected ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
vAPI.vapiClientInjected = true;
|
|
||||||
|
|
||||||
|
vAPI.vapiClientInjected = true;
|
||||||
|
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
|
||||||
|
Math.random().toString(36).slice(2);
|
||||||
vAPI.chrome = true;
|
vAPI.chrome = true;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -85,21 +86,14 @@ var messagingConnector = function(response) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var uniqueId = function() {
|
|
||||||
return Math.random().toString(36).slice(2);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.messaging = {
|
vAPI.messaging = {
|
||||||
port: null,
|
port: null,
|
||||||
channels: {},
|
channels: {},
|
||||||
listeners: {},
|
listeners: {},
|
||||||
requestId: 1,
|
requestId: 1,
|
||||||
connectorId: uniqueId(),
|
|
||||||
|
|
||||||
setup: function() {
|
setup: function() {
|
||||||
this.port = chrome.runtime.connect({name: this.connectorId});
|
this.port = chrome.runtime.connect({name: vAPI.sessionId});
|
||||||
this.port.onMessage.addListener(messagingConnector);
|
this.port.onMessage.addListener(messagingConnector);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -150,6 +144,6 @@ vAPI.messaging = {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
})();
|
})(this);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -31,8 +31,10 @@
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
self.vAPI = self.vAPI || {};
|
var vAPI = self.vAPI = self.vAPI || {};
|
||||||
vAPI.firefox = true;
|
vAPI.firefox = true;
|
||||||
|
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
|
||||||
|
Math.random().toString(36).slice(2);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
// For non background pages
|
// For non background pages
|
||||||
|
|
||||||
(function() {
|
(function(self) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var vAPI = self.vAPI = self.vAPI || {};
|
var vAPI = self.vAPI = self.vAPI || {};
|
||||||
if(vAPI.vapiClientInjected) {
|
if(vAPI.vapiClientInjected) {
|
||||||
|
@ -29,7 +29,8 @@
|
||||||
}
|
}
|
||||||
vAPI.vapiClientInjected = true;
|
vAPI.vapiClientInjected = true;
|
||||||
vAPI.safari = true;
|
vAPI.safari = true;
|
||||||
|
vAPI.sessionId = String.fromCharCode(Date.now() % 25 + 97) +
|
||||||
|
Math.random().toString(36).slice(2);
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
var messagingConnector = function(response) {
|
var messagingConnector = function(response) {
|
||||||
if(!response) {
|
if(!response) {
|
||||||
|
@ -63,26 +64,21 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
var uniqueId = function() {
|
|
||||||
return Math.random().toString(36).slice(2);
|
|
||||||
};
|
|
||||||
/******************************************************************************/
|
|
||||||
// Relevant?
|
// Relevant?
|
||||||
// https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW12
|
// https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW12
|
||||||
vAPI.messaging = {
|
vAPI.messaging = {
|
||||||
channels: {},
|
channels: {},
|
||||||
listeners: {},
|
listeners: {},
|
||||||
requestId: 1,
|
requestId: 1,
|
||||||
connectorId: uniqueId(),
|
|
||||||
setup: function() {
|
setup: function() {
|
||||||
if(typeof safari === "undefined") {
|
if(typeof safari === "undefined") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.connector = function(msg) {
|
this.connector = function(msg) {
|
||||||
// messages from the background script are sent to every frame,
|
// messages from the background script are sent to every frame,
|
||||||
// so we need to check the connectorId to accept only
|
// so we need to check the vAPI.sessionId to accept only
|
||||||
// what is meant for the current context
|
// what is meant for the current context
|
||||||
if(msg.name === vAPI.messaging.connectorId || msg.name === 'broadcast') {
|
if(msg.name === vAPI.sessionId || msg.name === 'broadcast') {
|
||||||
messagingConnector(msg.message);
|
messagingConnector(msg.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -131,7 +127,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
safari.extension.globalPage.contentWindow.vAPI.messaging.onMessage({
|
safari.extension.globalPage.contentWindow.vAPI.messaging.onMessage({
|
||||||
name: vAPI.messaging.connectorId,
|
name: vAPI.sessionId,
|
||||||
message: message,
|
message: message,
|
||||||
target: {
|
target: {
|
||||||
page: {
|
page: {
|
||||||
|
@ -142,7 +138,7 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
safari.self.tab.dispatchMessage(vAPI.messaging.connectorId, message);
|
safari.self.tab.dispatchMessage(vAPI.sessionId, message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
close: function() {
|
close: function() {
|
||||||
|
@ -215,8 +211,7 @@
|
||||||
var firstMutation = function() {
|
var firstMutation = function() {
|
||||||
document.removeEventListener("DOMContentLoaded", firstMutation, true);
|
document.removeEventListener("DOMContentLoaded", firstMutation, true);
|
||||||
firstMutation = false;
|
firstMutation = false;
|
||||||
var randEventName = uniqueId();
|
document.addEventListener(vAPI.sessionId, function(e) {
|
||||||
document.addEventListener(randEventName, function(e) {
|
|
||||||
if(shouldBlockDetailedRequest(e.detail)) {
|
if(shouldBlockDetailedRequest(e.detail)) {
|
||||||
e.detail.url = false;
|
e.detail.url = false;
|
||||||
}
|
}
|
||||||
|
@ -225,7 +220,7 @@
|
||||||
var tmpScript = "\
|
var tmpScript = "\
|
||||||
(function() {\
|
(function() {\
|
||||||
var block = function(u, t) {\
|
var block = function(u, t) {\
|
||||||
var e = new CustomEvent('" + randEventName + "', {\
|
var e = new CustomEvent('" + vAPI.sessionId + "', {\
|
||||||
detail: {\
|
detail: {\
|
||||||
url: u,\
|
url: u,\
|
||||||
type: t\
|
type: t\
|
||||||
|
@ -298,5 +293,5 @@ return r;\
|
||||||
safari.self.tab.setContextMenuEventUserInfo(e, details);
|
safari.self.tab.setContextMenuEventUserInfo(e, details);
|
||||||
};
|
};
|
||||||
self.addEventListener("contextmenu", onContextMenu, true);
|
self.addEventListener("contextmenu", onContextMenu, true);
|
||||||
})();
|
})(this);
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
font: 12px sans-serif;
|
||||||
|
}
|
||||||
|
ul, li, div {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
padding: 6px 8px 4px 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: none;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: inline;
|
||||||
|
line-height: 1;
|
||||||
|
color: #444;
|
||||||
|
background-color: #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
button:hover {
|
||||||
|
background-color: #eee;
|
||||||
|
}
|
||||||
|
button:disabled {
|
||||||
|
color: #999;
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
#create:not(:disabled) {
|
||||||
|
background-color: #ffdca8;
|
||||||
|
}
|
||||||
|
section {
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
height: 8em;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
textarea {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font: 11px monospace;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 2px;
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
div {
|
||||||
|
bottom: 2px;
|
||||||
|
direction: ltr;
|
||||||
|
opacity: 0.2;
|
||||||
|
position: absolute;
|
||||||
|
right: 2px;
|
||||||
|
}
|
||||||
|
div:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
margin-left: 3px;
|
||||||
|
}
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
text-align: left;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
body > ul > li {
|
||||||
|
padding-top: 3px;
|
||||||
|
}
|
||||||
|
ul > li > span:nth-of-type(1) {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
ul > li > span:nth-of-type(2) {
|
||||||
|
font-size: smaller;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
ul > li > ul {
|
||||||
|
background-color: #eee;
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0 0 0 1em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
ul > li > ul > li {
|
||||||
|
font: 11px monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
ul > li > ul > li:hover {
|
||||||
|
background-color: rgba(255,255,255,1.0);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body direction="{{bidi_dir}}">
|
||||||
|
<section>
|
||||||
|
<textarea lang="en" dir="ltr" spellcheck="false"></textarea>
|
||||||
|
<div>
|
||||||
|
<button id="create" type="button" disabled="disabled">{{create}}</button>
|
||||||
|
<button id="pick" type="button">{{pick}}</button>
|
||||||
|
<button id="quit" type="button">{{quit}}</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<ul>
|
||||||
|
<li id="netFilters">
|
||||||
|
<span>{{netFilters}}</span><ul lang="en" class="changeFilter"></ul>
|
||||||
|
</li>
|
||||||
|
<li id="cosmeticFilters">
|
||||||
|
<span>{{cosmeticFilters}}</span> <span>{{cosmeticFiltersHint}}</span>
|
||||||
|
<ul lang="en" class="changeFilter"></ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
|
@ -125,12 +125,7 @@ if ( window.top !== window ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/314#issuecomment-58878112
|
var pickerRoot = document.getElementById(vAPI.sessionId);
|
||||||
// Using an id makes uBlock's CSS rules more specific, thus prevents
|
|
||||||
// surrounding external rules from winning over own rules.
|
|
||||||
var µBlockId = CSS.escape('µBlock');
|
|
||||||
|
|
||||||
var pickerRoot = document.getElementById(µBlockId);
|
|
||||||
|
|
||||||
if ( pickerRoot ) {
|
if ( pickerRoot ) {
|
||||||
return;
|
return;
|
||||||
|
@ -138,22 +133,22 @@ if ( pickerRoot ) {
|
||||||
|
|
||||||
var localMessager = vAPI.messaging.channel('element-picker.js');
|
var localMessager = vAPI.messaging.channel('element-picker.js');
|
||||||
|
|
||||||
var svgns = 'http://www.w3.org/2000/svg';
|
|
||||||
|
|
||||||
var svgRoot = null;
|
var svgRoot = null;
|
||||||
var svgOcean = null;
|
var svgOcean = null;
|
||||||
var svgIslands = null;
|
var svgIslands = null;
|
||||||
var divDialog = null;
|
var frameDialog = null;
|
||||||
|
var dialogBody = null;
|
||||||
var taCandidate = null;
|
var taCandidate = null;
|
||||||
var urlNormalizer = null;
|
var urlNormalizer = null;
|
||||||
|
|
||||||
|
var lastDetails = null;
|
||||||
|
|
||||||
var netFilterCandidates = [];
|
var netFilterCandidates = [];
|
||||||
var cosmeticFilterCandidates = [];
|
var cosmeticFilterCandidates = [];
|
||||||
|
|
||||||
var targetElements = [];
|
var targetElements = [];
|
||||||
var svgWidth = 0;
|
var svgWidth = 0;
|
||||||
var svgHeight = 0;
|
var svgHeight = 0;
|
||||||
var elementFromPointCSSProperty = 'pointerEvents';
|
|
||||||
var onSvgHoveredTimer = null;
|
var onSvgHoveredTimer = null;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -189,20 +184,6 @@ var unpausePicker = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var pickerRootDistance = function(elem) {
|
|
||||||
var distance = 0;
|
|
||||||
while ( elem ) {
|
|
||||||
if ( elem === pickerRoot ) {
|
|
||||||
return distance;
|
|
||||||
}
|
|
||||||
elem = elem.parentNode;
|
|
||||||
distance += 1;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var highlightElements = function(elems, force) {
|
var highlightElements = function(elems, force) {
|
||||||
// To make mouse move handler more efficient
|
// To make mouse move handler more efficient
|
||||||
if ( !force && elems.length === targetElements.length ) {
|
if ( !force && elems.length === targetElements.length ) {
|
||||||
|
@ -479,7 +460,7 @@ var userFilterFromCandidate = function() {
|
||||||
|
|
||||||
var onCandidateChanged = function() {
|
var onCandidateChanged = function() {
|
||||||
var elems = elementsFromFilter(taCandidate.value);
|
var elems = elementsFromFilter(taCandidate.value);
|
||||||
divDialog.querySelector('#create').disabled = elems.length === 0;
|
dialogBody.querySelector('#create').disabled = elems.length === 0;
|
||||||
highlightElements(elems);
|
highlightElements(elems);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -554,7 +535,7 @@ var onDialogClicked = function(ev) {
|
||||||
stopPicker();
|
stopPicker();
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ( ev.target.tagName.toLowerCase() === 'li' && pickerRootDistance(ev.target) === 5 ) {
|
else if ( ev.target.parentNode.classList.contains('changeFilter') ) {
|
||||||
taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev));
|
taCandidate.value = candidateFromFilterChoice(filterChoiceFromEvent(ev));
|
||||||
onCandidateChanged();
|
onCandidateChanged();
|
||||||
}
|
}
|
||||||
|
@ -583,7 +564,7 @@ var showDialog = function(options) {
|
||||||
|
|
||||||
// Create lists of candidate filters
|
// Create lists of candidate filters
|
||||||
var populate = function(src, des) {
|
var populate = function(src, des) {
|
||||||
var root = divDialog.querySelector(des);
|
var root = dialogBody.querySelector(des);
|
||||||
var ul = root.querySelector('ul');
|
var ul = root.querySelector('ul');
|
||||||
removeAllChildren(ul);
|
removeAllChildren(ul);
|
||||||
var li;
|
var li;
|
||||||
|
@ -598,8 +579,8 @@ var showDialog = function(options) {
|
||||||
populate(netFilterCandidates, '#netFilters');
|
populate(netFilterCandidates, '#netFilters');
|
||||||
populate(cosmeticFilterCandidates, '#cosmeticFilters');
|
populate(cosmeticFilterCandidates, '#cosmeticFilters');
|
||||||
|
|
||||||
divDialog.querySelector('ul').style.display = netFilterCandidates.length || cosmeticFilterCandidates.length ? '' : 'none';
|
dialogBody.querySelector('ul').style.display = netFilterCandidates.length || cosmeticFilterCandidates.length ? '' : 'none';
|
||||||
divDialog.querySelector('#create').disabled = true;
|
dialogBody.querySelector('#create').disabled = true;
|
||||||
|
|
||||||
// Auto-select a candidate filter
|
// Auto-select a candidate filter
|
||||||
var filterChoice = {
|
var filterChoice = {
|
||||||
|
@ -621,17 +602,19 @@ var showDialog = function(options) {
|
||||||
taCandidate.value = candidateFromFilterChoice(filterChoice);
|
taCandidate.value = candidateFromFilterChoice(filterChoice);
|
||||||
onCandidateChanged();
|
onCandidateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
frameDialog.style.height = dialogBody.offsetHeight + 'px';
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var elementFromPoint = function(x, y) {
|
var elementFromPoint = function(x, y) {
|
||||||
svgRoot.style[elementFromPointCSSProperty] = 'none';
|
svgRoot.style.pointerEvents = 'none';
|
||||||
var elem = document.elementFromPoint(x, y);
|
var elem = document.elementFromPoint(x, y);
|
||||||
if ( elem === document.body || elem === document.documentElement ) {
|
if ( elem === document.body || elem === document.documentElement ) {
|
||||||
elem = null;
|
elem = null;
|
||||||
}
|
}
|
||||||
svgRoot.style[elementFromPointCSSProperty] = '';
|
svgRoot.style.pointerEvents = '';
|
||||||
return elem;
|
return elem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -699,12 +682,13 @@ var stopPicker = function() {
|
||||||
window.removeEventListener('keydown', onKeyPressed, true);
|
window.removeEventListener('keydown', onKeyPressed, true);
|
||||||
window.removeEventListener('scroll', onScrolled, true);
|
window.removeEventListener('scroll', onScrolled, true);
|
||||||
taCandidate.removeEventListener('input', onCandidateChanged);
|
taCandidate.removeEventListener('input', onCandidateChanged);
|
||||||
divDialog.removeEventListener('click', onDialogClicked);
|
dialogBody.removeEventListener('click', onDialogClicked);
|
||||||
svgRoot.removeEventListener('mousemove', onSvgHovered);
|
svgRoot.removeEventListener('mousemove', onSvgHovered);
|
||||||
svgRoot.removeEventListener('click', onSvgClicked);
|
svgRoot.removeEventListener('click', onSvgClicked);
|
||||||
pickerRoot.parentNode.removeChild(pickerRoot);
|
pickerRoot.parentNode.removeChild(pickerRoot);
|
||||||
pickerRoot =
|
pickerRoot =
|
||||||
divDialog =
|
frameDialog =
|
||||||
|
dialogBody =
|
||||||
svgRoot = svgOcean = svgIslands =
|
svgRoot = svgOcean = svgIslands =
|
||||||
taCandidate =
|
taCandidate =
|
||||||
urlNormalizer = null;
|
urlNormalizer = null;
|
||||||
|
@ -715,267 +699,29 @@ var stopPicker = function() {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var startPicker = function(details) {
|
var onFrameReady = function() {
|
||||||
pickerRoot = document.createElement('div');
|
var elem;
|
||||||
pickerRoot.id = µBlockId;
|
var details = lastDetails;
|
||||||
pickerRoot.setAttribute('lang', navigator.language);
|
this.onload = lastDetails = null;
|
||||||
|
|
||||||
var pickerStyle = document.createElement('style');
|
var parsedDom = (new DOMParser()).parseFromString(
|
||||||
pickerStyle.setAttribute('scoped', '');
|
details.frameContent,
|
||||||
pickerStyle.textContent = [
|
'text/html'
|
||||||
'#µBlock, #µBlock * {',
|
);
|
||||||
'background: transparent;',
|
var frameDoc = this.contentDocument;
|
||||||
'background-image: none;',
|
frameDoc.documentElement.replaceChild(
|
||||||
'border: 0;',
|
frameDoc.adoptNode(parsedDom.head),
|
||||||
'border-radius: 0;',
|
frameDoc.head
|
||||||
'box-shadow: none;',
|
);
|
||||||
'color: #000;',
|
frameDoc.documentElement.replaceChild(
|
||||||
'display: inline;',
|
frameDoc.adoptNode(parsedDom.body),
|
||||||
'float: none;',
|
frameDoc.body
|
||||||
'font: 12px sans-serif;',
|
|
||||||
'height: auto;',
|
|
||||||
'letter-spacing: normal;',
|
|
||||||
'margin: 0;',
|
|
||||||
'max-width: none;',
|
|
||||||
'min-height: 0;',
|
|
||||||
'min-width: 0;',
|
|
||||||
'outline: 0;',
|
|
||||||
'overflow: visible;',
|
|
||||||
'padding: 0;',
|
|
||||||
'text-transform: none;',
|
|
||||||
'vertical-align: baseline;',
|
|
||||||
'width: auto;',
|
|
||||||
'z-index: auto;',
|
|
||||||
'}',
|
|
||||||
'#µBlock {',
|
|
||||||
'position: absolute;',
|
|
||||||
'top: 0;',
|
|
||||||
'left: 0;',
|
|
||||||
'}',
|
|
||||||
'#µBlock style, #µBlock script {',
|
|
||||||
'display: none;',
|
|
||||||
'}',
|
|
||||||
'#µBlock ul, #µBlock li, #µBlock div {',
|
|
||||||
'display: block;',
|
|
||||||
'}',
|
|
||||||
'#µBlock *::selection {',
|
|
||||||
'background-color: Highlight;',
|
|
||||||
'color: HighlightText;',
|
|
||||||
'}',
|
|
||||||
'#µBlock button {',
|
|
||||||
'border: 1px solid #aaa !important;',
|
|
||||||
'padding: 6px 8px 4px 8px;',
|
|
||||||
'box-sizing: border-box;',
|
|
||||||
'box-shadow: none;',
|
|
||||||
'border-radius: 3px;',
|
|
||||||
'display: inline;',
|
|
||||||
'line-height: 1;',
|
|
||||||
'color: #444;',
|
|
||||||
'background-color: #ccc;',
|
|
||||||
'cursor: pointer;',
|
|
||||||
'}',
|
|
||||||
'#µBlock button:hover {',
|
|
||||||
'background: none;',
|
|
||||||
'background-color: #eee;',
|
|
||||||
'background-image: none;',
|
|
||||||
'}',
|
|
||||||
'#µBlock button:disabled {',
|
|
||||||
'color: #999;',
|
|
||||||
'background-color: #ccc;',
|
|
||||||
'}',
|
|
||||||
'#µBlock button#create:not(:disabled) {',
|
|
||||||
'background-color: #ffdca8;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > svg {',
|
|
||||||
'position: absolute;',
|
|
||||||
'top: 0;',
|
|
||||||
'left: 0;',
|
|
||||||
'pointer-events: auto;',
|
|
||||||
'cursor: crosshair;',
|
|
||||||
'z-index: 4999999999;',
|
|
||||||
'}',
|
|
||||||
'#µBlock.paused > svg {',
|
|
||||||
'cursor: wait;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > svg > path:first-child {',
|
|
||||||
'fill: rgba(0,0,0,0.75);',
|
|
||||||
'fill-rule: evenodd;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > svg > path + path {',
|
|
||||||
'stroke: #F00;',
|
|
||||||
'stroke-width: 0.5px;',
|
|
||||||
'fill: rgba(255,0,0,0.25);',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div {',
|
|
||||||
'background-color: rgba(255,255,255,0.9);',
|
|
||||||
'bottom: 4px;',
|
|
||||||
'display: none;',
|
|
||||||
'font: 12px sans-serif;',
|
|
||||||
'padding: 4px;',
|
|
||||||
'position: fixed;',
|
|
||||||
'right: 4px;',
|
|
||||||
'width: 30em;',
|
|
||||||
'z-index: 5999999999;',
|
|
||||||
'}',
|
|
||||||
'#µBlock.paused > div {',
|
|
||||||
'opacity: 0.2;',
|
|
||||||
'display: block;',
|
|
||||||
'}',
|
|
||||||
'#µBlock.paused > div:hover {',
|
|
||||||
'opacity: 1;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > div {',
|
|
||||||
'box-sizing: border-box;',
|
|
||||||
'display: inline-block;',
|
|
||||||
'height: 8em;',
|
|
||||||
'padding: 0;',
|
|
||||||
'position: relative;',
|
|
||||||
'width: 100%;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > div > textarea {',
|
|
||||||
'background-color: white;',
|
|
||||||
'border: 1px solid #ccc;',
|
|
||||||
'box-sizing: border-box;',
|
|
||||||
'font: 11px monospace;',
|
|
||||||
'height: 100% !important;',
|
|
||||||
'max-height: 100% !important;',
|
|
||||||
'min-height: 100% !important;',
|
|
||||||
'overflow: hidden !important;',
|
|
||||||
'padding: 2px;',
|
|
||||||
'resize: none;',
|
|
||||||
'width: 100% !important;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > div > div {',
|
|
||||||
'bottom: 2px;',
|
|
||||||
'direction: ltr;',
|
|
||||||
'opacity: 0.2;',
|
|
||||||
'position: absolute;',
|
|
||||||
'right: 2px;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > div > div:hover {',
|
|
||||||
'opacity: 1;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > div > div > button {',
|
|
||||||
'margin-left: 3px !important;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul {',
|
|
||||||
'margin: 0;',
|
|
||||||
'list-style-type: none;',
|
|
||||||
'text-align: left;',
|
|
||||||
'overflow: hidden;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul > li {',
|
|
||||||
'padding-top: 3px;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul > li > span:nth-of-type(1) {',
|
|
||||||
'font-weight: bold;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul > li > span:nth-of-type(2) {',
|
|
||||||
'font-size: smaller;',
|
|
||||||
'color: gray;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul > li > ul {',
|
|
||||||
'background-color: #eee;',
|
|
||||||
'list-style-type: none;',
|
|
||||||
'margin: 0 0 0 1em;',
|
|
||||||
'overflow: hidden;',
|
|
||||||
'text-align: left;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul > li > ul > li {',
|
|
||||||
'font: 11px monospace;',
|
|
||||||
'white-space: nowrap;',
|
|
||||||
'cursor: pointer;',
|
|
||||||
'direction: ltr;',
|
|
||||||
'}',
|
|
||||||
'#µBlock > div > ul > li > ul > li:hover {',
|
|
||||||
'background-color: rgba(255,255,255,1.0);',
|
|
||||||
'}',
|
|
||||||
''
|
|
||||||
].join('\n');
|
|
||||||
pickerRoot.appendChild(pickerStyle);
|
|
||||||
|
|
||||||
svgRoot = document.createElementNS(svgns, 'svg');
|
|
||||||
svgRoot.appendChild(document.createElementNS(svgns, 'path'));
|
|
||||||
svgRoot.appendChild(document.createElementNS(svgns, 'path'));
|
|
||||||
svgWidth = document.documentElement.scrollWidth;
|
|
||||||
svgHeight = Math.max(
|
|
||||||
document.documentElement.scrollHeight,
|
|
||||||
window.scrollY + window.innerHeight
|
|
||||||
);
|
);
|
||||||
svgRoot.setAttribute('x', 0);
|
|
||||||
svgRoot.setAttribute('y', 0);
|
|
||||||
svgRoot.style.width = svgWidth + 'px';
|
|
||||||
svgRoot.style.height = svgHeight + 'px';
|
|
||||||
svgRoot.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight);
|
|
||||||
svgOcean = svgRoot.firstChild;
|
|
||||||
svgIslands = svgRoot.lastChild;
|
|
||||||
pickerRoot.appendChild(svgRoot);
|
|
||||||
|
|
||||||
// TODO: do not rely on element ids, they could collide with whatever
|
dialogBody = frameDoc.body;
|
||||||
// is used in the page. Just use built-in hierarchy of elements as
|
dialogBody.addEventListener('click', onDialogClicked);
|
||||||
// selectors.
|
taCandidate = dialogBody.querySelector('textarea');
|
||||||
|
|
||||||
divDialog = document.createElement('div');
|
|
||||||
divDialog.innerHTML = [
|
|
||||||
'<div>',
|
|
||||||
'<textarea lang="en" dir="ltr" spellcheck="false"></textarea>',
|
|
||||||
'<div>',
|
|
||||||
'<button id="create" type="button" disabled="disabled">.</button>',
|
|
||||||
'<button id="pick" type="button">.</button>',
|
|
||||||
'<button id="quit" type="button">.</button>',
|
|
||||||
'</div>',
|
|
||||||
'</div>',
|
|
||||||
'<ul>',
|
|
||||||
'<li id="netFilters"><span>.</span><ul lang="en"></ul></li>',
|
|
||||||
'<li id="cosmeticFilters"><span>.</span> <span>.</span><ul lang="en"></ul></li>',
|
|
||||||
'</ul>'
|
|
||||||
].join('');
|
|
||||||
pickerRoot.appendChild(divDialog);
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/344#issuecomment-60775958
|
|
||||||
// Insert in `html` tag, not `body` tag.
|
|
||||||
document.documentElement.appendChild(pickerRoot);
|
|
||||||
svgRoot.addEventListener('click', onSvgClicked);
|
|
||||||
svgRoot.addEventListener('mousemove', onSvgHovered);
|
|
||||||
divDialog.addEventListener('click', onDialogClicked);
|
|
||||||
taCandidate = divDialog.querySelector('textarea');
|
|
||||||
taCandidate.addEventListener('input', onCandidateChanged);
|
taCandidate.addEventListener('input', onCandidateChanged);
|
||||||
urlNormalizer = document.createElement('a');
|
|
||||||
window.addEventListener('scroll', onScrolled, true);
|
|
||||||
window.addEventListener('keydown', onKeyPressed, true);
|
|
||||||
|
|
||||||
highlightElements([], true);
|
|
||||||
|
|
||||||
var i18nMap = {
|
|
||||||
'#µBlock > div': '@@bidi_dir',
|
|
||||||
'#create': 'create',
|
|
||||||
'#pick': 'pick',
|
|
||||||
'#quit': 'quit',
|
|
||||||
'ul > li#netFilters > span:nth-of-type(1)': 'netFilters',
|
|
||||||
'ul > li#cosmeticFilters > span:nth-of-type(1)': 'cosmeticFilters',
|
|
||||||
'ul > li#cosmeticFilters > span:nth-of-type(2)': 'cosmeticFiltersHint'
|
|
||||||
};
|
|
||||||
|
|
||||||
if ( details.i18n['@@bidi_dir'] ) {
|
|
||||||
divDialog.style.direction = details.i18n['@@bidi_dir'];
|
|
||||||
delete i18nMap['#µBlock > div'];
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( var k in i18nMap ) {
|
|
||||||
if ( i18nMap.hasOwnProperty(k) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
divDialog.querySelector(k).firstChild.nodeValue = details.i18n[i18nMap[k]];
|
|
||||||
}
|
|
||||||
|
|
||||||
// First we test if pointer-events are hadnled in Node.elementFromPoint().
|
|
||||||
// If the browser ignores pointer-events in Node.elementFromPoint(),
|
|
||||||
// then use the display property instead (e.g., for older Safari).
|
|
||||||
var elem = elementFromPoint(0, 0);
|
|
||||||
|
|
||||||
if ( elem === svgRoot ) {
|
|
||||||
elementFromPointCSSProperty = 'display';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-select a specific target, if any, and if possible
|
// Auto-select a specific target, if any, and if possible
|
||||||
|
|
||||||
|
@ -1028,6 +774,124 @@ var startPicker = function(details) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var startPicker = function(details) {
|
||||||
|
pickerRoot = document.createElement('div');
|
||||||
|
var pid = vAPI.sessionId;
|
||||||
|
pickerRoot.id = pid;
|
||||||
|
pickerRoot.setAttribute('lang', navigator.language);
|
||||||
|
|
||||||
|
var pickerStyle = document.createElement('style');
|
||||||
|
pickerStyle.setAttribute('scoped', '');
|
||||||
|
pid = '#' + pid;
|
||||||
|
pickerStyle.textContent = [
|
||||||
|
pid + ' {',
|
||||||
|
'position: absolute;',
|
||||||
|
'top: 0;',
|
||||||
|
'left: 0;',
|
||||||
|
'}',
|
||||||
|
pid + ', ' + pid + ' > iframe, ' + pid + ' > svg {',
|
||||||
|
'background: transparent;',
|
||||||
|
'border: 0;',
|
||||||
|
'border-radius: 0;',
|
||||||
|
'box-shadow: none;',
|
||||||
|
'display: inline;',
|
||||||
|
'float: none;',
|
||||||
|
'height: auto;',
|
||||||
|
'margin: 0;',
|
||||||
|
'max-width: none;',
|
||||||
|
'min-height: 0;',
|
||||||
|
'min-width: 0;',
|
||||||
|
'outline: 0;',
|
||||||
|
'overflow: visible;',
|
||||||
|
'padding: 0;',
|
||||||
|
'width: auto;',
|
||||||
|
'z-index: auto;',
|
||||||
|
'}',
|
||||||
|
pid + ' > svg {',
|
||||||
|
'position: absolute;',
|
||||||
|
'top: 0;',
|
||||||
|
'left: 0;',
|
||||||
|
'pointer-events: auto;',
|
||||||
|
'cursor: crosshair;',
|
||||||
|
'z-index: 4999999999;',
|
||||||
|
'}',
|
||||||
|
pid + '.paused > svg {',
|
||||||
|
'cursor: wait;',
|
||||||
|
'}',
|
||||||
|
pid + ' > svg > path:first-child {',
|
||||||
|
'fill: rgba(0,0,0,0.75);',
|
||||||
|
'fill-rule: evenodd;',
|
||||||
|
'}',
|
||||||
|
pid + ' > svg > path + path {',
|
||||||
|
'stroke: #F00;',
|
||||||
|
'stroke-width: 0.5px;',
|
||||||
|
'fill: rgba(255,0,0,0.25);',
|
||||||
|
'}',
|
||||||
|
pid + ' > iframe {',
|
||||||
|
'background-color: rgba(255,255,255,0.9);',
|
||||||
|
'bottom: 4px;',
|
||||||
|
'display: none;',
|
||||||
|
'padding: 4px;',
|
||||||
|
'position: fixed;',
|
||||||
|
'right: 4px;',
|
||||||
|
'width: 30em;',
|
||||||
|
'z-index: 5999999999;',
|
||||||
|
'}',
|
||||||
|
pid + '.paused > iframe {',
|
||||||
|
'opacity: 0.2;',
|
||||||
|
'display: block;',
|
||||||
|
'}',
|
||||||
|
pid + '.paused > iframe:hover {',
|
||||||
|
'opacity: 1;',
|
||||||
|
'}',
|
||||||
|
''
|
||||||
|
].join('\n');
|
||||||
|
pickerRoot.appendChild(pickerStyle);
|
||||||
|
|
||||||
|
var svgns = 'http://www.w3.org/2000/svg';
|
||||||
|
svgRoot = document.createElementNS(svgns, 'svg');
|
||||||
|
svgRoot.appendChild(document.createElementNS(svgns, 'path'));
|
||||||
|
svgRoot.appendChild(document.createElementNS(svgns, 'path'));
|
||||||
|
svgWidth = document.documentElement.scrollWidth;
|
||||||
|
svgHeight = Math.max(
|
||||||
|
document.documentElement.scrollHeight,
|
||||||
|
window.scrollY + window.innerHeight
|
||||||
|
);
|
||||||
|
svgRoot.setAttribute('x', 0);
|
||||||
|
svgRoot.setAttribute('y', 0);
|
||||||
|
svgRoot.style.width = svgWidth + 'px';
|
||||||
|
svgRoot.style.height = svgHeight + 'px';
|
||||||
|
svgRoot.setAttribute('viewBox', '0 0 ' + svgWidth + ' ' + svgHeight);
|
||||||
|
svgOcean = svgRoot.firstChild;
|
||||||
|
svgIslands = svgRoot.lastChild;
|
||||||
|
pickerRoot.appendChild(svgRoot);
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/344#issuecomment-60775958
|
||||||
|
// Insert in `html` tag, not `body` tag.
|
||||||
|
document.documentElement.appendChild(pickerRoot);
|
||||||
|
svgRoot.addEventListener('click', onSvgClicked);
|
||||||
|
svgRoot.addEventListener('mousemove', onSvgHovered);
|
||||||
|
urlNormalizer = document.createElement('a');
|
||||||
|
window.addEventListener('scroll', onScrolled, true);
|
||||||
|
window.addEventListener('keydown', onKeyPressed, true);
|
||||||
|
|
||||||
|
highlightElements([], true);
|
||||||
|
|
||||||
|
lastDetails = {
|
||||||
|
frameContent: details.frameContent,
|
||||||
|
clientX: details.clientX,
|
||||||
|
clientY: details.clientY,
|
||||||
|
target: details.target
|
||||||
|
};
|
||||||
|
|
||||||
|
frameDialog = document.createElement('iframe');
|
||||||
|
frameDialog.setAttribute('seamless', '');
|
||||||
|
frameDialog.onload = onFrameReady;
|
||||||
|
pickerRoot.appendChild(frameDialog);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
localMessager.send({ what: 'elementPickerArguments' }, startPicker);
|
localMessager.send({ what: 'elementPickerArguments' }, startPicker);
|
||||||
|
|
||||||
// So the shortcuts will be usable in Firefox
|
// So the shortcuts will be usable in Firefox
|
||||||
|
|
|
@ -527,6 +527,40 @@ var µb = µBlock;
|
||||||
var onMessage = function(request, sender, callback) {
|
var onMessage = function(request, sender, callback) {
|
||||||
// Async
|
// Async
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
|
case 'elementPickerArguments':
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('GET', 'epicker.html', true);
|
||||||
|
xhr.overrideMimeType('text/html;charset=utf-8');
|
||||||
|
xhr.responseType = 'text';
|
||||||
|
xhr.onload = function() {
|
||||||
|
this.onload = null;
|
||||||
|
var i18n = {
|
||||||
|
bidi_dir: document.body.getAttribute('dir'),
|
||||||
|
create: vAPI.i18n('pickerCreate'),
|
||||||
|
pick: vAPI.i18n('pickerPick'),
|
||||||
|
quit: vAPI.i18n('pickerQuit'),
|
||||||
|
netFilters: vAPI.i18n('pickerNetFilters'),
|
||||||
|
cosmeticFilters: vAPI.i18n('pickerCosmeticFilters'),
|
||||||
|
cosmeticFiltersHint: vAPI.i18n('pickerCosmeticFiltersHint')
|
||||||
|
};
|
||||||
|
var reStrings = /\{\{(\w+)\}\}/g;
|
||||||
|
var replacer = function(a0, string) {
|
||||||
|
return i18n[string];
|
||||||
|
};
|
||||||
|
|
||||||
|
callback({
|
||||||
|
frameContent: this.responseText.replace(reStrings, replacer),
|
||||||
|
target: µb.contextMenuTarget,
|
||||||
|
clientX: µb.contextMenuClientX,
|
||||||
|
clientY: µb.contextMenuClientY
|
||||||
|
});
|
||||||
|
|
||||||
|
µb.contextMenuTarget = '';
|
||||||
|
µb.contextMenuClientX = -1;
|
||||||
|
µb.contextMenuClientY = -1;
|
||||||
|
};
|
||||||
|
xhr.send();
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -535,26 +569,6 @@ var onMessage = function(request, sender, callback) {
|
||||||
var response;
|
var response;
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'elementPickerArguments':
|
|
||||||
response = {
|
|
||||||
i18n: {
|
|
||||||
'@@bidi_dir': document.body.getAttribute('dir'),
|
|
||||||
create: vAPI.i18n('pickerCreate'),
|
|
||||||
pick: vAPI.i18n('pickerPick'),
|
|
||||||
quit: vAPI.i18n('pickerQuit'),
|
|
||||||
netFilters: vAPI.i18n('pickerNetFilters'),
|
|
||||||
cosmeticFilters: vAPI.i18n('pickerCosmeticFilters'),
|
|
||||||
cosmeticFiltersHint: vAPI.i18n('pickerCosmeticFiltersHint')
|
|
||||||
},
|
|
||||||
target: µb.contextMenuTarget,
|
|
||||||
clientX: µb.contextMenuClientX,
|
|
||||||
clientY: µb.contextMenuClientY
|
|
||||||
};
|
|
||||||
µb.contextMenuTarget = '';
|
|
||||||
µb.contextMenuClientX = -1;
|
|
||||||
µb.contextMenuClientY = -1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'createUserFilter':
|
case 'createUserFilter':
|
||||||
µb.appendUserFilters(request.filters);
|
µb.appendUserFilters(request.filters);
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue