Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flickering during CSS injection in Chrome Extension

I have an addon that changes the look of a website completely by injecting a CSS file. It works but for a split second before the new look appear the old look is there, thus causing the "flicker" each time a page loads. The new look changes the background color etc. so the switch is very noticeable. (I only notice this on my desktop, not on my laptop. I don't know why but other users report it as well, perhaps the faster computer makes the page show faster than it takes to get the CSS injected?). The CSS injection need to happen so the injected CSS is the most important (as in last).

What I've tried that cause this issue (code afterwards):

  1. Manifest: Made sure css is in web_accessible_resources
  2. Manifest: Doing the injection directly in content_scripts
  3. Manifest: Doing it through a javascrip run from the content_scripts
  4. Manifest: Making sure all content-scripts are run at document_start
  5. Manifest: Tried running the injection from a script running on the background page
  6. JS Injection Timing: Added eventlistener for DOMSubtreeModified
  7. JS injection Timing: Added eventlistener for chrome.webNavigation.onCommitted
  8. JS Injection Timing: Waiting for document.head / document.body
  9. JS Injection Method: appendChild
  10. JS Injection Method: chrome.tabs.executeScript()
  11. JS Injection Code: Link element linking to css file in extension
  12. JS Injection Code: Directly executed javascript

Code Examples:

Manifest:

{
  "manifest_version": 2,
  "name": "foo",
  "short_name": "bar",
  "description": "baz",
  "options_page": "options.html",
  "version": "2.1.1",
  "homepage_url": "http://google.com/",
  "permissions": ["storage", "*://google.com/*", "webNavigation", "tabs", "activeTab"],
  "browser_action": {
    "default_icon": "icon16.png",
    "default_title": "title",
    "default_popup": "popup.html"
  },
  "icons": {
    "16": "icon16.png",
    "48": "icon48.png",
    "128": "icon128.png"
  },
  "content_scripts": [
    {
      "matches": ["*://google.com/*"],
      "js": ["carbonicEditionScript.js"],
      "all_frames": true,
      "run_at": "document_start"
    }
  ],
  "background": {
    "page": "popup.html"
  },
  "web_accessible_resources": ["carbonicEditionStyle.css"]
}

carbonicEditionScript.js

document.addEventListener('DOMSubtreeModified', injectStyle, false);
function injectStyle(){
    document.removeEventListener('DOMSubtreeModified', injectStyle, false);
    var style = document.createElement('style');
    style.setAttribute("id", "CarbonicEditionStyle");
    style.setAttribute("class", "CarbonicEditionStyle");
    style.setAttribute("type", "text/css");
    style.appendChild(document.createTextNode(css));
    document.getElementsByTagName("html")[0].appendChild(style);
}

carbonicEditionScriptAlternative.js

document.addEventListener('DOMSubtreeModified', injectCSS, false);
function injectCSS(){
var style = document.createElement('link');
style.rel = 'stylesheet';
style.type = 'text/css';
style.href = chrome.extension.getURL('carbonicEditionStyle.css');
if(document.head){
  document.removeEventListener('DOMSubtreeModified', injectCSS, false);
  (document.head||document.documentElement).appendChild(style);
}}

background.js

chrome.webNavigation.onCommitted.addListener(function(o) {
  chrome.tabs.executeScript(o.tabId, {
        code: "var css = 'body{background-color: green !important;}'; var style = document.createElement('style'); style.setAttribute('id', 'CarbonicEditionStyle'); style.setAttribute('class', 'CarbonicEditionStyle'); style.setAttribute('type', 'text/css'); style.appendChild(document.createTextNode(css)); document.getElementsByTagName('html')[0].appendChild(style);"
  });
}, {
  url: [{hostContains: 'google.com'}]
});

Does anyone know what's going on? All the solutions above work but the flicker is still happening.

like image 784
user1800592 Avatar asked Nov 01 '25 12:11

user1800592


1 Answers

Stylish-chrome extension fixed the flicker just by using webNavigation.onCommited event so you should've been already able to fix the issue. However, the problem you experience might be caused by asynchronous reading of the css code from the extension package since you mention web_accessible_resources. In this case cache it in chrome.storage.local or sessionStorage or localStorage. Or consider embedding the CSS inside your content script.

Some [probably redundant] notes on the code you've posted:

  1. Don't use DOMSubtreeModified event because it's a) not actually needed - (you can inject elements even before the webpage is parsed) and b) it's ancient/deprecated/slow/bad.

    So the entire content script might be:

    var style = document.createElement('style');
    style.id = "CarbonicEditionStyle";
    style.className = "CarbonicEditionStyle";
    style.type = "text/css";
    style.textContent = "body{background-color: green !important;}";
    (document.body || document.head || document.documentElement).appendChild(style);
    
  2. Use runAt: "document_start" in executeScript as by default it's document_idle which occurs usually only when the DOM is loaded and parsed:

    chrome.tabs.executeScript(o.tabId, {runAt: "document_start", code: "......"});
    
  3. Consider injecting the CSS directly via insertCSS (not really needed to eliminate the flicker and it won't allow you to disable the injected style but for the sake of completeness):

    chrome.tabs.insertCSS(o.tabId, {runAt: "document_start", code: "body{background....."});
    
  4. "matches": ["*://google.com/*"], won't match www.google.com which is used by default so it should be "matches": ["*://*.google.com/*"],
  5. google.com isn't the only domain, there are also lots of international domains.
like image 77
wOxxOm Avatar answered Nov 04 '25 03:11

wOxxOm



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!