mirror of
https://github.com/gorhill/uBlock.git
synced 2024-09-20 13:03:56 +02:00
Isolate element picker dialog from page content world
Related issues: - https://github.com/gorhill/uBlock/issues/3497 - https://github.com/uBlockOrigin/uBlock-issues/issues/1215 To solve above issues, the element picker's dialog is now isolated from the page content in which it is embedded. The highly interactive, mouse-driven part of the element picker is still visible by the page content.
This commit is contained in:
parent
43dba2bd0e
commit
9eb455ab5e
9 changed files with 1043 additions and 819 deletions
|
@ -102,8 +102,14 @@ vAPI.messaging = {
|
||||||
},
|
},
|
||||||
disconnectListenerBound: null,
|
disconnectListenerBound: null,
|
||||||
|
|
||||||
|
// 2020-09-01:
|
||||||
|
// In Firefox, `details instanceof Object` resolves to `false` despite
|
||||||
|
// `details` being a valid object. Consequently, falling back to use
|
||||||
|
// `typeof details`.
|
||||||
|
// This is an issue which surfaced when the element picker code was
|
||||||
|
// revisited to isolate the picker dialog DOM from the page DOM.
|
||||||
messageListener: function(details) {
|
messageListener: function(details) {
|
||||||
if ( details instanceof Object === false ) { return; }
|
if ( typeof details !== 'object' || details === null ) { return; }
|
||||||
|
|
||||||
// Response to specific message previously sent
|
// Response to specific message previously sent
|
||||||
if ( details.msgId !== undefined ) {
|
if ( details.msgId !== undefined ) {
|
||||||
|
|
184
src/css/epicker-dialog.css
Normal file
184
src/css/epicker-dialog.css
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
html#ublock0-epicker,
|
||||||
|
#ublock0-epicker body {
|
||||||
|
background: transparent;
|
||||||
|
color: black;
|
||||||
|
cursor: not-allowed;
|
||||||
|
font: 12px sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
#ublock0-epicker :focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
#ublock0-epicker ul,
|
||||||
|
#ublock0-epicker li,
|
||||||
|
#ublock0-epicker div {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #toolbar {
|
||||||
|
cursor: grab;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
#ublock0-epicker aside.moving #toolbar {
|
||||||
|
cursor: grabbing;
|
||||||
|
}
|
||||||
|
#ublock0-epicker ul {
|
||||||
|
margin: 0.25em 0 0 0;
|
||||||
|
}
|
||||||
|
#ublock0-epicker button {
|
||||||
|
background-color: #ccc;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
box-shadow: none;
|
||||||
|
color: #000;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.7;
|
||||||
|
padding: 4px 6px;
|
||||||
|
}
|
||||||
|
#ublock0-epicker button:disabled {
|
||||||
|
color: #999;
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
#ublock0-epicker button:not(:disabled):hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #create:not(:disabled) {
|
||||||
|
background-color: hsl(36, 100%, 83%);
|
||||||
|
border-color: hsl(36, 50%, 60%);
|
||||||
|
}
|
||||||
|
#ublock0-epicker #preview {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.preview #preview {
|
||||||
|
background-color: hsl(204, 100%, 83%);
|
||||||
|
border-color: hsl(204, 50%, 60%);
|
||||||
|
}
|
||||||
|
#ublock0-epicker section {
|
||||||
|
border: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#ublock0-epicker section > div:first-child {
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
#ublock0-epicker section.invalidFilter > div:first-child {
|
||||||
|
border-color: red;
|
||||||
|
}
|
||||||
|
#ublock0-epicker section > div:first-child > textarea {
|
||||||
|
background-color: #fff;
|
||||||
|
border: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #000;
|
||||||
|
font: 11px monospace;
|
||||||
|
height: 8em;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 2px;
|
||||||
|
resize: none;
|
||||||
|
width: 100%;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #resultsetCount {
|
||||||
|
background-color: #aaa;
|
||||||
|
bottom: 0;
|
||||||
|
color: white;
|
||||||
|
padding: 2px 4px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
#ublock0-epicker section.invalidFilter #resultsetCount {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
#ublock0-epicker section > div:first-child + div {
|
||||||
|
direction: ltr;
|
||||||
|
margin: 2px 0;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#ublock0-epicker ul {
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
text-align: left;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #candidateFilters {
|
||||||
|
max-height: 16em;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #candidateFilters > li:first-of-type {
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
#ublock0-epicker .changeFilter > li > span:nth-of-type(1) {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
#ublock0-epicker .changeFilter > li > span:nth-of-type(2) {
|
||||||
|
font-size: smaller;
|
||||||
|
color: gray;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #candidateFilters .changeFilter {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0 0 0 1em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #candidateFilters .changeFilter li {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
direction: ltr;
|
||||||
|
font: 11px monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #candidateFilters .changeFilter li.active {
|
||||||
|
border: 1px dotted gray;
|
||||||
|
}
|
||||||
|
#ublock0-epicker #candidateFilters .changeFilter li:hover {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
#ublock0-epicker aside {
|
||||||
|
background-color: #eee;
|
||||||
|
border: 1px solid #aaa;
|
||||||
|
bottom: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
cursor: default;
|
||||||
|
min-width: 24em;
|
||||||
|
padding: 4px;
|
||||||
|
position: fixed;
|
||||||
|
right: 4px;
|
||||||
|
width: calc(40% - 4px);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
https://github.com/gorhill/uBlock/issues/3449
|
||||||
|
https://github.com/uBlockOrigin/uBlock-issues/issues/55
|
||||||
|
**/
|
||||||
|
@keyframes startDialog {
|
||||||
|
0% { opacity: 1.0; }
|
||||||
|
60% { opacity: 1.0; }
|
||||||
|
100% { opacity: 0.1; }
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.paused > aside {
|
||||||
|
opacity: 0.1;
|
||||||
|
visibility: visible;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.paused > aside:not(:hover):not(.show) {
|
||||||
|
animation-duration: 1.6s;
|
||||||
|
animation-name: startDialog;
|
||||||
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.paused > aside:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.paused > aside.show {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.paused > aside.hide {
|
||||||
|
opacity: 0.1;
|
||||||
|
}
|
||||||
|
|
60
src/css/epicker.css
Normal file
60
src/css/epicker.css
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
html#ublock0-epicker,
|
||||||
|
#ublock0-epicker body {
|
||||||
|
background: transparent !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
color: black !important;
|
||||||
|
font: 12px sans-serif !important;
|
||||||
|
height: 100vh !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
position: fixed !important;
|
||||||
|
width: 100vw !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker :focus {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker svg {
|
||||||
|
cursor: crosshair !important;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 100% !important;
|
||||||
|
left: 0 !important;
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker .paused > svg {
|
||||||
|
cursor: not-allowed !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker svg > path:first-child {
|
||||||
|
fill: rgba(0,0,0,0.5) !important;
|
||||||
|
fill-rule: evenodd !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker svg > path + path {
|
||||||
|
stroke: #F00 !important;
|
||||||
|
stroke-width: 0.5px !important;
|
||||||
|
fill: rgba(255,63,63,0.20) !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.zap svg > path + path {
|
||||||
|
stroke: #FF0 !important;
|
||||||
|
stroke-width: 0.5px !important;
|
||||||
|
fill: rgba(255,255,63,0.20) !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.preview svg > path {
|
||||||
|
fill: rgba(0,0,0,0.10) !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.preview svg > path + path {
|
||||||
|
stroke: none !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body > iframe {
|
||||||
|
border: 0 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
display: none !important;
|
||||||
|
height: 100% !important;
|
||||||
|
left: 0 !important;
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
#ublock0-epicker body.paused > iframe {
|
||||||
|
display: initial !important;
|
||||||
|
}
|
250
src/epicker.html
250
src/epicker.html
|
@ -1,250 +0,0 @@
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>uBlock Origin Element Picker</title>
|
|
||||||
<style>
|
|
||||||
html#ublock0-epicker,
|
|
||||||
#ublock0-epicker body {
|
|
||||||
background: transparent !important;
|
|
||||||
color: black !important;
|
|
||||||
font: 12px sans-serif !important;
|
|
||||||
height: 100% !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker :focus {
|
|
||||||
outline: none !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker ul,
|
|
||||||
#ublock0-epicker li,
|
|
||||||
#ublock0-epicker div {
|
|
||||||
display: block !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #toolbar {
|
|
||||||
cursor: grab;
|
|
||||||
display: flex !important;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
#ublock0-epicker aside.moving #toolbar {
|
|
||||||
cursor: grabbing;
|
|
||||||
}
|
|
||||||
#ublock0-epicker ul {
|
|
||||||
margin: 0.25em 0 0 0 !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker button {
|
|
||||||
background-color: #ccc !important;
|
|
||||||
border: 1px solid #aaa !important;
|
|
||||||
border-radius: 3px !important;
|
|
||||||
box-sizing: border-box !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
color: #000 !important;
|
|
||||||
cursor: pointer !important;
|
|
||||||
opacity: 0.7 !important;
|
|
||||||
padding: 4px 6px !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker button:disabled {
|
|
||||||
color: #999 !important;
|
|
||||||
background-color: #ccc !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker button:not(:disabled):hover {
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #create:not(:disabled) {
|
|
||||||
background-color: hsl(36, 100%, 83%) !important;
|
|
||||||
border-color: hsl(36, 50%, 60%) !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #preview {
|
|
||||||
float: left !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.preview #preview {
|
|
||||||
background-color: hsl(204, 100%, 83%) !important;
|
|
||||||
border-color: hsl(204, 50%, 60%) !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker section {
|
|
||||||
border: 0 !important;
|
|
||||||
box-sizing: border-box !important;
|
|
||||||
display: inline-block !important;
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker section > div:first-child {
|
|
||||||
border: 1px solid #aaa !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
position: relative !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker section.invalidFilter > div:first-child {
|
|
||||||
border-color: red !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker section > div:first-child > textarea {
|
|
||||||
background-color: #fff !important;
|
|
||||||
border: none !important;
|
|
||||||
box-sizing: border-box !important;
|
|
||||||
color: #000 !important;
|
|
||||||
font: 11px monospace !important;
|
|
||||||
height: 8em !important;
|
|
||||||
margin: 0 !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
overflow-y: auto !important;
|
|
||||||
padding: 2px !important;
|
|
||||||
resize: none !important;
|
|
||||||
width: 100% !important;
|
|
||||||
word-break: break-all !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #resultsetCount {
|
|
||||||
background-color: #aaa !important;
|
|
||||||
bottom: 0 !important;
|
|
||||||
color: white !important;
|
|
||||||
padding: 2px 4px !important;
|
|
||||||
position: absolute !important;
|
|
||||||
right: 0 !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker section.invalidFilter #resultsetCount {
|
|
||||||
background-color: red !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker section > div:first-child + div {
|
|
||||||
direction: ltr !important;
|
|
||||||
margin: 2px 0 !important;
|
|
||||||
text-align: right !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker ul {
|
|
||||||
padding: 0 !important;
|
|
||||||
list-style-type: none !important;
|
|
||||||
text-align: left !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #candidateFilters {
|
|
||||||
max-height: 16em !important;
|
|
||||||
overflow-y: auto !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #candidateFilters > li:first-of-type {
|
|
||||||
margin-bottom: 0.5em !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker .changeFilter > li > span:nth-of-type(1) {
|
|
||||||
font-weight: bold !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker .changeFilter > li > span:nth-of-type(2) {
|
|
||||||
font-size: smaller !important;
|
|
||||||
color: gray !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #candidateFilters .changeFilter {
|
|
||||||
list-style-type: none !important;
|
|
||||||
margin: 0 0 0 1em !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
text-align: left !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #candidateFilters .changeFilter li {
|
|
||||||
border: 1px solid transparent;
|
|
||||||
cursor: pointer !important;
|
|
||||||
direction: ltr !important;
|
|
||||||
font: 11px monospace !important;
|
|
||||||
white-space: nowrap !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #candidateFilters .changeFilter li.active {
|
|
||||||
border: 1px dotted gray;
|
|
||||||
}
|
|
||||||
#ublock0-epicker #candidateFilters .changeFilter li:hover {
|
|
||||||
background-color: white !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker svg {
|
|
||||||
position: fixed !important;
|
|
||||||
top: 0 !important;
|
|
||||||
left: 0 !important;
|
|
||||||
cursor: crosshair !important;
|
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker .paused > svg {
|
|
||||||
cursor: not-allowed !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker svg > path:first-child {
|
|
||||||
fill: rgba(0,0,0,0.5) !important;
|
|
||||||
fill-rule: evenodd !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker svg > path + path {
|
|
||||||
stroke: #F00 !important;
|
|
||||||
stroke-width: 0.5px !important;
|
|
||||||
fill: rgba(255,63,63,0.20) !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.zap svg > path + path {
|
|
||||||
stroke: #FF0 !important;
|
|
||||||
stroke-width: 0.5px !important;
|
|
||||||
fill: rgba(255,255,63,0.20) !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.preview svg > path {
|
|
||||||
fill: rgba(0,0,0,0.10) !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.preview svg > path + path {
|
|
||||||
stroke: none !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker aside {
|
|
||||||
background-color: #eee !important;
|
|
||||||
border: 1px solid #aaa !important;
|
|
||||||
bottom: 4px !important;
|
|
||||||
box-sizing: border-box !important;
|
|
||||||
min-width: 24em !important;
|
|
||||||
padding: 4px !important;
|
|
||||||
position: fixed !important;
|
|
||||||
right: 4px !important;
|
|
||||||
visibility: hidden !important;
|
|
||||||
width: calc(40% - 4px) !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.paused > aside {
|
|
||||||
opacity: 0.1;
|
|
||||||
visibility: visible !important;
|
|
||||||
z-index: 100 !important;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
https://github.com/gorhill/uBlock/issues/3449
|
|
||||||
https://github.com/uBlockOrigin/uBlock-issues/issues/55
|
|
||||||
**/
|
|
||||||
@keyframes startDialog {
|
|
||||||
0% { opacity: 1.0; }
|
|
||||||
60% { opacity: 1.0; }
|
|
||||||
100% { opacity: 0.1; }
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.paused > aside:not(:hover):not(.show) {
|
|
||||||
animation-duration: 1.6s !important;
|
|
||||||
animation-name: startDialog !important;
|
|
||||||
animation-timing-function: linear !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.paused > aside:hover {
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.paused > aside.show {
|
|
||||||
opacity: 1 !important;
|
|
||||||
}
|
|
||||||
#ublock0-epicker body.paused > aside.hide {
|
|
||||||
opacity: 0.1 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body direction="{{bidi_dir}}">
|
|
||||||
<svg><path d></path><path d></path></svg>
|
|
||||||
<aside>
|
|
||||||
<section>
|
|
||||||
<div>
|
|
||||||
<textarea lang="en" dir="ltr" spellcheck="false"></textarea>
|
|
||||||
<div id="resultsetCount"></div>
|
|
||||||
</div>
|
|
||||||
<div id="toolbar">
|
|
||||||
<div>
|
|
||||||
<button id="preview" type="button">{{preview}}</button>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<button id="create" type="button" disabled>{{create}}</button>
|
|
||||||
<button id="pick" type="button">{{pick}}</button>
|
|
||||||
<button id="quit" type="button">{{quit}}</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
<ul id="candidateFilters">
|
|
||||||
<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>
|
|
||||||
</aside>
|
|
||||||
</body>
|
|
499
src/js/epicker-dialog.js
Normal file
499
src/js/epicker-dialog.js
Normal file
|
@ -0,0 +1,499 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2014-present 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
(( ) => {
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if ( typeof vAPI !== 'object' ) { return; }
|
||||||
|
|
||||||
|
const epickerId = (( ) => {
|
||||||
|
const url = new URL(self.location.href);
|
||||||
|
return url.searchParams.get('epid');
|
||||||
|
})();
|
||||||
|
if ( epickerId === null ) { return; }
|
||||||
|
|
||||||
|
let epickerConnectionId;
|
||||||
|
let filterHostname = '';
|
||||||
|
let filterOrigin = '';
|
||||||
|
let filterResultset = [];
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const $id = id => document.getElementById(id);
|
||||||
|
const $stor = selector => document.querySelector(selector);
|
||||||
|
const $storAll = selector => document.querySelectorAll(selector);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const filterFromTextarea = function() {
|
||||||
|
const s = taCandidate.value.trim();
|
||||||
|
if ( s === '' ) { return ''; }
|
||||||
|
const pos = s.indexOf('\n');
|
||||||
|
const filter = pos === -1 ? s.trim() : s.slice(0, pos).trim();
|
||||||
|
staticFilteringParser.analyze(filter);
|
||||||
|
staticFilteringParser.analyzeExtra();
|
||||||
|
return staticFilteringParser.shouldDiscard() ? '!' : filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const userFilterFromCandidate = function(filter) {
|
||||||
|
if ( filter === '' || filter === '!' ) { return; }
|
||||||
|
|
||||||
|
// Cosmetic filter?
|
||||||
|
if ( filter.startsWith('##') ) {
|
||||||
|
return filterHostname + filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume net filter
|
||||||
|
const opts = [];
|
||||||
|
|
||||||
|
// If no domain included in filter, we need domain option
|
||||||
|
if ( filter.startsWith('||') === false ) {
|
||||||
|
opts.push(`domain=${filterHostname}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( filterResultset.length !== 0 ) {
|
||||||
|
const item = filterResultset[0];
|
||||||
|
if ( item.opts ) {
|
||||||
|
opts.push(item.opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( opts.length ) {
|
||||||
|
filter += '$' + opts.join(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
return filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const candidateFromFilterChoice = function(filterChoice) {
|
||||||
|
let { slot, filters } = filterChoice;
|
||||||
|
let filter = filters[slot];
|
||||||
|
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/47
|
||||||
|
for ( const elem of $storAll('#candidateFilters li') ) {
|
||||||
|
elem.classList.remove('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( filter === undefined ) { return ''; }
|
||||||
|
|
||||||
|
// For net filters there no such thing as a path
|
||||||
|
if ( filter.startsWith('##') === false ) {
|
||||||
|
$stor(`#netFilters li:nth-of-type(${slot+1})`)
|
||||||
|
.classList.add('active');
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, we have a cosmetic filter
|
||||||
|
|
||||||
|
$stor(`#cosmeticFilters li:nth-of-type(${slot+1})`)
|
||||||
|
.classList.add('active');
|
||||||
|
|
||||||
|
// Modifier means "target broadly". Hence:
|
||||||
|
// - Do not compute exact path.
|
||||||
|
// - Discard narrowing directives.
|
||||||
|
// - Remove the id if one or more classes exist
|
||||||
|
// TODO: should remove tag name too? ¯\_(ツ)_/¯
|
||||||
|
if ( filterChoice.modifier ) {
|
||||||
|
filter = filter.replace(/:nth-of-type\(\d+\)/, '');
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/162
|
||||||
|
// Mind escaped periods: they do not denote a class identifier.
|
||||||
|
if ( filter.charAt(2) === '#' ) {
|
||||||
|
const pos = filter.search(/[^\\]\./);
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
filter = '##' + filter.slice(pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return path: the target element, then all siblings prepended
|
||||||
|
let selector = '', joiner = '';
|
||||||
|
for ( ; slot < filters.length; slot++ ) {
|
||||||
|
filter = filters[slot];
|
||||||
|
// Remove all classes when an id exists.
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/162
|
||||||
|
// Mind escaped periods: they do not denote a class identifier.
|
||||||
|
if ( filter.charAt(2) === '#' ) {
|
||||||
|
filter = filter.replace(/([^\\])\..+$/, '$1');
|
||||||
|
}
|
||||||
|
selector = filter.slice(2) + joiner + selector;
|
||||||
|
// Stop at any element with an id: these are unique in a web page
|
||||||
|
if ( filter.startsWith('###') ) { break; }
|
||||||
|
// Stop if current selector matches only one element on the page
|
||||||
|
if ( document.querySelectorAll(selector).length === 1 ) { break; }
|
||||||
|
joiner = ' > ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/2519
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/17
|
||||||
|
if (
|
||||||
|
slot === filters.length &&
|
||||||
|
selector.startsWith('body > ') === false &&
|
||||||
|
document.querySelectorAll(selector).length > 1
|
||||||
|
) {
|
||||||
|
selector = 'body > ' + selector;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '##' + selector;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onCandidateChanged = function() {
|
||||||
|
const filter = filterFromTextarea();
|
||||||
|
const bad = filter === '!';
|
||||||
|
$stor('section').classList.toggle('invalidFilter', bad);
|
||||||
|
$id('create').disabled = bad;
|
||||||
|
if ( bad ) {
|
||||||
|
$id('resultsetCount').textContent = 'E';
|
||||||
|
$id('create').setAttribute('disabled', '');
|
||||||
|
}
|
||||||
|
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||||
|
what: 'dialogSetFilter',
|
||||||
|
filter,
|
||||||
|
compiled: filter.startsWith('##')
|
||||||
|
? staticFilteringParser.result.compiled
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onPreviewClicked = function() {
|
||||||
|
const state = pickerBody.classList.toggle('preview');
|
||||||
|
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||||
|
what: 'dialogPreview',
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onCreateClicked = function() {
|
||||||
|
const candidate = filterFromTextarea();
|
||||||
|
const filter = userFilterFromCandidate(candidate);
|
||||||
|
if ( filter !== undefined ) {
|
||||||
|
vAPI.messaging.send('elementPicker', {
|
||||||
|
what: 'createUserFilter',
|
||||||
|
autoComment: true,
|
||||||
|
filters: filter,
|
||||||
|
origin: filterOrigin,
|
||||||
|
pageDomain: filterHostname,
|
||||||
|
killCache: /^#[$?]?#/.test(candidate) === false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||||
|
what: 'dialogCreate',
|
||||||
|
filter: candidate,
|
||||||
|
compiled: candidate.startsWith('##')
|
||||||
|
? staticFilteringParser.result.compiled
|
||||||
|
: undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onPickClicked = function(ev) {
|
||||||
|
if (
|
||||||
|
(ev instanceof MouseEvent) &&
|
||||||
|
(ev.type === 'mousedown') &&
|
||||||
|
(ev.which !== 1 || ev.target !== document.body)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pickerBody.classList.remove('paused');
|
||||||
|
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||||
|
what: 'dialogPick'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onQuitClicked = function() {
|
||||||
|
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||||
|
what: 'dialogQuit'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onCandidateClicked = function(ev) {
|
||||||
|
let li = ev.target.closest('li');
|
||||||
|
const ul = li.closest('.changeFilter');
|
||||||
|
if ( ul === null ) { return; }
|
||||||
|
const choice = {
|
||||||
|
filters: Array.from(ul.querySelectorAll('li')).map(a => a.textContent),
|
||||||
|
slot: 0,
|
||||||
|
modifier: ev.ctrlKey || ev.metaKey
|
||||||
|
};
|
||||||
|
while ( li.previousElementSibling !== null ) {
|
||||||
|
li = li.previousElementSibling;
|
||||||
|
choice.slot += 1;
|
||||||
|
}
|
||||||
|
taCandidate.value = candidateFromFilterChoice(choice);
|
||||||
|
onCandidateChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onKeyPressed = function(ev) {
|
||||||
|
// Esc
|
||||||
|
if ( ev.key === 'Escape' || ev.which === 27 ) {
|
||||||
|
onQuitClicked();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onStartMoving = (( ) => {
|
||||||
|
let mx0 = 0, my0 = 0;
|
||||||
|
let mx1 = 0, my1 = 0;
|
||||||
|
let r0 = 0, b0 = 0;
|
||||||
|
let rMax = 0, bMax = 0;
|
||||||
|
let timer;
|
||||||
|
|
||||||
|
const move = ( ) => {
|
||||||
|
timer = undefined;
|
||||||
|
let r1 = Math.min(Math.max(r0 - mx1 + mx0, 4), rMax);
|
||||||
|
let b1 = Math.min(Math.max(b0 - my1 + my0, 4), bMax);
|
||||||
|
dialog.style.setProperty('right', `${r1}px`, 'important');
|
||||||
|
dialog.style.setProperty('bottom', `${b1}px`, 'important');
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveAsync = ev => {
|
||||||
|
if ( ev.isTrusted === false ) { return; }
|
||||||
|
eatEvent(ev);
|
||||||
|
if ( timer !== undefined ) { return; }
|
||||||
|
mx1 = ev.pageX;
|
||||||
|
my1 = ev.pageY;
|
||||||
|
timer = self.requestAnimationFrame(move);
|
||||||
|
};
|
||||||
|
|
||||||
|
const stop = ev => {
|
||||||
|
if ( ev.isTrusted === false ) { return; }
|
||||||
|
if ( dialog.classList.contains('moving') === false ) { return; }
|
||||||
|
dialog.classList.remove('moving');
|
||||||
|
self.removeEventListener('mousemove', moveAsync, { capture: true });
|
||||||
|
self.removeEventListener('mouseup', stop, { capture: true, once: true });
|
||||||
|
eatEvent(ev);
|
||||||
|
};
|
||||||
|
|
||||||
|
return function(ev) {
|
||||||
|
if ( ev.isTrusted === false ) { return; }
|
||||||
|
const target = dialog.querySelector('#toolbar');
|
||||||
|
if ( ev.target !== target ) { return; }
|
||||||
|
if ( dialog.classList.contains('moving') ) { return; }
|
||||||
|
mx0 = ev.pageX; my0 = ev.pageY;
|
||||||
|
const style = self.getComputedStyle(dialog);
|
||||||
|
r0 = parseInt(style.right, 10);
|
||||||
|
b0 = parseInt(style.bottom, 10);
|
||||||
|
const rect = dialog.getBoundingClientRect();
|
||||||
|
rMax = pickerBody.clientWidth - 4 - rect.width ;
|
||||||
|
bMax = pickerBody.clientHeight - 4 - rect.height;
|
||||||
|
dialog.classList.add('moving');
|
||||||
|
self.addEventListener('mousemove', moveAsync, { capture: true });
|
||||||
|
self.addEventListener('mouseup', stop, { capture: true, once: true });
|
||||||
|
eatEvent(ev);
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const eatEvent = function(ev) {
|
||||||
|
ev.stopPropagation();
|
||||||
|
ev.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const showDialog = function(details) {
|
||||||
|
pickerBody.classList.add('paused');
|
||||||
|
|
||||||
|
const { netFilters, cosmeticFilters, filter, options } = details;
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/738
|
||||||
|
// Trim dots.
|
||||||
|
filterHostname = details.hostname;
|
||||||
|
if ( filterHostname.slice(-1) === '.' ) {
|
||||||
|
filterHostname = filterHostname.slice(0, -1);
|
||||||
|
}
|
||||||
|
filterOrigin = details.origin;
|
||||||
|
|
||||||
|
// Create lists of candidate filters
|
||||||
|
const populate = function(src, des) {
|
||||||
|
const root = dialog.querySelector(des);
|
||||||
|
const ul = root.querySelector('ul');
|
||||||
|
while ( ul.firstChild !== null ) {
|
||||||
|
ul.firstChild.remove();
|
||||||
|
}
|
||||||
|
for ( let i = 0; i < src.length; i++ ) {
|
||||||
|
const li = document.createElement('li');
|
||||||
|
li.textContent = src[i];
|
||||||
|
ul.appendChild(li);
|
||||||
|
}
|
||||||
|
if ( src.length !== 0 ) {
|
||||||
|
root.style.removeProperty('display');
|
||||||
|
} else {
|
||||||
|
root.style.setProperty('display', 'none', 'important');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
populate(netFilters, '#netFilters');
|
||||||
|
populate(cosmeticFilters, '#cosmeticFilters');
|
||||||
|
|
||||||
|
dialog.querySelector('ul').style.display =
|
||||||
|
netFilters.length || cosmeticFilters.length ? '' : 'none';
|
||||||
|
dialog.querySelector('#create').disabled = true;
|
||||||
|
|
||||||
|
// Auto-select a candidate filter
|
||||||
|
|
||||||
|
// 2020-09-01:
|
||||||
|
// In Firefox, `details instanceof Object` resolves to `false` despite
|
||||||
|
// `details` being a valid object. Consequently, falling back to use
|
||||||
|
// `typeof details`.
|
||||||
|
// This is an issue which surfaced when the element picker code was
|
||||||
|
// revisited to isolate the picker dialog DOM from the page DOM.
|
||||||
|
if ( typeof filter !== 'object' || filter === null ) {
|
||||||
|
taCandidate.value = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filterChoice = {
|
||||||
|
filters: filter.filters,
|
||||||
|
slot: filter.slot,
|
||||||
|
modifier: options.modifier || false
|
||||||
|
};
|
||||||
|
|
||||||
|
taCandidate.value = candidateFromFilterChoice(filterChoice);
|
||||||
|
onCandidateChanged();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Let's have the element picker code flushed from memory when no longer
|
||||||
|
// in use: to ensure this, release all local references.
|
||||||
|
|
||||||
|
const stopPicker = function() {
|
||||||
|
vAPI.shutdown.remove(stopPicker);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const pickerBody = document.body;
|
||||||
|
const dialog = $stor('aside');
|
||||||
|
const taCandidate = $stor('textarea');
|
||||||
|
let staticFilteringParser;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const startDialog = function() {
|
||||||
|
dialog.addEventListener('click', eatEvent);
|
||||||
|
taCandidate.addEventListener('input', onCandidateChanged);
|
||||||
|
$stor('body').addEventListener('mousedown', onPickClicked);
|
||||||
|
$id('preview').addEventListener('click', onPreviewClicked);
|
||||||
|
$id('create').addEventListener('click', onCreateClicked);
|
||||||
|
$id('pick').addEventListener('click', onPickClicked);
|
||||||
|
$id('quit').addEventListener('click', onQuitClicked);
|
||||||
|
$id('candidateFilters').addEventListener('click', onCandidateClicked);
|
||||||
|
$id('toolbar').addEventListener('mousedown', onStartMoving);
|
||||||
|
self.addEventListener('keydown', onKeyPressed, true);
|
||||||
|
staticFilteringParser = new vAPI.StaticFilteringParser({ interactive: true });
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onPickerMessage = function(msg) {
|
||||||
|
switch ( msg.what ) {
|
||||||
|
case 'showDialog':
|
||||||
|
showDialog(msg);
|
||||||
|
break;
|
||||||
|
case 'filterResultset':
|
||||||
|
filterResultset = msg.resultset;
|
||||||
|
$id('resultsetCount').textContent = filterResultset.length;
|
||||||
|
if ( filterResultset.length !== 0 ) {
|
||||||
|
$id('create').removeAttribute('disabled');
|
||||||
|
} else {
|
||||||
|
$id('create').setAttribute('disabled', '');
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const onConnectionMessage = function(msg) {
|
||||||
|
switch ( msg.what ) {
|
||||||
|
case 'connectionBroken':
|
||||||
|
stopPicker();
|
||||||
|
break;
|
||||||
|
case 'connectionMessage':
|
||||||
|
onPickerMessage(msg.payload);
|
||||||
|
break;
|
||||||
|
case 'connectionAccepted':
|
||||||
|
epickerConnectionId = msg.id;
|
||||||
|
startDialog();
|
||||||
|
vAPI.MessagingConnection.sendTo(epickerConnectionId, {
|
||||||
|
what: 'dialogInit',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.MessagingConnection.connectTo(
|
||||||
|
`epickerDialog-${epickerId}`,
|
||||||
|
`epicker-${epickerId}`,
|
||||||
|
onConnectionMessage
|
||||||
|
);
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
DO NOT:
|
||||||
|
- Remove the following code
|
||||||
|
- Add code beyond the following code
|
||||||
|
Reason:
|
||||||
|
- https://github.com/gorhill/uBlock/pull/3721
|
||||||
|
- uBO never uses the return value from injected content scripts
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
void 0;
|
|
@ -713,34 +713,19 @@ const onMessage = function(request, sender, callback) {
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'elementPickerArguments':
|
case 'elementPickerArguments':
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', 'epicker.html', true);
|
xhr.open('GET', 'css/epicker.css', true);
|
||||||
xhr.overrideMimeType('text/html;charset=utf-8');
|
xhr.overrideMimeType('text/html;charset=utf-8');
|
||||||
xhr.responseType = 'text';
|
xhr.responseType = 'text';
|
||||||
xhr.onload = function() {
|
xhr.onload = function() {
|
||||||
this.onload = null;
|
this.onload = null;
|
||||||
const i18n = {
|
|
||||||
bidi_dir: document.body.getAttribute('dir'),
|
|
||||||
create: vAPI.i18n('pickerCreate'),
|
|
||||||
pick: vAPI.i18n('pickerPick'),
|
|
||||||
quit: vAPI.i18n('pickerQuit'),
|
|
||||||
preview: vAPI.i18n('pickerPreview'),
|
|
||||||
netFilters: vAPI.i18n('pickerNetFilters'),
|
|
||||||
cosmeticFilters: vAPI.i18n('pickerCosmeticFilters'),
|
|
||||||
cosmeticFiltersHint: vAPI.i18n('pickerCosmeticFiltersHint')
|
|
||||||
};
|
|
||||||
const reStrings = /\{\{(\w+)\}\}/g;
|
|
||||||
const replacer = function(a0, string) {
|
|
||||||
return i18n[string];
|
|
||||||
};
|
|
||||||
|
|
||||||
callback({
|
callback({
|
||||||
frameContent: this.responseText.replace(reStrings, replacer),
|
frameCSS: this.responseText,
|
||||||
target: µb.epickerArgs.target,
|
target: µb.epickerArgs.target,
|
||||||
mouse: µb.epickerArgs.mouse,
|
mouse: µb.epickerArgs.mouse,
|
||||||
zap: µb.epickerArgs.zap,
|
zap: µb.epickerArgs.zap,
|
||||||
eprom: µb.epickerArgs.eprom,
|
eprom: µb.epickerArgs.eprom,
|
||||||
|
dialogURL: vAPI.getURL(`/web_accessible_resources/epicker-dialog.html${vAPI.warSecret()}`),
|
||||||
});
|
});
|
||||||
|
|
||||||
µb.epickerArgs.target = '';
|
µb.epickerArgs.target = '';
|
||||||
};
|
};
|
||||||
xhr.send();
|
xhr.send();
|
||||||
|
@ -754,21 +739,6 @@ const onMessage = function(request, sender, callback) {
|
||||||
let response;
|
let response;
|
||||||
|
|
||||||
switch ( request.what ) {
|
switch ( request.what ) {
|
||||||
case 'compileCosmeticFilterSelector': {
|
|
||||||
const parser = new vAPI.StaticFilteringParser();
|
|
||||||
parser.analyze(request.selector);
|
|
||||||
if ( (parser.flavorBits & parser.BITFlavorExtCosmetic) !== 0 ) {
|
|
||||||
response = parser.result.compiled;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/3497
|
|
||||||
// This needs to be removed once issue is fixed.
|
|
||||||
case 'createUserFilter':
|
|
||||||
µb.createUserFilters(request);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'elementPickerEprom':
|
case 'elementPickerEprom':
|
||||||
µb.epickerArgs.eprom = request;
|
µb.epickerArgs.eprom = request;
|
||||||
break;
|
break;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -428,7 +428,7 @@ const matchBucket = function(url, hostname, bucket, start) {
|
||||||
});
|
});
|
||||||
|
|
||||||
await vAPI.tabs.executeScript(tabId, {
|
await vAPI.tabs.executeScript(tabId, {
|
||||||
file: '/js/scriptlets/element-picker.js',
|
file: '/js/scriptlets/epicker.js',
|
||||||
runAt: 'document_end',
|
runAt: 'document_end',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
48
src/web_accessible_resources/epicker-dialog.html
Normal file
48
src/web_accessible_resources/epicker-dialog.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html id="ublock0-epicker">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>uBlock Origin Element Picker</title>
|
||||||
|
<link rel="stylesheet" href="../css/epicker-dialog.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<aside>
|
||||||
|
<section>
|
||||||
|
<div>
|
||||||
|
<textarea lang="en" dir="ltr" spellcheck="false"></textarea>
|
||||||
|
<div id="resultsetCount"></div>
|
||||||
|
</div>
|
||||||
|
<div id="toolbar">
|
||||||
|
<div>
|
||||||
|
<button id="preview" type="button" data-i18n="pickerPreview"></button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="create" type="button" disabled data-i18n="pickerCreate"></button>
|
||||||
|
<button id="pick" type="button" data-i18n="pickerPick"></button>
|
||||||
|
<button id="quit" type="button" data-i18n="pickerQuit"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<ul id="candidateFilters">
|
||||||
|
<li id="netFilters">
|
||||||
|
<span data-i18n="pickerNetFilters"></span><ul lang="en" class="changeFilter"></ul>
|
||||||
|
</li>
|
||||||
|
<li id="cosmeticFilters">
|
||||||
|
<span data-i18n="pickerCosmeticFilters"></span> <span data-i18n="pickerCosmeticFiltersHint"></span>
|
||||||
|
<ul lang="en" class="changeFilter"></ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<script src="../js/vapi.js"></script>
|
||||||
|
<script src="../js/vapi-common.js"></script>
|
||||||
|
<script src="../js/vapi-client.js"></script>
|
||||||
|
<script src="../js/vapi-client-extra.js"></script>
|
||||||
|
<script src="../js/i18n.js"></script>
|
||||||
|
<script src="../js/epicker-dialog.js"></script>
|
||||||
|
<script src="../js/static-filtering-parser.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue