| Index: lib/cssInjection.js | 
| =================================================================== | 
| --- a/lib/cssInjection.js | 
| +++ b/lib/cssInjection.js | 
| @@ -21,21 +21,28 @@ | 
|  | 
| const {RegExpFilter} = require("filterClasses"); | 
| 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"); | 
|  | 
| -const userStyleSheetsSupported = "extensionTypes" in browser && | 
| -                                 "CSSOrigin" in browser.extensionTypes; | 
| +// 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 selectorGroupSize = 1024; | 
|  | 
| +let userStyleSheetsSupported = true; | 
| + | 
| function* splitSelectors(selectors) | 
| { | 
| // Chromium's Blink engine supports only up to 8,192 simple selectors, and | 
| // even fewer compound selectors, in a rule. The exact number of selectors | 
| // that would work depends on their sizes (e.g. "#foo .bar" has a size of 2). | 
| // Since we don't know the sizes of the selectors here, we simply split them | 
| // into groups of 1,024, based on the reasonable assumption that the average | 
| // selector won't have a size greater than 8. The alternative would be to | 
| @@ -56,27 +63,42 @@ | 
|  | 
| function createStyleSheet(selectors) | 
| { | 
| return Array.from(createRules(selectors)).join("\n"); | 
| } | 
|  | 
| function addStyleSheet(tabId, frameId, styleSheet) | 
| { | 
| -  browser.tabs.insertCSS(tabId, { | 
| -    code: styleSheet, | 
| -    cssOrigin: "user", | 
| -    frameId, | 
| -    matchAboutBlank: true, | 
| -    runAt: "document_start" | 
| -  }); | 
| +  try | 
| +  { | 
| +    browser.tabs.insertCSS(tabId, { | 
| +      code: styleSheet, | 
| +      cssOrigin: "user", | 
| +      frameId, | 
| +      matchAboutBlank: true, | 
| +      runAt: "document_start" | 
| +    }); | 
| +  } | 
| +  catch (error) | 
| +  { | 
| +    if (!/\bcssOrigin\b/.test(error.message)) | 
| +      throw error; | 
| + | 
| +    userStyleSheetsSupported = false; | 
| +  } | 
| + | 
| +  return userStyleSheetsSupported; | 
| } | 
|  | 
| function removeStyleSheet(tabId, frameId, styleSheet) | 
| { | 
| +  if (!styleSheetRemovalSupported) | 
| +    return; | 
| + | 
| browser.tabs.removeCSS(tabId, { | 
| code: styleSheet, | 
| cssOrigin: "user", | 
| frameId, | 
| matchAboutBlank: true | 
| }); | 
| } | 
|  | 
| @@ -94,26 +116,27 @@ | 
|  | 
| // 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); | 
| +  if (styleSheet && !addStyleSheet(tabId, frameId, styleSheet)) | 
| +    return false; | 
|  | 
| // 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); | 
|  | 
| frame.injectedStyleSheets.set(groupName, styleSheet); | 
| +  return true; | 
| } | 
|  | 
| port.on("elemhide.getSelectors", (message, sender) => | 
| { | 
| let selectors = []; | 
| let emulatedPatterns = []; | 
| let trace = devtools && devtools.hasPanel(sender.page); | 
| let inline = !userStyleSheetsSupported; | 
| @@ -130,23 +153,33 @@ | 
| hostname, | 
| specificOnly ? ElemHide.SPECIFIC_ONLY : ElemHide.ALL_MATCHING | 
| ); | 
|  | 
| for (let filter of ElemHideEmulation.getRulesForDomain(hostname)) | 
| emulatedPatterns.push({selector: filter.selector, text: filter.text}); | 
| } | 
|  | 
| -  if (!inline) | 
| -    updateFrameStyles(sender.page.id, sender.frame.id, selectors, "standard"); | 
| +  if (!inline && !updateFrameStyles(sender.page.id, sender.frame.id, | 
| +                                    selectors, "standard")) | 
| +  { | 
| +    inline = true; | 
| +  } | 
|  | 
| let response = {trace, inline, emulatedPatterns}; | 
| if (trace || inline) | 
| response.selectors = selectors; | 
|  | 
| +  // If we can't remove user style sheets using tabs.removeCSS, we'll only keep | 
| +  // adding them, which could cause problems with emulation filters as | 
| +  // described in issue #5864. Instead, we can just ask the content script to | 
| +  // add styles for emulation filters inline. | 
| +  if (!styleSheetRemovalSupported) | 
| +    response.inlineEmulated = true; | 
| + | 
| return response; | 
| }); | 
|  | 
| port.on("elemhide.injectSelectors", (message, sender) => | 
| { | 
| updateFrameStyles(sender.page.id, sender.frame.id, message.selectors, | 
| message.groupName); | 
| }); | 
|  |