#435: using shadow nodes instead of modifying directly nodes' style attr

This commit is contained in:
gorhill 2015-07-06 07:48:56 -04:00
parent c11421d574
commit febb18147a
6 changed files with 188 additions and 60 deletions

View File

@ -446,12 +446,34 @@ var uBlockCollapser = (function() {
if ( document.body === null ) {
return;
}
// https://github.com/chrisaljoudi/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
var elems = document.querySelectorAll(selectors);
var i = elems.length;
if ( i === 0 ) {
return;
}
// https://github.com/chrisaljoudi/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
var template = vAPI.perNodeShadowTemplate;
if ( template === null ) {
while ( i-- ) {
elems[i].style.setProperty('display', 'none', 'important');
}
return;
}
// https://github.com/gorhill/uBlock/issues/435
// Using shadow content so that we do not have to modify style
// attribute.
var sessionId = vAPI.sessionId;
var elem, shadow;
while ( i-- ) {
elems[i].style.setProperty('display', 'none', 'important');
elem = elems[i];
shadow = elem.shadowRoot;
if ( shadow !== null && shadow.className === sessionId ) {
continue;
}
shadow = elem.createShadowRoot();
shadow.className = sessionId;
shadow.appendChild(template.cloneNode(true));
}
};

View File

@ -54,6 +54,14 @@ if ( vAPI.contentscriptStartInjected ) {
}
vAPI.contentscriptStartInjected = true;
vAPI.styles = vAPI.styles || [];
vAPI.perNodeShadowTemplate = (function() {
if ( typeof document.documentElement.createShadowRoot !== 'function' ) {
return null;
}
var content = document.createElement('content');
content.select = '#' + vAPI.sessionId;
return content;
})();
/******************************************************************************/
@ -157,12 +165,34 @@ var hideElements = function(selectors) {
if ( document.body === null ) {
return;
}
// https://github.com/chrisaljoudi/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
var elems = document.querySelectorAll(selectors);
var i = elems.length;
if ( i === 0 ) {
return;
}
// https://github.com/chrisaljoudi/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
var template = vAPI.perNodeShadowTemplate;
if ( template === null ) {
while ( i-- ) {
elems[i].style.setProperty('display', 'none', 'important');
}
return;
}
// https://github.com/gorhill/uBlock/issues/435
// Using shadow content so that we do not have to modify style
// attribute.
var sessionId = vAPI.sessionId;
var elem, shadow;
while ( i-- ) {
elems[i].style.setProperty('display', 'none', 'important');
elem = elems[i];
shadow = elem.shadowRoot;
if ( shadow !== null && shadow.className === sessionId ) {
continue;
}
shadow = elem.createShadowRoot();
shadow.className = sessionId;
shadow.appendChild(template.cloneNode(true));
}
};

View File

@ -29,8 +29,11 @@
/******************************************************************************/
var showdomButton = uDom.nodeFromId('showdom');
// Don't bother if the browser is not modern enough.
if ( typeof Map === undefined || typeof WeakMap === undefined ) {
showdomButton.classList.add('disabled');
return;
}
@ -44,7 +47,6 @@ var inspectedURL = '';
var inspectedHostname = '';
var pollTimer = null;
var fingerprint = null;
var showdomButton = uDom.nodeFromId('showdom');
var inspector = uDom.nodeFromId('domInspector');
var domTree = uDom.nodeFromId('domTree');
var tabSelector = uDom.nodeFromId('pageSelector');

View File

@ -49,6 +49,8 @@ if ( Array.isArray(styles) === false ) {
return;
}
var sessionId = vAPI.sessionId;
/******************************************************************************/
// Remove all cosmetic filtering-related styles from the DOM
@ -60,15 +62,12 @@ var style, i;
i = styles.length;
while ( i-- ) {
style = styles[i];
if ( style.parentElement === null ) {
continue;
}
style.parentElement.removeChild(style);
selectors.push(style.textContent.replace(reProperties, ''));
if ( style.sheet !== null ) {
style.sheet.disabled = true;
}
}
// Remove `display: none !important` attribute
if ( selectors.length === 0 ) {
return;
}
@ -78,13 +77,23 @@ try {
elems = document.querySelectorAll(selectors.join(','));
} catch (e) {
}
i = elems.length;
var elem, shadow;
while ( i-- ) {
style = elems[i].style;
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
style.removeProperty('display');
elem = elems[i];
shadow = elem.shadowRoot;
if ( shadow === undefined ) {
style = elem.style;
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
style.removeProperty('display');
}
continue;
}
if ( shadow === null || shadow.className !== sessionId ) {
continue;
}
shadow.children[0].select = '';
}
/******************************************************************************/

View File

@ -49,6 +49,8 @@ if ( Array.isArray(styles) === false ) {
return;
}
var sessionId = vAPI.sessionId;
/******************************************************************************/
// Insert all cosmetic filtering-related style tags in the DOM
@ -56,23 +58,16 @@ if ( Array.isArray(styles) === false ) {
var selectors = [];
var reProperties = /\s*\{[^}]+\}\s*/;
var style, i;
var parent = document.head || document.body || document.documentElement;
i = styles.length;
while ( i-- ) {
style = styles[i];
if ( style.parentElement !== null ) {
continue;
}
if ( parent === null ) {
continue;
}
selectors.push(style.textContent.replace(reProperties, ''));
parent.appendChild(style);
if ( style.sheet !== null ) {
style.sheet.disabled = false;
}
}
// Add `display: none !important` attribute
if ( selectors.length === 0 ) {
return;
}
@ -83,12 +78,22 @@ try {
} catch (e) {
}
var elem, shadow, selector = '#' + sessionId;
i = elems.length;
while ( i-- ) {
style = elems[i].style;
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
style.setProperty('display', 'none', 'important');
elem = elems[i];
shadow = elem.shadowRoot;
if ( shadow === undefined ) {
style = elems[i].style;
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) {
style.setProperty('display', 'none', 'important');
}
continue;
}
if ( shadow === null || shadow.className !== sessionId ) {
continue;
}
shadow.children[0].select = selector;
}
/******************************************************************************/

View File

@ -42,7 +42,9 @@ if ( typeof vAPI !== 'object' ) {
/******************************************************************************/
if ( document.querySelector('iframe.dom-inspector.' + vAPI.sessionId) !== null ) {
var sessionId = vAPI.sessionId;
if ( document.querySelector('iframe.dom-inspector.' + sessionId) !== null ) {
return;
}
@ -159,7 +161,7 @@ var toggledNodes = new Map();
// Some kind of fingerprint for the DOM, without incurring too much overhead.
var domFingerprint = function() {
return vAPI.sessionId;
return sessionId;
};
/******************************************************************************/
@ -274,7 +276,7 @@ var domLayout = (function() {
return null;
}
// skip uBlock's own nodes
if ( node.classList.contains(vAPI.sessionId) ) {
if ( node.classList.contains(sessionId) ) {
return null;
}
if ( level === 0 && localName === 'body' ) {
@ -807,14 +809,14 @@ var onScrolled = function() {
/******************************************************************************/
var resetToggledNodes = function() {
var value;
var details;
// Chromium does not support destructuring as of v43.
for ( var node of toggledNodes.keys() ) {
value = toggledNodes.get(node);
if ( value !== null ) {
node.style.removeProperty('display');
details = toggledNodes.get(node);
if ( details.show ) {
showNode(node, details.v1, details.v2);
} else {
node.style.setProperty('display', value);
hideNode(node);
}
}
toggledNodes.clear();
@ -845,6 +847,7 @@ var selectNodes = function(selector, nid) {
/******************************************************************************/
var shutdown = function() {
toggleStylesVisibility(true);
resetToggledNodes();
domLayout.shutdown();
localMessager.removeAllListeners();
@ -869,32 +872,38 @@ var toggleNodes = function(nodes, originalState, targetState) {
if ( i === 0 ) {
return;
}
var node, value;
var node, details;
while ( i-- ) {
node = nodes[i];
if ( originalState ) { // any, ?
if ( targetState ) { // any, any
value = toggledNodes.get(node);
if ( value === undefined ) {
continue;
}
if ( value !== null ) {
node.style.removeProperty('display');
} else {
node.style.setProperty('display', value);
}
// originally visible node
if ( originalState ) {
// unhide visible node
if ( targetState ) {
details = toggledNodes.get(node) || {};
showNode(node, details.v1, details.v2);
toggledNodes.delete(node);
} else { // any, hidden
toggledNodes.set(node, node.style.getPropertyValue('display') || null);
node.style.setProperty('display', 'none');
}
} else { // hidden, ?
if ( targetState ) { // hidden, any
toggledNodes.set(node, 'none');
node.style.setProperty('display', 'initial', 'important');
} else { // hidden, hidden
// hide visible node
else {
toggledNodes.set(node, {
show: true,
v1: node.style.getPropertyValue('display') || '',
v2: node.style.getPropertyPriority('display') || ''
});
hideNode(node);
}
}
// originally hidden node
else {
// show hidden node
if ( targetState ) {
toggledNodes.set(node, { show: false });
showNode(node, 'initial', 'important');
}
// hide hidden node
else {
hideNode(node);
toggledNodes.delete(node);
node.style.setProperty('display', 'none', 'important');
}
}
}
@ -902,6 +911,56 @@ var toggleNodes = function(nodes, originalState, targetState) {
// https://www.youtube.com/watch?v=L5jRewnxSBY
/******************************************************************************/
var showNode = function(node, v1, v2) {
var shadow = node.shadowRoot;
if ( shadow === undefined ) {
if ( !v1 ) {
node.style.removeProperty('display');
} else {
node.style.setProperty('display', v1, v2);
}
} else if ( shadow !== null && shadow.className === sessionId ) {
shadow.children[0].select = '';
}
};
/******************************************************************************/
var hideNode = function(node) {
var shadow = node.shadowRoot;
if ( shadow === undefined ) {
node.style.setProperty('display', 'none', 'important');
return;
}
if ( shadow !== null && shadow.className === sessionId ) {
shadow.children[0].select = '#' + sessionId;
return;
}
// not all node can be shadowed
try {
shadow = node.createShadowRoot();
} catch (ex) {
return;
}
shadow.className = sessionId;
shadow.appendChild(vAPI.perNodeShadowTemplate.cloneNode(true));
};
/******************************************************************************/
var toggleStylesVisibility = function(state) {
var styleTags = vAPI.styles || [];
var i = styleTags.length, sheet;
while ( i-- ) {
sheet = styleTags[i].sheet;
if ( sheet !== null ) {
sheet.disabled = !state;
}
}
};
/******************************************************************************/
/******************************************************************************/
@ -978,7 +1037,7 @@ var onMessage = function(request) {
// Install DOM inspector widget
pickerRoot = document.createElement('iframe');
pickerRoot.classList.add(vAPI.sessionId);
pickerRoot.classList.add(sessionId);
pickerRoot.classList.add('dom-inspector');
pickerRoot.style.cssText = [
'background: transparent',
@ -1048,6 +1107,7 @@ pickerRoot.onload = function() {
window.addEventListener('scroll', onScrolled, true);
highlightElements();
toggleStylesVisibility(false);
localMessager.addListener(onMessage);
};