#731: added session and permanent firewalls

This commit is contained in:
gorhill 2015-02-11 00:26:45 -05:00
parent 9a75572d8c
commit 646f92b32f
9 changed files with 227 additions and 107 deletions

View file

@ -165,14 +165,14 @@ body.dirty #refresh:hover {
color: black;
}
#dynamicFilteringContainer {
#firewallContainer {
border: 0;
font-size: 12px;
margin: 0;
padding: 0;
text-align: right;
}
#dynamicFilteringContainer > div {
#firewallContainer > div {
background-color: #e6e6e6;
border: 0;
border-bottom: 1px solid white;
@ -180,10 +180,10 @@ body.dirty #refresh:hover {
margin: 0;
padding: 0;
}
#dynamicFilteringContainer > div:hover {
#firewallContainer > div:hover {
background-color: #f0f0f0;
}
#dynamicFilteringContainer > div > span {
#firewallContainer > div > span {
background-color: transparent;
border: none;
box-sizing: border-box;
@ -196,58 +196,58 @@ body.dirty #refresh:hover {
position: relative;
vertical-align: middle;
}
#dynamicFilteringContainer > div:nth-of-type(1) > span {
#firewallContainer > div:nth-of-type(1) > span {
height: 18px;
line-height: 18px;
}
#dynamicFilteringContainer > div > span:nth-of-type(1) {
#firewallContainer > div > span:nth-of-type(1) {
border-right: 1px solid white;
padding-right: 2px;
text-overflow: ellipsis;
width: 70%;
}
#dynamicFilteringContainer > div > span:nth-of-type(2) {
#firewallContainer > div > span:nth-of-type(2) {
cursor: pointer;
width: 15%;
}
#dynamicFilteringContainer > div > span:nth-of-type(3) {
#firewallContainer > div > span:nth-of-type(3) {
border-left: 1px solid white;
color: #444;
cursor: pointer;
text-align: center;
width: 15%;
}
#dynamicFilteringContainer > div.isDomain > span:nth-of-type(1) {
#firewallContainer > div.isDomain > span:nth-of-type(1) {
font-weight: bold;
}
#dynamicFilteringContainer > div.allowed > span:nth-of-type(1) {
#firewallContainer > div.allowed > span:nth-of-type(1) {
background-color: rgba(0, 160, 0, 0.1);
}
#dynamicFilteringContainer > div.blocked > span:nth-of-type(1) {
#firewallContainer > div.blocked > span:nth-of-type(1) {
background-color: rgba(192, 0, 0, 0.1);
}
#dynamicFilteringContainer > div.allowed.blocked > span:nth-of-type(1) {
#firewallContainer > div.allowed.blocked > span:nth-of-type(1) {
background-color: rgba(192, 160, 0, 0.1);
}
#dynamicFilteringContainer > div > span.aRule {
#firewallContainer > div > span.aRule {
background-color: rgba(0, 160, 0, 0.3);
}
#dynamicFilteringContainer > div > span.bRule {
#firewallContainer > div > span.bRule {
background-color: rgba(192, 0, 0, 0.3);
}
#dynamicFilteringContainer > div > span.nRule {
#firewallContainer > div > span.nRule {
background-color: rgba(96, 96, 96, 0.3);
}
#dynamicFilteringContainer > div > span.ownRule {
#firewallContainer > div > span.ownRule {
color: white;
}
#dynamicFilteringContainer > div > span.aRule.ownRule {
#firewallContainer > div > span.aRule.ownRule {
background-color: rgba(0, 160, 0, 1);
}
#dynamicFilteringContainer > div > span.bRule.ownRule {
#firewallContainer > div > span.bRule.ownRule {
background-color: rgba(192, 0, 0, 1);
}
#dynamicFilteringContainer > div > span.nRule.ownRule {
#firewallContainer > div > span.nRule.ownRule {
background-color: rgba(108, 108, 108, 1);
}
@ -276,23 +276,28 @@ body.dirty #refresh:hover {
#actionSelector > span:nth-of-type(3) {
background-color: rgb(192, 0, 0);
}
#dynamicFilteringContainer span.aRule #actionSelector > span:nth-of-type(1),
#dynamicFilteringContainer span.nRule #actionSelector > span:nth-of-type(2),
#dynamicFilteringContainer span.bRule #actionSelector > span:nth-of-type(3) {
#firewallContainer span.aRule #actionSelector > span:nth-of-type(1),
#firewallContainer span.nRule #actionSelector > span:nth-of-type(2),
#firewallContainer span.bRule #actionSelector > span:nth-of-type(3) {
visibility: hidden;
}
#offOverlay {
background-color: #fff;
bottom: 0;
#saveRules {
background-color: #ffe;
border: 1px solid #eec;
border-radius: 4px;
color: #888;
cursor: pointer;
display: none;
left: 0;
opacity: 0.4;
pointer-events: none;
position: absolute;
right: 0;
top: 0;
font-size: 30px;
line-height: 40px;
padding: 0.1em 0.4em;
position: fixed;
text-align: center;
}
body.off #offOverlay {
#firewallContainer.dirty ~ #saveRules {
display: block;
}
#firewallContainer.dirty ~ #saveRules:hover {
color: black;
}

View file

@ -58,7 +58,6 @@ return {
collapseBlocked: true,
contextMenuEnabled: true,
dynamicFilteringString: '',
dynamicFilteringSelfie: '',
dynamicFilteringEnabled: false,
experimentalEnabled: false,
externalLists: defaultExternalLists,

View file

@ -24,7 +24,7 @@
/******************************************************************************/
µBlock.dynamicNetFilteringEngine = (function() {
µBlock.Firewall = (function() {
/******************************************************************************/
@ -119,6 +119,97 @@ Matrix.prototype.reset = function() {
/******************************************************************************/
Matrix.prototype.assign = function(other) {
var thisRules = this.rules;
var otherRules = other.rules;
var k;
// Remove rules not in other
for ( k in thisRules ) {
if ( thisRules.hasOwnProperty(k) === false ) {
continue;
}
if ( otherRules.hasOwnProperty(k) === false ) {
delete thisRules[k];
}
}
// Add/change rules in other
for ( k in otherRules ) {
if ( otherRules.hasOwnProperty(k) === false ) {
continue;
}
thisRules[k] = otherRules[k];
}
};
/******************************************************************************/
Matrix.prototype.copyRules = function(other, srcHostname, desHostnames) {
var thisRules = this.rules;
var otherRules = other.rules;
// Specific types
thisRules['* *'] = otherRules['* *'];
var ruleKey = srcHostname + ' *';
thisRules[ruleKey] = otherRules[ruleKey];
// Specific destinations
for ( var desHostname in desHostnames ) {
if ( desHostnames.hasOwnProperty(desHostname) === false ) {
continue;
}
ruleKey = '* ' + desHostname;
thisRules[ruleKey] = otherRules[ruleKey];
ruleKey = srcHostname + ' ' + desHostname ;
thisRules[ruleKey] = otherRules[ruleKey];
}
return true;
};
/******************************************************************************/
// - * * type
// - from * type
// - * to *
// - from to *
Matrix.prototype.hasSameRules = function(other, srcHostname, desHostnames) {
var thisRules = this.rules;
var otherRules = other.rules;
var ruleKey;
// Specific types
ruleKey = '* *';
if ( thisRules[ruleKey] !== otherRules[ruleKey] ) {
return false;
}
ruleKey = srcHostname + ' *';
if ( thisRules[ruleKey] !== otherRules[ruleKey] ) {
return false;
}
// Specific destinations
for ( var desHostname in desHostnames ) {
if ( desHostnames.hasOwnProperty(desHostname) === false ) {
continue;
}
ruleKey = '* ' + desHostname;
if ( thisRules[ruleKey] !== otherRules[ruleKey] ) {
return false;
}
ruleKey = srcHostname + ' ' + desHostname ;
if ( thisRules[ruleKey] !== otherRules[ruleKey] ) {
return false;
}
}
return true;
};
/******************************************************************************/
Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) {
var bitOffset = typeBitOffsets[type];
var k = srcHostname + ' ' + desHostname;
@ -501,7 +592,7 @@ Matrix.prototype.fromSelfie = function(selfie) {
/******************************************************************************/
return new Matrix();
return Matrix;
/******************************************************************************/
@ -510,3 +601,8 @@ return new Matrix();
})();
/******************************************************************************/
µBlock.sessionFirewall = new µBlock.Firewall();
µBlock.permanentFirewall = new µBlock.Firewall();
/******************************************************************************/

View file

@ -142,9 +142,9 @@ var getHostnameDict = function(hostnameToCountMap) {
/******************************************************************************/
var getDynamicFilterRules = function(srcHostname, desHostnames) {
var getFirewallRules = function(srcHostname, desHostnames) {
var r = {};
var dFiltering = µb.dynamicNetFilteringEngine;
var dFiltering = µb.sessionFirewall;
r['/ * *'] = dFiltering.evaluateCellZY('*', '*', '*').toFilterString();
r['/ * image'] = dFiltering.evaluateCellZY('*', '*', 'image').toFilterString();
r['/ * 3p'] = dFiltering.evaluateCellZY('*', '*', '3p').toFilterString();
@ -200,12 +200,19 @@ var getStats = function(tabId) {
r.netFilteringSwitch = pageStore.getNetFilteringSwitch();
r.hostnameDict = getHostnameDict(pageStore.hostnameToCountMap);
r.contentLastModified = pageStore.contentLastModified;
r.dynamicFilterRules = getDynamicFilterRules(pageStore.pageHostname, r.hostnameDict);
r.firewallRules = getFirewallRules(pageStore.pageHostname, r.hostnameDict);
r.canElementPicker = r.pageHostname.indexOf('.') !== -1;
r.canRequestLog = canRequestLog;
} else {
r.hostnameDict = {};
r.dynamicFilterRules = getDynamicFilterRules();
r.firewallRules = getFirewallRules();
}
if ( r.pageHostname ) {
r.matrixIsDirty = !µb.sessionFirewall.hasSameRules(
µb.permanentFirewall,
r.pageHostname,
r.hostnameDict
);
}
return r;
};
@ -275,8 +282,17 @@ var onMessage = function(request, sender, callback) {
response = lastModified !== request.contentLastModified;
break;
case 'toggleDynamicFilter':
µb.toggleDynamicFilter(request);
case 'saveRules':
µb.permanentFirewall.copyRules(
µb.sessionFirewall,
request.srcHostname,
request.desHostnames
);
µb.savePermanentFirewallRules();
break;
case 'toggleFirewallRule':
µb.toggleFirewallRule(request);
response = getStats(request.tabId);
break;
@ -723,13 +739,13 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'getDynamicRules':
response = µb.dynamicNetFilteringEngine.toString();
response = µb.permanentFirewall.toString();
break;
case 'setDynamicRules':
µb.dynamicNetFilteringEngine.fromString(request.rawRules);
µb.saveDynamicRules();
response = µb.dynamicNetFilteringEngine.toString();
µb.permanentFirewall.fromString(request.rawRules);
µb.savePermanentFirewallRules();
response = µb.permanentFirewall.toString();
break;
default:

View file

@ -604,7 +604,7 @@ PageStore.prototype.getNetFilteringSwitch = function() {
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
return this.getNetFilteringSwitch() &&
(µb.userSettings.advancedUserEnabled &&
µb.dynamicNetFilteringEngine.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
};
/******************************************************************************/
@ -613,7 +613,7 @@ PageStore.prototype.getGenericCosmeticFilteringSwitch = function() {
return this.getNetFilteringSwitch() &&
this.skipCosmeticFiltering === false &&
(µb.userSettings.advancedUserEnabled &&
µb.dynamicNetFilteringEngine.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
};
/******************************************************************************/
@ -648,7 +648,7 @@ PageStore.prototype.filterRequest = function(context) {
// We evaluate dynamic filtering first, and hopefully we can skip
// evaluation of static filtering.
if ( µb.userSettings.advancedUserEnabled ) {
var df = µb.dynamicNetFilteringEngine.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType);
var df = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType);
if ( df.mustBlockOrAllow() ) {
result = df.toFilterString();
}
@ -689,8 +689,7 @@ PageStore.prototype.filterRequestNoCache = function(context) {
// We evaluate dynamic filtering first, and hopefully we can skip
// evaluation of static filtering.
if ( µb.userSettings.advancedUserEnabled ) {
var df = µb.dynamicNetFilteringEngine.clearRegisters();
df.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType);
var df = µb.sessionFirewall.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType);
if ( df.mustBlockOrAllow() ) {
result = df.toFilterString();
}

View file

@ -99,7 +99,7 @@ var hashFromPopupData = function(reset) {
}
var hasher = [];
var rules = popupData.dynamicFilterRules;
var rules = popupData.firewallRules;
var rule;
for ( var key in rules ) {
if ( rules.hasOwnProperty(key) === false ) {
@ -141,7 +141,7 @@ var rulekeyCompare = function(a, b) {
/******************************************************************************/
var addDynamicFilterRow = function(des) {
var addFirewallRow = function(des) {
var row = rowsToRecycle.pop();
if ( row.length === 0 ) {
row = uDom('#templates > div:nth-of-type(1)').clone();
@ -155,7 +155,7 @@ var addDynamicFilterRow = function(des) {
row.toggleClass('isDomain', des === hnDetails.domain);
row.toggleClass('allowed', hnDetails.allowCount !== 0);
row.toggleClass('blocked', hnDetails.blockCount !== 0);
row.appendTo('#dynamicFilteringContainer');
row.appendTo('#firewallContainer');
// Hacky? I couldn't figure a CSS recipe for this problem.
// I do not want the left pane -- optional and hidden by defaut -- to
@ -171,8 +171,8 @@ var addDynamicFilterRow = function(des) {
/******************************************************************************/
var updateDynamicFilterCell = function(scope, des, type, rule) {
var selector = '#dynamicFilteringContainer span[data-src="' + scope + '"][data-des="' + des + '"][data-type="' + type + '"]';
var updateFirewallCell = function(scope, des, type, rule) {
var selector = '#firewallContainer span[data-src="' + scope + '"][data-des="' + des + '"][data-type="' + type + '"]';
var cell = uDom(selector);
// This should not happen
@ -230,48 +230,61 @@ var updateDynamicFilterCell = function(scope, des, type, rule) {
/******************************************************************************/
var updateAllDynamicFilters = function() {
var rules = popupData.dynamicFilterRules;
var updateAllFirewallCells = function() {
var rules = popupData.firewallRules;
for ( var key in rules ) {
if ( rules.hasOwnProperty(key) === false ) {
continue;
}
updateDynamicFilterCell(
updateFirewallCell(
key.charAt(0),
key.slice(2, key.indexOf(' ', 2)),
key.slice(key.lastIndexOf(' ') + 1),
rules[key]
);
}
uDom('#firewallContainer').toggleClass(
'dirty',
popupData.matrixIsDirty === true
);
};
/******************************************************************************/
var buildAllDynamicFilters = function() {
var buildAllFirewallRows = function() {
// Do this before removing the rows
if ( dfHotspots === null ) {
dfHotspots = uDom('#actionSelector').on('click', 'span', setDynamicFilterHandler);
dfHotspots = uDom('#actionSelector').on('click', 'span', setFirewallRuleHandler);
}
dfHotspots.detach();
// Remove and reuse all rows: the order may have changed, we can't just
// reuse them in-place.
rowsToRecycle = uDom('#dynamicFilteringContainer > div:nth-of-type(7) ~ div').detach();
rowsToRecycle = uDom('#firewallContainer > div:nth-of-type(7) ~ div').detach();
var n = allHostnameRows.length;
for ( var i = 0; i < n; i++ ) {
addDynamicFilterRow(allHostnameRows[i]);
addFirewallRow(allHostnameRows[i]);
}
if ( dfPaneBuilt !== true ) {
uDom('#dynamicFilteringContainer')
.on('click', 'span[data-src]', unsetDynamicFilterHandler)
uDom('#firewallContainer')
.on('click', 'span[data-src]', unsetFirewallRuleHandler)
.on('mouseenter', '[data-src]', mouseenterCellHandler)
.on('mouseleave', '[data-src]', mouseleaveCellHandler);
dfPaneBuilt = true;
}
updateAllDynamicFilters();
// The padlock must be manually positioned, because its position depends
// on whether there is a vertical scrollbar.
var pane = document.getElementById('firewallContainer');
var rect = pane.getBoundingClientRect();
var padlock = document.getElementById('saveRules');
padlock.style.setProperty('left', (rect.left + 4) + 'px');
padlock.style.setProperty('top', (rect.top + 4) + 'px');
updateAllFirewallCells();
};
/******************************************************************************/
@ -284,7 +297,7 @@ var renderPrivacyExposure = function() {
// Sort hostnames. First-party hostnames must always appear at the top
// of the list.
var desHostnameDone = {};
var keys = Object.keys(popupData.dynamicFilterRules)
var keys = Object.keys(popupData.firewallRules)
.sort(rulekeyCompare);
var key, des, hnDetails;
for ( var i = 0; i < keys.length; i++ ) {
@ -384,7 +397,7 @@ var renderPopup = function() {
// Build dynamic filtering pane only if in use
if ( dfPaneVisible ) {
buildAllDynamicFilters();
buildAllFirewallRows();
}
};
@ -442,7 +455,7 @@ var gotoURL = function(ev) {
/******************************************************************************/
var toggleDynamicFiltering = function() {
var toggleFirewallPane = function() {
if ( popupData.advancedUserEnabled === false ) {
return;
}
@ -457,7 +470,7 @@ var toggleDynamicFiltering = function() {
// Dynamic filtering pane may not have been built yet
uDom('#panes').toggleClass('dfEnabled', popupData.dfEnabled);
if ( popupData.dfEnabled && dfPaneBuilt === false ) {
buildAllDynamicFilters();
buildAllFirewallRows();
}
};
@ -475,32 +488,32 @@ var mouseleaveCellHandler = function() {
/******************************************************************************/
var setDynamicFilter = function(src, des, type, action) {
var setFirewallRule = function(src, des, type, action) {
// This can happen on pages where uBlock does not work
if ( typeof popupData.pageHostname !== 'string' || popupData.pageHostname === '' ) {
return;
}
var onDynamicFilterChanged = function(response) {
var onFirewallRuleChanged = function(response) {
cachePopupData(response);
updateAllDynamicFilters();
updateAllFirewallCells();
hashFromPopupData();
};
messager.send({
what: 'toggleDynamicFilter',
what: 'toggleFirewallRule',
tabId: popupData.tabId,
pageHostname: popupData.pageHostname,
srcHostname: src,
desHostname: des,
requestType: type,
action: action
}, onDynamicFilterChanged);
}, onFirewallRuleChanged);
};
/******************************************************************************/
var unsetDynamicFilterHandler = function() {
var unsetFirewallRuleHandler = function() {
var cell = uDom(this);
setDynamicFilter(
setFirewallRule(
cell.attr('data-src') === '/' ? '*' : popupData.pageHostname,
cell.attr('data-des'),
cell.attr('data-type'),
@ -511,7 +524,7 @@ var unsetDynamicFilterHandler = function() {
/******************************************************************************/
var setDynamicFilterHandler = function() {
var setFirewallRuleHandler = function() {
var hotspot = uDom(this);
var cell = hotspot.ancestors('[data-src]');
if ( cell.length === 0 ) {
@ -526,7 +539,7 @@ var setDynamicFilterHandler = function() {
} else {
action = 1;
}
setDynamicFilter(
setFirewallRule(
cell.attr('data-src') === '/' ? '*' : popupData.pageHostname,
cell.attr('data-des'),
cell.attr('data-type'),
@ -553,6 +566,16 @@ var reloadTab = function() {
/******************************************************************************/
var saveRules = function() {
messager.send({ what: 'saveRules',
'srcHostname': popupData.pageHostname,
'desHostnames': popupData.hostnameDict
});
uDom('#firewallContainer').removeClass('dirty');
};
/******************************************************************************/
// Poll for changes.
//
// I couldn't find a better way to be notified of changes which can affect
@ -625,8 +648,9 @@ uDom.onLoad(function() {
uDom('#switch').on('click', toggleNetFilteringSwitch);
uDom('#gotoPick').on('click', gotoPick);
uDom('a[href]').on('click', gotoURL);
uDom('h2').on('click', toggleDynamicFiltering);
uDom('h2').on('click', toggleFirewallPane);
uDom('#refresh').on('click', reloadTab);
uDom('#saveRules').on('click', saveRules);
});
/******************************************************************************/

View file

@ -84,8 +84,8 @@
/******************************************************************************/
µBlock.saveDynamicRules = function() {
this.userSettings.dynamicFilteringString = this.dynamicNetFilteringEngine.toString();
µBlock.savePermanentFirewallRules = function() {
this.userSettings.dynamicFilteringString = this.permanentFirewall.toString();
this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString);
};
@ -715,25 +715,12 @@
µb.mirrors.toggle(false /* userSettings.experimentalEnabled */);
µb.contextMenu.toggle(userSettings.contextMenuEnabled);
µb.dynamicNetFilteringEngine.fromString(userSettings.dynamicFilteringString);
µb.permanentFirewall.fromString(userSettings.dynamicFilteringString);
µb.sessionFirewall.assign(µb.permanentFirewall);
// Remove obsolete setting
delete userSettings.logRequests;
µb.XAL.keyvalRemoveOne('logRequests');
if ( typeof userSettings.dynamicFilteringSelfie === 'string' ) {
if ( userSettings.dynamicFilteringString === '' && userSettings.dynamicFilteringSelfie !== '' ) {
µb.dynamicNetFilteringEngine.fromObsoleteSelfie(userSettings.dynamicFilteringSelfie);
userSettings.dynamicFilteringString = µb.dynamicNetFilteringEngine.toString();
µb.XAL.keyvalSetOne('dynamicFilteringString', userSettings.dynamicFilteringString);
// Auto-enable advanced user if there were dynamic rules
userSettings.advancedUserEnabled = userSettings.dynamicFilteringString !== '';
µb.XAL.keyvalSetOne('advancedUserEnabled', userSettings.advancedUserEnabled);
}
delete userSettings.dynamicFilteringSelfie;
µb.XAL.keyvalRemoveOne('dynamicFilteringSelfie');
}
};
this.loadUserSettings(onUserSettingsReady);

View file

@ -280,19 +280,13 @@ var matchWhitelistDirective = function(url, hostname, directive) {
/******************************************************************************/
µBlock.toggleDynamicFilter = function(details) {
µBlock.toggleFirewallRule = function(details) {
var changed = false;
if ( details.action !== 0 ) {
changed = this.dynamicNetFilteringEngine.setCellZ(details.srcHostname, details.desHostname, details.requestType, details.action);
this.sessionFirewall.setCellZ(details.srcHostname, details.desHostname, details.requestType, details.action);
} else {
changed = this.dynamicNetFilteringEngine.unsetCell(details.srcHostname, details.desHostname, details.requestType);
this.sessionFirewall.unsetCell(details.srcHostname, details.desHostname, details.requestType);
}
if ( !changed ) {
return;
}
this.userSettings.dynamicFilteringString = this.dynamicNetFilteringEngine.toString();
this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString);
// https://github.com/gorhill/uBlock/issues/420
this.cosmeticFilteringEngine.removeFromSelectorCache(details.srcHostname, 'net');

View file

@ -27,7 +27,7 @@
<p class="statValue" id="popupHitDomainCount"></p>
<div id="refresh" class="fa">&#xf021;</div>
</div><!-- DO NOT REMOVE --><div>
<div id="dynamicFilteringContainer">
<div id="firewallContainer">
<div><span data-i18n="popupAnyRulePrompt"></span><span data-src="/" data-des="*" data-type="*"> </span><span data-src="." data-des="*" data-type="*"> </span></div>
<div><span data-i18n="popupImageRulePrompt"></span><span data-src="/" data-des="*" data-type="image"> </span><span data-src="." data-des="*" data-type="image"> </span></div>
<div><span data-i18n="popup3pAnyRulePrompt"></span><span data-src="/" data-des="*" data-type="3p"> </span><span data-src="." data-des="*" data-type="3p"> </span></div>
@ -35,7 +35,7 @@
<div><span data-i18n="popup1pScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="1p-script"> </span><span data-src="." data-des="*" data-type="1p-script"> </span></div>
<div><span data-i18n="popup3pScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="3p-script"> </span><span data-src="." data-des="*" data-type="3p-script"> </span></div>
<div><span data-i18n="popup3pFrameRulePrompt"></span><span data-src="/" data-des="*" data-type="3p-frame"> </span><span data-src="." data-des="*" data-type="3p-frame"> </span></div>
</div><!-- <div id="offOverlay"></div> -->
</div><div id="saveRules" class="fa">&#xf13e;</div>
</div>
</div>