mirror of https://github.com/gorhill/uBlock.git
Merge background changes to user filters in "My filters" pane
Related issue: - https://github.com/gorhill/uBlock/issues/3704
This commit is contained in:
parent
b7d41ec85a
commit
e85c6f2d3e
|
@ -49,6 +49,7 @@
|
||||||
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||||
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
||||||
<script src="lib/codemirror/addon/selection/active-line.js"></script>
|
<script src="lib/codemirror/addon/selection/active-line.js"></script>
|
||||||
|
<script src="lib/diff/swatinem_diff.js"></script>
|
||||||
<script src="lib/regexanalyzer/regex.js"></script>
|
<script src="lib/regexanalyzer/regex.js"></script>
|
||||||
|
|
||||||
<script src="js/codemirror/search.js"></script>
|
<script src="js/codemirror/search.js"></script>
|
||||||
|
@ -59,6 +60,7 @@
|
||||||
<script src="js/vapi.js"></script>
|
<script src="js/vapi.js"></script>
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-client.js"></script>
|
<script src="js/vapi-client.js"></script>
|
||||||
|
<script src="js/vapi-client-extra.js"></script>
|
||||||
<script src="js/udom.js"></script>
|
<script src="js/udom.js"></script>
|
||||||
<script src="js/i18n.js"></script>
|
<script src="js/i18n.js"></script>
|
||||||
<script src="js/dashboard-common.js"></script>
|
<script src="js/dashboard-common.js"></script>
|
||||||
|
|
|
@ -108,17 +108,72 @@ const userFiltersChanged = function(changed) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const renderUserFilters = async function() {
|
// https://github.com/gorhill/uBlock/issues/3704
|
||||||
|
// Merge changes to user filters occurring in the background with changes
|
||||||
|
// made in the editor. The code assumes that no deletion occurred in the
|
||||||
|
// background.
|
||||||
|
|
||||||
|
const threeWayMerge = function(newContent) {
|
||||||
|
const prvContent = cachedUserFilters.trim().split(/\n/);
|
||||||
|
const differ = new self.diff_match_patch();
|
||||||
|
const newChanges = differ.diff(
|
||||||
|
prvContent,
|
||||||
|
newContent.trim().split(/\n/)
|
||||||
|
);
|
||||||
|
const usrChanges = differ.diff(
|
||||||
|
prvContent,
|
||||||
|
getEditorText().trim().split(/\n/)
|
||||||
|
);
|
||||||
|
const out = [];
|
||||||
|
let i = 0, j = 0, k = 0;
|
||||||
|
while ( i < prvContent.length ) {
|
||||||
|
for ( ; j < newChanges.length; j++ ) {
|
||||||
|
const change = newChanges[j];
|
||||||
|
if ( change[0] !== 1 ) { break; }
|
||||||
|
out.push(change[1]);
|
||||||
|
}
|
||||||
|
for ( ; k < usrChanges.length; k++ ) {
|
||||||
|
const change = usrChanges[k];
|
||||||
|
if ( change[0] !== 1 ) { break; }
|
||||||
|
out.push(change[1]);
|
||||||
|
}
|
||||||
|
if ( k === usrChanges.length || usrChanges[k][0] !== -1 ) {
|
||||||
|
out.push(prvContent[i]);
|
||||||
|
}
|
||||||
|
i += 1; j += 1; k += 1;
|
||||||
|
}
|
||||||
|
for ( ; j < newChanges.length; j++ ) {
|
||||||
|
const change = newChanges[j];
|
||||||
|
if ( change[0] !== 1 ) { continue; }
|
||||||
|
out.push(change[1]);
|
||||||
|
}
|
||||||
|
for ( ; k < usrChanges.length; k++ ) {
|
||||||
|
const change = usrChanges[k];
|
||||||
|
if ( change[0] !== 1 ) { continue; }
|
||||||
|
out.push(change[1]);
|
||||||
|
}
|
||||||
|
return out.join('\n');
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
const renderUserFilters = async function(merge = false) {
|
||||||
const details = await vAPI.messaging.send('dashboard', {
|
const details = await vAPI.messaging.send('dashboard', {
|
||||||
what: 'readUserFilters',
|
what: 'readUserFilters',
|
||||||
});
|
});
|
||||||
if ( details instanceof Object === false || details.error ) { return; }
|
if ( details instanceof Object === false || details.error ) { return; }
|
||||||
|
|
||||||
let content = details.content.trim();
|
const newContent = details.content.trim();
|
||||||
cachedUserFilters = content;
|
|
||||||
setEditorText(content);
|
|
||||||
|
|
||||||
|
if ( merge && self.hasUnsavedData() ) {
|
||||||
|
setEditorText(threeWayMerge(newContent));
|
||||||
|
userFiltersChanged(true);
|
||||||
|
} else {
|
||||||
|
setEditorText(newContent);
|
||||||
userFiltersChanged(false);
|
userFiltersChanged(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedUserFilters = newContent;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -240,21 +295,26 @@ uDom('#exportUserFiltersToFile').on('click', exportUserFiltersToFile);
|
||||||
uDom('#userFiltersApply').on('click', ( ) => { applyChanges(); });
|
uDom('#userFiltersApply').on('click', ( ) => { applyChanges(); });
|
||||||
uDom('#userFiltersRevert').on('click', revertChanges);
|
uDom('#userFiltersRevert').on('click', revertChanges);
|
||||||
|
|
||||||
|
(async ( ) => {
|
||||||
|
await renderUserFilters();
|
||||||
|
|
||||||
|
cmEditor.clearHistory();
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/3706
|
// https://github.com/gorhill/uBlock/issues/3706
|
||||||
// Save/restore cursor position
|
// Save/restore cursor position
|
||||||
//
|
|
||||||
// CodeMirror reference: https://codemirror.net/doc/manual.html#api_selection
|
|
||||||
{
|
{
|
||||||
let curline = 0;
|
const line =
|
||||||
let timer;
|
await vAPI.localStorage.getItemAsync('myFiltersCursorPosition');
|
||||||
|
|
||||||
renderUserFilters().then(( ) => {
|
|
||||||
cmEditor.clearHistory();
|
|
||||||
return vAPI.localStorage.getItemAsync('myFiltersCursorPosition');
|
|
||||||
}).then(line => {
|
|
||||||
if ( typeof line === 'number' ) {
|
if ( typeof line === 'number' ) {
|
||||||
cmEditor.setCursor(line, 0);
|
cmEditor.setCursor(line, 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/3706
|
||||||
|
// Save/restore cursor position
|
||||||
|
{
|
||||||
|
let curline = 0;
|
||||||
|
let timer;
|
||||||
cmEditor.on('cursorActivity', ( ) => {
|
cmEditor.on('cursorActivity', ( ) => {
|
||||||
if ( timer !== undefined ) { return; }
|
if ( timer !== undefined ) { return; }
|
||||||
if ( cmEditor.getCursor().line === curline ) { return; }
|
if ( cmEditor.getCursor().line === curline ) { return; }
|
||||||
|
@ -264,9 +324,30 @@ uDom('#userFiltersRevert').on('click', revertChanges);
|
||||||
vAPI.localStorage.setItem('myFiltersCursorPosition', curline);
|
vAPI.localStorage.setItem('myFiltersCursorPosition', curline);
|
||||||
}, 701);
|
}, 701);
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/3704
|
||||||
|
// Merge changes to user filters occurring in the background
|
||||||
|
vAPI.broadcastListener.add(msg => {
|
||||||
|
switch ( msg.what ) {
|
||||||
|
case 'userFiltersUpdated': {
|
||||||
|
cmEditor.startOperation();
|
||||||
|
const scroll = cmEditor.getScrollInfo();
|
||||||
|
const selections = cmEditor.listSelections();
|
||||||
|
renderUserFilters(true).then(( ) => {
|
||||||
|
cmEditor.clearHistory();
|
||||||
|
cmEditor.setSelection(selections[0].anchor, selections[0].head);
|
||||||
|
cmEditor.scrollTo(scroll.left, scroll.top);
|
||||||
|
cmEditor.endOperation();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
cmEditor.on('changes', userFiltersChanged);
|
cmEditor.on('changes', userFiltersChanged);
|
||||||
CodeMirror.commands.save = applyChanges;
|
CodeMirror.commands.save = applyChanges;
|
||||||
|
|
||||||
|
|
|
@ -331,14 +331,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
||||||
this.netWhitelistModifyTime = Date.now();
|
this.netWhitelistModifyTime = Date.now();
|
||||||
};
|
};
|
||||||
|
|
||||||
/*******************************************************************************
|
/******************************************************************************/
|
||||||
|
|
||||||
TODO(seamless migration):
|
|
||||||
The code related to 'remoteBlacklist' can be removed when I am confident
|
|
||||||
all users have moved to a version of uBO which no longer depends on
|
|
||||||
the property 'remoteBlacklists, i.e. v1.11 and beyond.
|
|
||||||
|
|
||||||
**/
|
|
||||||
|
|
||||||
µBlock.loadSelectedFilterLists = async function() {
|
µBlock.loadSelectedFilterLists = async function() {
|
||||||
const bin = await vAPI.storage.get('selectedFilterLists');
|
const bin = await vAPI.storage.get('selectedFilterLists');
|
||||||
|
@ -559,6 +552,8 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
||||||
if ( options.killCache ) {
|
if ( options.killCache ) {
|
||||||
browser.webRequest.handlerBehaviorChanged();
|
browser.webRequest.handlerBehaviorChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vAPI.messaging.broadcast({ what: 'userFiltersUpdated' });
|
||||||
};
|
};
|
||||||
|
|
||||||
µBlock.createUserFilters = function(details) {
|
µBlock.createUserFilters = function(details) {
|
||||||
|
@ -1281,7 +1276,6 @@ self.addEventListener('hiddenSettingsChanged', ( ) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const destroy = function() {
|
const destroy = function() {
|
||||||
µb.cacheStorage.remove('selfie'); // TODO: obsolete, remove eventually.
|
|
||||||
µb.assets.remove(/^selfie\//);
|
µb.assets.remove(/^selfie\//);
|
||||||
µb.selfieIsInvalid = true;
|
µb.selfieIsInvalid = true;
|
||||||
createTimer = vAPI.setTimeout(( ) => {
|
createTimer = vAPI.setTimeout(( ) => {
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
diff_match_patch, as expected by CodeMirror.
|
diff_match_patch, as expected by CodeMirror.
|
||||||
|
|
||||||
2018-12-20 gorhill:
|
2018-12-20 gorhill:
|
||||||
|
===================
|
||||||
There was an issue causing the wrong diff data to be issued, for instance
|
There was an issue causing the wrong diff data to be issued, for instance
|
||||||
when diff-ing these two URLs on a character granularity basis (failure
|
when diff-ing these two URLs on a character granularity basis (failure
|
||||||
point is marked):
|
point is marked):
|
||||||
|
@ -60,8 +61,10 @@
|
||||||
|
|
||||||
So I will assume this was the issue.
|
So I will assume this was the issue.
|
||||||
|
|
||||||
TODO:
|
2021-07-17 gorhill:
|
||||||
- Apply other changes which were applied to the original code
|
===================
|
||||||
|
Added pure diff() method which natively deals with arrays and other minor
|
||||||
|
changes related to ES6.
|
||||||
|
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
@ -77,15 +80,20 @@
|
||||||
|
|
||||||
context.diff_match_patch.prototype.diff_main = function(a, b) {
|
context.diff_match_patch.prototype.diff_main = function(a, b) {
|
||||||
if ( a === b ) { return [ [ 0, a ] ]; }
|
if ( a === b ) { return [ [ 0, a ] ]; }
|
||||||
var aa = a.match(/\n|[^\n]+\n?/g) || [];
|
const aa = a.match(/\n|[^\n]+\n?/g) || [];
|
||||||
var bb = b.match(/\n|[^\n]+\n?/g) || [];
|
const bb = b.match(/\n|[^\n]+\n?/g) || [];
|
||||||
var d = new Diff(aa, bb, eqlDefault);
|
const d = new Diff(aa, bb);
|
||||||
return d.editscript();
|
return d.editscript();
|
||||||
};
|
};
|
||||||
|
|
||||||
function eqlDefault(a, b) { return a === b; }
|
context.diff_match_patch.prototype.diff = function(a, b) {
|
||||||
|
const d = new Diff(a, b);
|
||||||
|
return d.editscript();
|
||||||
|
};
|
||||||
|
|
||||||
function Diff(a, b, eql) {
|
const eqlDefault = (a, b) => a === b;
|
||||||
|
|
||||||
|
function Diff(a, b, eql = eqlDefault) {
|
||||||
this.a = a;
|
this.a = a;
|
||||||
this.b = b;
|
this.b = b;
|
||||||
this.eql = eql;
|
this.eql = eql;
|
||||||
|
@ -101,10 +109,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
Diff.prototype.editscript = function Diff_editscript() {
|
Diff.prototype.editscript = function Diff_editscript() {
|
||||||
var moda = this.moda, modb = this.modb;
|
const moda = this.moda, modb = this.modb;
|
||||||
var astart = 0, aend = moda.length;
|
var astart = 0, aend = moda.length;
|
||||||
var bstart = 0, bend = modb.length;
|
var bstart = 0, bend = modb.length;
|
||||||
var result = [];
|
const result = [];
|
||||||
while (astart < aend || bstart < bend) {
|
while (astart < aend || bstart < bend) {
|
||||||
if (astart < aend && bstart < bend) {
|
if (astart < aend && bstart < bend) {
|
||||||
if (!moda[astart] && !modb[bstart]) {
|
if (!moda[astart] && !modb[bstart]) {
|
||||||
|
@ -131,7 +139,7 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Diff.prototype.lcs = function Diff_lcs(astart, aend, bstart, bend) {
|
Diff.prototype.lcs = function Diff_lcs(astart, aend, bstart, bend) {
|
||||||
var a = this.a, b = this.b, eql = this.eql;
|
const a = this.a, b = this.b, eql = this.eql;
|
||||||
// separate common head
|
// separate common head
|
||||||
while (astart < aend && bstart < bend && eql(a[astart], b[bstart])) {
|
while (astart < aend && bstart < bend && eql(a[astart], b[bstart])) {
|
||||||
astart++; bstart++;
|
astart++; bstart++;
|
||||||
|
@ -154,7 +162,7 @@
|
||||||
astart++;
|
astart++;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var snake = this.snake(astart, aend, bstart, bend);
|
const snake = this.snake(astart, aend, bstart, bend);
|
||||||
|
|
||||||
this.lcs(astart, snake.x, bstart, snake.y);
|
this.lcs(astart, snake.x, bstart, snake.y);
|
||||||
this.lcs(snake.x, aend, snake.y, bend);
|
this.lcs(snake.x, aend, snake.y, bend);
|
||||||
|
@ -162,27 +170,27 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
Diff.prototype.snake = function Diff_snake(astart, aend, bstart, bend) {
|
Diff.prototype.snake = function Diff_snake(astart, aend, bstart, bend) {
|
||||||
var a = this.a, b = this.b, eql = this.eql;
|
const a = this.a, b = this.b, eql = this.eql;
|
||||||
|
|
||||||
var N = aend - astart,
|
const N = aend - astart;
|
||||||
M = bend - bstart;
|
const M = bend - bstart;
|
||||||
|
|
||||||
var kdown = astart - bstart;
|
const kdown = astart - bstart;
|
||||||
var kup = aend - bend;
|
const kup = aend - bend;
|
||||||
|
|
||||||
var delta = N - M;
|
const delta = N - M;
|
||||||
var deltaOdd = delta & 1;
|
const deltaOdd = delta & 1;
|
||||||
|
|
||||||
var down = this.down;
|
const down = this.down;
|
||||||
down[kdown + 1] = astart;
|
down[kdown + 1] = astart;
|
||||||
var up = this.up;
|
const up = this.up;
|
||||||
up[kup - 1] = aend;
|
up[kup - 1] = aend;
|
||||||
|
|
||||||
var Dmax = (N + M + 1) / 2;
|
const Dmax = (N + M + 1) / 2;
|
||||||
for (var D = 0; D <= Dmax; D++) {
|
for (let D = 0; D <= Dmax; D++) {
|
||||||
var k, x, y;
|
|
||||||
// forward path
|
// forward path
|
||||||
for (k = kdown - D; k <= kdown + D; k += 2) {
|
for (let k = kdown - D; k <= kdown + D; k += 2) {
|
||||||
|
let x;
|
||||||
if (k === kdown - D) {
|
if (k === kdown - D) {
|
||||||
x = down[k + 1]; // down
|
x = down[k + 1]; // down
|
||||||
} else {
|
} else {
|
||||||
|
@ -191,7 +199,7 @@
|
||||||
x = down[k + 1]; // down
|
x = down[k + 1]; // down
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
y = x - k;
|
let y = x - k;
|
||||||
|
|
||||||
while (x < aend && y < bend && eql(a[x], b[y])) {
|
while (x < aend && y < bend && eql(a[x], b[y])) {
|
||||||
x++; y++; // diagonal
|
x++; y++; // diagonal
|
||||||
|
@ -210,7 +218,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// reverse path
|
// reverse path
|
||||||
for (k = kup - D; k <= kup + D; k += 2) {
|
for (let k = kup - D; k <= kup + D; k += 2) {
|
||||||
|
let x;
|
||||||
if (k === kup + D) {
|
if (k === kup + D) {
|
||||||
x = up[k - 1]; // up
|
x = up[k - 1]; // up
|
||||||
} else {
|
} else {
|
||||||
|
@ -219,7 +228,7 @@
|
||||||
x = up[k - 1]; // up
|
x = up[k - 1]; // up
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
y = x - k;
|
let y = x - k;
|
||||||
|
|
||||||
while (x > astart && y > bstart && eql(a[x - 1], b[y - 1])) {
|
while (x > astart && y > bstart && eql(a[x - 1], b[y - 1])) {
|
||||||
x--; y--; // diagonal
|
x--; y--; // diagonal
|
||||||
|
|
Loading…
Reference in New Issue