Index: lib/cssInjection.js |
=================================================================== |
--- a/lib/cssInjection.js |
+++ b/lib/cssInjection.js |
@@ -23,21 +23,17 @@ |
const {ElemHide} = require("elemHide"); |
const {ElemHideEmulation} = require("elemHideEmulation"); |
const {checkWhitelisted} = require("whitelisting"); |
const {extractHostFromFrame} = require("url"); |
const {port} = require("messaging"); |
const devtools = require("devtools"); |
const info = require("info"); |
-// Chromium's support for tabs.removeCSS is still a work in progress and the |
-// API is likely to be different from Firefox's; for now we just don't use it |
-// at all, even if it's available. |
-// See https://crbug.com/608854 |
-const styleSheetRemovalSupported = info.platform == "gecko"; |
+const styleSheetRemovalSupported = "removeCSS" in browser.tabs; |
const selectorGroupSize = 1024; |
let userStyleSheetsSupported = true; |
function* splitSelectors(selectors) |
{ |
// Chromium's Blink engine supports only up to 8,192 simple selectors, and |
@@ -112,31 +108,74 @@ |
if (!frame) |
return false; |
if (!frame.injectedStyleSheets) |
frame.injectedStyleSheets = new Map(); |
let oldStyleSheet = frame.injectedStyleSheets.get(groupName); |
+ // The behavior of tabs.insertCSS and tabs.removeCSS is different between |
kzar
2018/03/19 18:42:49
Thanks for taking the time to write such a good co
|
+ // Firefox and Chromium. On Firefox, it is an error to inject a duplicate |
+ // style sheet, whereas Chromium will happily inject the duplicate as a copy. |
+ // tabs.removeCSS on Chromium will remove only the last injected copy. |
+ // |
// Ideally we would compare the old and new style sheets and skip this code |
// if they're the same, but the old style sheet can be a leftover from a |
// previous instance of the frame. We must add the new style sheet |
// regardless. |
- |
- // Add the new style sheet first to keep previously hidden elements from |
- // reappearing momentarily. |
- if (styleSheet && !addStyleSheet(tabId, frameId, styleSheet)) |
- return false; |
+ // |
+ // We could simply remove the old style sheet first before we add the new |
+ // one, but this may cause previously hidden elements to reappear |
+ // momentarily. On the other hand, we cannot remove the old style sheet after |
+ // we add the new one if they're exactly the same, because this would cause |
+ // Firefox to remove its only copy; if we skip the removal, Chromium would |
+ // end up with a duplicate copy! |
+ // |
+ // So: (1) we do not want to remove first to avoid flicker; (2) if the old |
kzar
2018/03/19 18:42:49
I think this stanza can be removed.
|
+ // and new style sheets are identical, we do not want to remove on Firefox, |
+ // but we do want to remove on Chromium. |
+ // |
+ // In order to resolve this conundrum without resorting to browser-specific |
+ // code, we treat the different types of style sheets differently. |
+ if (groupName == "standard") |
kzar
2018/03/19 18:42:49
Perhaps the logic would be clearer something like
|
+ { |
+ // Style sheets in the "standard" group are typically only added once and |
kzar
2018/03/19 18:42:49
I think instead of saying 'the "standard" group' a
|
+ // never removed. In this case, it's OK to remove the old style sheet first |
+ // and accept the flicker. |
+ if (oldStyleSheet) |
+ { |
+ removeStyleSheet(tabId, frameId, oldStyleSheet); |
- // Sometimes the old and new style sheets can be exactly the same. In such a |
- // case, do not remove the "old" style sheet, because it is in fact the new |
- // style sheet now. |
- if (oldStyleSheet && oldStyleSheet != styleSheet) |
- removeStyleSheet(tabId, frameId, oldStyleSheet); |
+ // Delete the entry first, just in case style sheet injection below |
+ // fails. |
+ frame.injectedStyleSheets.delete(groupName); |
+ } |
+ |
+ if (styleSheet && !addStyleSheet(tabId, frameId, styleSheet)) |
+ return false; |
+ } |
+ else if (styleSheet != oldStyleSheet) |
+ { |
+ // Note that it is possible that the old and new style sheets are the same |
+ // but the old one is really a leftover from a previous instance of the |
+ // frame (i.e. it doesn't exist in the current instance of the frame). In |
+ // this case we'll skip the addition of the new style sheet and ads will |
+ // not get blocked. This would not be acceptable for style sheets in the |
+ // "standard" group, but it's OK for other style sheets. Given the problems |
+ // described above, this is the best tradeoff. |
+ |
+ // Add the new style sheet first to keep previously hidden elements from |
+ // reappearing momentarily. |
+ if (styleSheet && !addStyleSheet(tabId, frameId, styleSheet)) |
+ return false; |
+ |
+ if (oldStyleSheet) |
+ removeStyleSheet(tabId, frameId, oldStyleSheet); |
+ } |
frame.injectedStyleSheets.set(groupName, styleSheet); |
return true; |
} |
port.on("elemhide.getSelectors", (message, sender) => |
{ |
let selectors = []; |