Index: include.preload.js |
=================================================================== |
--- a/include.preload.js |
+++ b/include.preload.js |
@@ -31,16 +31,18 @@ |
["audio", "MEDIA"], |
["video", "MEDIA"], |
["frame", "SUBDOCUMENT"], |
["iframe", "SUBDOCUMENT"], |
["object", "OBJECT"], |
["embed", "OBJECT"] |
]); |
+let collapsingSelectors = new Set(); |
+ |
function getURLsFromObjectElement(element) |
{ |
let url = element.getAttribute("data"); |
if (url) |
return [url]; |
for (let child of element.children) |
{ |
@@ -124,16 +126,53 @@ |
{ |
if (/^(?!https?:)[\w-]+:/i.test(urls[i])) |
urls.splice(i--, 1); |
} |
return urls; |
} |
+function isCollapsibleMediaElement(element, mediatype) |
+{ |
+ if (mediatype != "MEDIA") |
+ return false; |
+ |
+ if (!element.getAttribute("src")) |
+ return false; |
+ |
+ for (let child of element.children) |
+ { |
+ // If the <video> or <audio> element contains any <source> or <track> |
+ // children, we cannot address it in CSS by the source URL; in that case we |
+ // don't "collapse" it using a CSS selector but rather hide it directly by |
+ // setting the style="..." attribute. |
+ if (child.localName == "source" || child.localName == "track") |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+function collapseMediaElement(element, srcValue) |
+{ |
+ if (!srcValue) |
+ return; |
+ |
+ let selector = element.localName + "[src=" + CSS.escape(srcValue) + "]"; |
+ |
+ // Adding selectors is expensive so do it only if we really have a new |
+ // selector. |
+ if (!collapsingSelectors.has(selector)) |
+ { |
+ collapsingSelectors.add(selector); |
+ elemhide.addSelectors([selector], null, "collapsing", true); |
+ } |
+} |
+ |
function hideElement(element) |
{ |
function doHide() |
{ |
let propertyName = "display"; |
let propertyValue = "none"; |
if (element.localName == "frame") |
{ |
@@ -161,29 +200,37 @@ |
let mediatype = typeMap.get(element.localName); |
if (!mediatype) |
return; |
let urls = getURLsFromElement(element); |
if (urls.length == 0) |
return; |
+ let collapsibleMediaElement = isCollapsibleMediaElement(element, mediatype); |
+ |
+ // Save the value of the src attribute because it can change between now and |
+ // when we get the response from the background page. |
+ let srcValue = collapsibleMediaElement ? element.getAttribute("src") : null; |
+ |
browser.runtime.sendMessage( |
{ |
type: "filters.collapse", |
urls, |
mediatype, |
baseURL: document.location.href |
}, |
- |
collapse => |
{ |
if (collapse) |
{ |
- hideElement(element); |
+ if (collapsibleMediaElement) |
+ collapseMediaElement(element, srcValue); |
+ else |
+ hideElement(element); |
} |
} |
); |
} |
function checkSitekey() |
{ |
let attr = document.documentElement.getAttribute("data-adblockkey"); |
@@ -381,21 +428,21 @@ |
// avoid creating the shadowRoot twice. |
let shadow = document.documentElement.shadowRoot || |
document.documentElement.createShadowRoot(); |
shadow.appendChild(document.createElement("content")); |
return shadow; |
}, |
- addSelectorsInline(selectors, groupName) |
+ addSelectorsInline(selectors, groupName, appendOnly = false) |
{ |
let style = this.styles.get(groupName); |
- if (style) |
+ if (style && !appendOnly) |
{ |
while (style.sheet.cssRules.length > 0) |
style.sheet.deleteRule(0); |
} |
if (selectors.length == 0) |
return; |
@@ -453,41 +500,44 @@ |
let selector = preparedSelectors.slice( |
i, i + this.selectorGroupSize |
).join(", "); |
style.sheet.insertRule(selector + "{display: none !important;}", |
style.sheet.cssRules.length); |
} |
}, |
- addSelectors(selectors, filters) |
+ addSelectors(selectors, filters, groupName = "emulated", appendOnly = false) |
{ |
if (this.inline || this.inlineEmulated) |
{ |
// Insert the style rules inline if we have been instructed by the |
// background page to do so. This is usually the case, except on platforms |
// that do support user stylesheets via the browser.tabs.insertCSS API |
// (Firefox 53 onwards for now and possibly Chrome in the near future). |
// Once all supported platforms have implemented this API, we can remove |
// the code below. See issue #5090. |
// Related Chrome and Firefox issues: |
// https://bugs.chromium.org/p/chromium/issues/detail?id=632009 |
// https://bugzilla.mozilla.org/show_bug.cgi?id=1310026 |
- this.addSelectorsInline(selectors, "emulated"); |
+ this.addSelectorsInline(selectors, groupName, appendOnly); |
} |
else |
{ |
browser.runtime.sendMessage({ |
type: "elemhide.injectSelectors", |
selectors, |
- groupName: "emulated" |
+ groupName, |
+ appendOnly |
}); |
} |
- if (this.tracer) |
+ // Only trace selectors that are based directly on hiding filters |
+ // (i.e. leave out collapsing selectors). |
+ if (this.tracer && groupName != "collapsing") |
this.tracer.addSelectors(selectors, filters); |
}, |
hideElements(elements, filters) |
{ |
for (let element of elements) |
hideElement(element); |