#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 ) { if ( document.body === null ) {
return; return;
} }
// https://github.com/chrisaljoudi/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
var elems = document.querySelectorAll(selectors); var elems = document.querySelectorAll(selectors);
var i = elems.length; 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-- ) { 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.contentscriptStartInjected = true;
vAPI.styles = vAPI.styles || []; 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 ) { if ( document.body === null ) {
return; return;
} }
// https://github.com/chrisaljoudi/uBlock/issues/158
// Using CSSStyleDeclaration.setProperty is more reliable
var elems = document.querySelectorAll(selectors); var elems = document.querySelectorAll(selectors);
var i = elems.length; 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-- ) { 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. // Don't bother if the browser is not modern enough.
if ( typeof Map === undefined || typeof WeakMap === undefined ) { if ( typeof Map === undefined || typeof WeakMap === undefined ) {
showdomButton.classList.add('disabled');
return; return;
} }
@ -44,7 +47,6 @@ var inspectedURL = '';
var inspectedHostname = ''; var inspectedHostname = '';
var pollTimer = null; var pollTimer = null;
var fingerprint = null; var fingerprint = null;
var showdomButton = uDom.nodeFromId('showdom');
var inspector = uDom.nodeFromId('domInspector'); var inspector = uDom.nodeFromId('domInspector');
var domTree = uDom.nodeFromId('domTree'); var domTree = uDom.nodeFromId('domTree');
var tabSelector = uDom.nodeFromId('pageSelector'); var tabSelector = uDom.nodeFromId('pageSelector');

View File

@ -49,6 +49,8 @@ if ( Array.isArray(styles) === false ) {
return; return;
} }
var sessionId = vAPI.sessionId;
/******************************************************************************/ /******************************************************************************/
// Remove all cosmetic filtering-related styles from the DOM // Remove all cosmetic filtering-related styles from the DOM
@ -60,15 +62,12 @@ var style, i;
i = styles.length; i = styles.length;
while ( i-- ) { while ( i-- ) {
style = styles[i]; style = styles[i];
if ( style.parentElement === null ) {
continue;
}
style.parentElement.removeChild(style);
selectors.push(style.textContent.replace(reProperties, '')); selectors.push(style.textContent.replace(reProperties, ''));
if ( style.sheet !== null ) {
style.sheet.disabled = true;
}
} }
// Remove `display: none !important` attribute
if ( selectors.length === 0 ) { if ( selectors.length === 0 ) {
return; return;
} }
@ -78,13 +77,23 @@ try {
elems = document.querySelectorAll(selectors.join(',')); elems = document.querySelectorAll(selectors.join(','));
} catch (e) { } catch (e) {
} }
i = elems.length; i = elems.length;
var elem, shadow;
while ( i-- ) { while ( i-- ) {
style = elems[i].style; elem = elems[i];
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) { shadow = elem.shadowRoot;
style.removeProperty('display'); 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; return;
} }
var sessionId = vAPI.sessionId;
/******************************************************************************/ /******************************************************************************/
// Insert all cosmetic filtering-related style tags in the DOM // Insert all cosmetic filtering-related style tags in the DOM
@ -56,23 +58,16 @@ if ( Array.isArray(styles) === false ) {
var selectors = []; var selectors = [];
var reProperties = /\s*\{[^}]+\}\s*/; var reProperties = /\s*\{[^}]+\}\s*/;
var style, i; var style, i;
var parent = document.head || document.body || document.documentElement;
i = styles.length; i = styles.length;
while ( i-- ) { while ( i-- ) {
style = styles[i]; style = styles[i];
if ( style.parentElement !== null ) {
continue;
}
if ( parent === null ) {
continue;
}
selectors.push(style.textContent.replace(reProperties, '')); 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 ) { if ( selectors.length === 0 ) {
return; return;
} }
@ -83,12 +78,22 @@ try {
} catch (e) { } catch (e) {
} }
var elem, shadow, selector = '#' + sessionId;
i = elems.length; i = elems.length;
while ( i-- ) { while ( i-- ) {
style = elems[i].style; elem = elems[i];
if ( typeof style === 'object' || typeof style.removeProperty === 'function' ) { shadow = elem.shadowRoot;
style.setProperty('display', 'none', 'important'); 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; return;
} }
@ -159,7 +161,7 @@ var toggledNodes = new Map();
// Some kind of fingerprint for the DOM, without incurring too much overhead. // Some kind of fingerprint for the DOM, without incurring too much overhead.
var domFingerprint = function() { var domFingerprint = function() {
return vAPI.sessionId; return sessionId;
}; };
/******************************************************************************/ /******************************************************************************/
@ -274,7 +276,7 @@ var domLayout = (function() {
return null; return null;
} }
// skip uBlock's own nodes // skip uBlock's own nodes
if ( node.classList.contains(vAPI.sessionId) ) { if ( node.classList.contains(sessionId) ) {
return null; return null;
} }
if ( level === 0 && localName === 'body' ) { if ( level === 0 && localName === 'body' ) {
@ -807,14 +809,14 @@ var onScrolled = function() {
/******************************************************************************/ /******************************************************************************/
var resetToggledNodes = function() { var resetToggledNodes = function() {
var value; var details;
// Chromium does not support destructuring as of v43. // Chromium does not support destructuring as of v43.
for ( var node of toggledNodes.keys() ) { for ( var node of toggledNodes.keys() ) {
value = toggledNodes.get(node); details = toggledNodes.get(node);
if ( value !== null ) { if ( details.show ) {
node.style.removeProperty('display'); showNode(node, details.v1, details.v2);
} else { } else {
node.style.setProperty('display', value); hideNode(node);
} }
} }
toggledNodes.clear(); toggledNodes.clear();
@ -845,6 +847,7 @@ var selectNodes = function(selector, nid) {
/******************************************************************************/ /******************************************************************************/
var shutdown = function() { var shutdown = function() {
toggleStylesVisibility(true);
resetToggledNodes(); resetToggledNodes();
domLayout.shutdown(); domLayout.shutdown();
localMessager.removeAllListeners(); localMessager.removeAllListeners();
@ -869,32 +872,38 @@ var toggleNodes = function(nodes, originalState, targetState) {
if ( i === 0 ) { if ( i === 0 ) {
return; return;
} }
var node, value; var node, details;
while ( i-- ) { while ( i-- ) {
node = nodes[i]; node = nodes[i];
if ( originalState ) { // any, ? // originally visible node
if ( targetState ) { // any, any if ( originalState ) {
value = toggledNodes.get(node); // unhide visible node
if ( value === undefined ) { if ( targetState ) {
continue; details = toggledNodes.get(node) || {};
} showNode(node, details.v1, details.v2);
if ( value !== null ) {
node.style.removeProperty('display');
} else {
node.style.setProperty('display', value);
}
toggledNodes.delete(node); toggledNodes.delete(node);
} else { // any, hidden
toggledNodes.set(node, node.style.getPropertyValue('display') || null);
node.style.setProperty('display', 'none');
} }
} else { // hidden, ? // hide visible node
if ( targetState ) { // hidden, any else {
toggledNodes.set(node, 'none'); toggledNodes.set(node, {
node.style.setProperty('display', 'initial', 'important'); show: true,
} else { // hidden, hidden 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); 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 // 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 // Install DOM inspector widget
pickerRoot = document.createElement('iframe'); pickerRoot = document.createElement('iframe');
pickerRoot.classList.add(vAPI.sessionId); pickerRoot.classList.add(sessionId);
pickerRoot.classList.add('dom-inspector'); pickerRoot.classList.add('dom-inspector');
pickerRoot.style.cssText = [ pickerRoot.style.cssText = [
'background: transparent', 'background: transparent',
@ -1048,6 +1107,7 @@ pickerRoot.onload = function() {
window.addEventListener('scroll', onScrolled, true); window.addEventListener('scroll', onScrolled, true);
highlightElements(); highlightElements();
toggleStylesVisibility(false);
localMessager.addListener(onMessage); localMessager.addListener(onMessage);
}; };