Chrome extension: must delay page load until javascript finishes - javascript

My extension reformats an ugly page that I visit often. Currently, the page first loads un-fixed, then there is a ~100ms delay, then my JS formats the html.
How do I prevent the uncorrected html from ever displaying?
My JS file is defined in the manifest as follows:
,"content_scripts": [{
"matches": ["*://*.<url goes here>.com/*"],
"js": ["js/1.js"]
}]

You need to adjust the run_at parameter.
By default, content scripts are executed after the page is fully loaded (at "document_idle").
Try adding "document_end" first and see if it improves the delay.
In the case of "document_end", the files are injected immediately after the DOM is complete, but before subresources like images and frames have loaded.
"content_scripts": [{
"matches": ["*://*.example.com/*"],
"js": ["js/1.js"],
"run_at": "document_end"
}]
This may still be too late. There's a final option, "document_start", that you can use, but beware - it executes really early, before any of the DOM is ready. There's nothing yet for you to correct.
You can wait for an element to appear and correct it immediately though, for instance.
You may also try to correct things with CSS injection. This can be safely inserted at "document_start" with no extra tricks.
P.S. Or, for example, use Gael's answer - add a CSS rule to hide the body, wait until the page is loaded (for instance, with DOMContentLoaded event), correcting it and then removing/overriding the CSS rule.

You can set run_at: "document_start" in your manifest. Add a rule to hide the page, and differ your current script in a window.onload event.
If you are visiting often this page, you could even load first your template/redesign, and then integrate the page data that you want from a cache or from the source.

Related

How to predit in chrome extension html before it is displayed

I am building a chrome extension that is also supposed to remove parts of HTML.Thing is, I need to do that when I receive the html from the server BUT before it is displayed.If I use run at for document end and try to modify the HTML it'll look ugly because the page will load and then change, and I want to remove html parts and edit them before it is displayed.
For example, if my extension will need to clear the whole body tag I will need a script like that in runbefore.js:
document.body.innerHTML = "";
But when I use content scripts -
"content_scripts": [
{
"matches": ["https://www.website.com/*"],
"js": ["runbefore.js"],
"run_at": "document_end"
}
It loads the page and only then clears the whole page, and I want to clear the body tag and only then display it.If I use document_start it won't even do anything.
How do I resolve that issue ?
You can change run_at property in your manifest to document_start in your manifest.
You can get more information about this property here.

How to turn on/off chrome extension using local storage in background page?

I've been reading a lot of other answers on here for similar questions but I'm still having trouble. I want the user to be able to turn off my chrome extension at any point by clicking on the icon. The extension that I wrote will execute once at each page load, so when the icon is clicked, it should disable the code from occurring on next page load. When the icon is clicked, it either turns grey to show that it's off, or back to color. This part of the code works, but it is also supposed to be updating a localStorage variable to off/on. Then when the user starts a new page and the content script runs again, the content script checks the localStorage variable and, ideally, will only execute if the variable is set to "on." But instead, it executes every time, even when the icon is grey. I checked the localStorage in the console and the variable says "on" every time. What am I doing wrong?
P.S. I did also check to see whether the content script was always setting the storage variable to "on" (because it checks to see if there is no variable saved yet), but that wasn't the case.
Manifest.json
{
"manifest_version": 2,
"name": "My Extension",
"version": "0.1",
"icons":
{
"128": "128.png",
"48": "48.png",
"16": "16.png"
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["jquery-3.1.1.min.js", "content.js"]
}
],
"browser_action": {
"default_icon": "16.png"
},
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": ["tabs", "storage"],
"web_accessible_resources": [
"spritesheet.png"
]
}
Background Page
chrome.browserAction.onClicked.addListener(function(tab) {
//if on, turn off
if (localStorage.onoff == "on") {
chrome.browserAction.setIcon({path:{"16": "16grey.png", "48": "48grey.png","128": "128grey.png"}});
localStorage.onoff = "off";
//if off, turn on
} else {
chrome.browserAction.setIcon({path:{"16": "16.png", "48": "48.png","128": "128.png"}});
localStorage.onoff = "on";
}
});
Content script
if (!localStorage.onoff) {
localStorage.onoff = "on";
}
if (localStorage.onoff == "on") {
//execute the extension
}
Use chrome.storage.local
You can not use localStorage to move data between your background script and your content script. In your content script, it will access the localStorage of the page/domain for the page in which you have injected the script. This is as it should be. Your content script should be for affecting the page into which it is injected. Thus, the functionality that is not part of the Chrome APIs should work more, or less, as it would if written in a page script.
To set a data value in your background script prior to your content script loading which your content script can read, you should use chrome.storage.local.
You can set it with chrome.storage.local.set():
chrome.storage.local.set({
onOff: 'on'
}, function(){
console.log('Data saved');
});
You can read it with chrome.storage.local.get():
chrome.storage.local.get({
onOff: 'off'
}, function(data) {
if(data.onOff === 'on') {
//Do your code
}
});
Other issues
Loading jQuery:
You are loading jQuery on every page. Particularly given that you are injecting your scripts on every page, you should seriously consider not using jQuery. jQuery is 85KiB of minimized code. That is a fairly heavy burden to add to every single page the user loads.
One of jQuery's primary benefits, cross browser compatibility, is not needed given that you are writing an extension which, very specifically, can only function in one browser (or a very small number of browsers). Thus, this primary benefit has no value to your code.
Everything that you are doing with jQuery in your code can be written in vanilla JavaScript. This will normally cost you very little additional code, particularly when compared to the 85KiB of minimized jQuery code. Thus, while you might be saving a bit of coding, it is at the cost of forcing your users to have 85KiB of jQuery code loaded and evaluated on every single page they view.
I don't know what you are using jQuery for in your code. It could be that it is actually needed for something. On the other hand, it is much more likely that loading it is just a convenience for you (Some people use it just because it makes their code look cleaner). If you are using just some basic jQuery, you may find the queryAll() function written in this other answer of mine to give you some of the functionality that is often looked for.
Injecting your content script in <all_urls>:
You should seriously consider if injecting into every single page the user visits is needed. It quite possibly is. You have given no indication what your code is doing. Thus, there is no way for us to tell. If all URLs are not actually affected by your extension, please look into limiting the URLs into which your code is injected.
In addition, you are turning on and of your content script functionality using a browserAction button. As such, you should consider using tabs.executeScript() to only inject your content script(s) when your extension is turned on.

Is there an equivalent to `run_at: start` in a background page?

I need to run some JavaScript code before any other code is executed on the page that's being loaded.
That works fine using a content script like this:
{
"matches": ["http://*/*", "https://*/*", "file://*/*"],
"js": ["contentScript.js"],
"run_at": "document_start"
}
But now I'd like to run the content script only on certain pages which the user selects by clicking on the browser action button.
I've tried listening to chrome.tabs.onUpdated and then calling executeScript, but other code is running on the page before my content script.
Is there a way to ensure that the code injected by a background page is run before other code?
I could also add a condition inside my content script, but that would mean I need access to the current tabId and a list of all tabs where my extension is activated.
According to the documentation chrome.tabs.executeScript has several useful parameters, including runAt (by default it's document_idle, which occurs after DOMContentLoaded event), so to inject a script as soon as possible use runAt: 'document_start'.
chrome.tabs.executeScript(tabId, {
runAt: 'document_start',
code: 'console.log(document.documentElement.innerHTML);',
});
Beware the DOM tree is usually empty at this stage especially if you inject from an early executed event listener such as tabs.onUpdated or webNavigation.onCommitted. Even HEAD or BODY elements may be still absent, so you'd have to use (document.head || document.documentElement) fallback as a container for any added nodes such as <style> or <script>.

Trying to inject CSS after DOM loaded (Writing Chrome Extension)

I'm trying to write a Chrome Extension that just adds a CSS file to the end of a page's css definitions.
Google's documentation says that I can just use the function chrome.tabs.insertCSS() to add css to a page, but running it from a content_script included javascript file does nothing.
I'm trying two separate methods as documented on Google's site. I have an alert() statement to tell me where in the script I am, but it never runs the final alert. This makes me think that my script may be crashing somewhere in the middle.
Here are my source files:
manifest.json
{
"name": "Custom CSS to GOOGLE",
"version": "1.0",
"description": "The first extension that I made.",
"content_scripts": [
{
"matches": ["http://*/*"],
//"js": ["style_injector.js"],
"js": ["inject.js"],
"css": ["newstyle.css"]
}
],
"permissions": [
"tabs", "http://*/*"
]
}
inject.css
alert("newpage");
chrome.tabs.executeScript(null, {code:"document.body.bgColor='red'"});
chrome.tabs.insertCSS(
null,
{
code:"body{background:red;}"
}
);
alert("finishpage");
You can't call chrome.tabs.* API from a content script. You need to do it from a background page. If you need to communicate between a content script and a background page there is message passing available.
PS. Things like "This makes me think that my script may be crashing somewhere in the middle." could be easily checked by looking at the console (for content scripts it is just a regular console in a tab, Ctrl+J).
"I'm trying to write a Chrome Extension that just adds a CSS file to the end of a page's css definitions"
You might need to add the !important flag to the css you are changing:
When the browser is displaying an HTTP page and the user clicks this extension's browser action, the extension sets the page's bgcolor property to 'red'. The result, unless the page has CSS that sets the background color, is that the page turns red.
...the !important flag is the only way to change some things but you may have to write js to override other styles, check this
As serg said, the background.html page is where you use the injectCSS api:
/* in background.html */
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(null,
{code:"document.body.bgColor='red'"});
});
/*in manifest.json */
"permissions": [
"tabs", "http://\*/*"
],

How to re-render a page from a Google Chrome extension?

I'm new to writing extensions for Google Chrome. I want to make an extension that only runs on a few pages (that I'll choose) and re-renders their CSS after the page has loaded (ideally I would like something similar to what you can do with GM_addStyle in greasemonkey scripts).
How can I accomplish this in a Chrome extension?
You can use Content scripts that have access to the pages DOM.
In your manifest.json you could have:
"content_scripts": [
{
"matches": ["http://www.google.com/*"],
"css": ["mystyles.css"],
"run_at": "document_end"
}
],
This will inject the css file mystyles in to any google page after the DOM has loaded. This doesn't completely overwrite the styles, but you will be able craft your CSS so it replaces their styles.
More information can be found on code.google.com. It also includes information about how to programmatically inject CSS into a page.

Categories

Resources