Chrome Extension - how do I capture all runtime exceptions - javascript

I'm working on a Chrome extension which has a background script (or event script) which runs continuously and detects if certain web pages are visited in any tab and then does some processing.
I noticed that the script randomly stops working every now and then, but starts again if I restart the browser or inspect the console for the background script.
I know how to store data in chrome.storage.local and I want to be able to detect and make an entry everytime a runtime error is thrown, is this possible? i.e. a script wide catch block?
I saw this post which explains how to handle runtime errors, but this only works within a single callback function. I want to be able to do this for the whole script.

You are probably hitting the idle unload that Event pages use (since you say inspecting works - that wakes it up).
It was your choice to put in "persistent": false and it comes with consequences.
If you rely on any state variables, they will be lost when the page is unloaded. If you must keep any state at all, do it in chrome.storage.local.
Another common mistake is not re-registering all event listeners every time the script runs. The unload-but-remember-listeners mechanism depends on it; if an event is triggered, the following happens:
Check it there was any listener registered for the event. If not, do nothing.
If there was, the listener itself no longer exists (JS context is unloaded). Execute the page to reconstruct the context.
After the page stops executing (disregarding async code, Chrome won't wait), pick the listener registered in this run that matches the event and execute it.
So if you, say, registered a listener in a codepath that's not executed every time you run the script (e.g. not in a top-level statement but conditionally or asynchronously), then after waking up the script won't have that listener enabled and the event will be dropped:
/* Chrome wakes up your page */
chrome.storage.local.get("option", function(data) {
if(data.option) {
// Asynchronous
chrome.someAPI.onSomeEvent.addListener(function() {
// This will not be handled after unload
});
}
});
// Synchronous
chrome.someAPI.onSomeEvent.addListener(function() {
// This will be handled after unload
chrome.storage.local.get("option", function(data) {
if(data.option) {
// Do stuff
});
}
});
/* At this point, Chrome triggers the event,
and if there are no listeners (re)registered it's lost */
So put any conditional/async processing inside the listeners.
There are also other reasons why it can stop working, but it's impossible to tell without seeing your code.

Related

Inconsistency with beforeUnload across different browsers

I'm attempting to run a function (that sends some fetch calls to an API) inside of a "beforeunload" event handler and the results are mixed. With Chrome it successfully runs my function if the user reloads the page or clicks a link for a different webpage, but it doesn't work if they close the tab. In Firefox the function only runs if they close the tab, but not in any other scenario. When I debug the beforeunload handler in the browser I can see the event handler clearly is triggered but the function inside doesn't always run. I suspect the function I'm trying to feed being async is part of the issue but I don't know why the results would be so inconsistent?
This is how I'm currently writing the event handler
window.addEventListener('beforeunload', async function(evt){
await exitChat();
});
I've also tried not using async/await. Let me know if seeing the exitChat() function code would help or anything else.
Edit: After a bit more debugging I can confirm that the issue, with Chrome at least, is that when the window is closed it can't make it through the entire exitChat() function in time before the page closes. So is there a way to force a delay in the unloading process?

Using external javascript code to run a snippet on the Chrome console

Is it possible in an external javascript code (for example, a userscript through tampermonkey) to run a code snippet on the Chrome console. For example, console.log prints text to the console. Is there some way, like a function console.eval or some more complex way where I can run code on the console without manually opening it on the given website, but using the original javascript code behind the website or a userscript?
Notes: I use Google Chrome on Windows 10. Preferably this answer should be as generally applicable as possible, but first priority for me is for it to work in my environment.
Thanks,
Mike
Uk, when i said if the page is reloading constantly, the "console" that u think of would also reload??, a lot of us knew about what I'm doing below(if not all of us) but I finally connected it with your question. Using one tab to control the other tab
ONE EDIT: I used an interval to determine if the controlled tab is CLOSED(since a certain value eventually changes if the tab is closed for good)
HOW TO USE:
Open a tab with the same origin as desired url(but not the constantly reloading site)..
eg: opening a tab on "https://example.com/404" if desired url is "https://example.com" is the desired url(the constantly reloading one)
In the code snippet I have below, you can put your tab controlling code in the loadFn function, where myWindow and this point to the controlled tab's window
eg: in the loadFn function, myWindow.console.log(1) or this.console.log(1) would both log 1 to the controlled tab's console
SECOND EDIT: I shall explain how it works(and talk about unloadFn as you requested in comments)
I use a combination of unload and load listening to be able to repeatedly send code "on reload" which is not an event in itself so I had to create it. In case I didn't explain myself, I'd go into detail now..
When a page is reloading(or when I'm JUST SPAWNING the page, eg: var myWindow=window.open(desiredUrl)), the unload event happens. There's just one problem however; every time the page is reloading, all event listeners and any code you put is removed(because reload unloads to then reload)
The solution is simple: on every unload, I set the listners again, and since the function would call itself(every time the page unloads), the listeners would successfully be reloaded every time the page reloads(and that is why loadFn could run in the other tab after every reload)
DO NOTE: You might ask "why use a setTimeout then?". Actually it's quite important. Without the setTimeout, the event listeners DO NOT GET ADDED, I think it's because the tab would ignore your commands(since it would be focusing on loading its default stuff(like event listeners for instance)), and asynchronous programming does wonders in this case because it will wait until the other stuff are processed(like event handling stuff) then run
SIDE NOTE: If that's not why setTimeout works and NOT USING it doesn't, all I know is that without it, it doesn't work, and with it, it works
var myWindow=window.open(desiredUrl) //remember to run this code on the same origin as the desiredUrl
function loadFn(){
//this will happen every time myWindow loads or reloads
myWindow.alert("It runs in the controlled tab")
myWindow.console.log("Even in the controlled tab's console it works >:D")
}
function unloadFn(){setTimeout(()=>{
myWindow.addEventListener('unload',unloadFn)
myWindow.addEventListener('load',loadFn)
if(!myWindow.Window){console.warn("myWindow was CLOSED")}
},0)}
myWindow.addEventListener('unload',unloadFn)
//extra thing below to tell if controlled tab is closed >:D
var i=setInterval(()=>{
//for if controlled tab is closed
if(!myWindow.document.location){clearInterval(i);console.warn("myWindow was CLOSED")}
},0)

In Chrome Event Pages, can I use the Alarms API to run code at specified intervals, while also ignoring code I want to run only once?

As I understand it, in Chrome Extensions, Event Pages go inactive after a short period of time to free up memory in Chrome. Whereas Background Pages run all the time. I'd like to code my extension to use Event Pages and allow my extension to not be a memory hog.
Since the event page goes inactive, I am unable to use setInterval to periodically run code in the background. The recommended solution is to use the chrome.alarms API as seen here:
chrome.alarms.onAlarm.addListener(function(alarm) {
if (alarm.name == 'name-of-alarm') {
// Do something...
testFunc();
}
});
// Create the alarm:
chrome.alarms.create('name-of-alarm', {
periodInMinutes: 10
});
My issue is that when this code wakes up my event page, it runs ALL of the code inside it and not just the testFunc() I specified. I have code in my event page that I only want to run once (when the browser starts or when the extension is reloaded/updated). The alarms API does not seem to be fit for what I'm trying to do.
What's the best way to handle this while keeping my event page an event page?

Chrome extension update flow

I am starting with chrome extension development and have a couple of questions regarding extension install/update flow and testing during the development :
What happens with the background script after extension update, does chrome perform background script reload ?
Are content scripts detached from background script after extension update ?
If there's an onInstalled event handler in background script, what happens with that event handler when chrome updates extension(is this event handler detached, and when update finishes, the new handler is attached and then executed or some other flow is exercised) ?
Is there a way to simulate update process during development in order to debug events that happen during the update process, for example to host extension on some local server and update from there ?
where to search for documentation on topics like this and similar, is the chromium source code the right place or at least the starting point ?
Thanks!
What happens with the background script after extension update, does Chrome perform background script reload?
The behavior depends on whether you have a handler to chrome.runtime.onUpdateAvailable event registered and whether your extension has a persistent background page or event page.
If you have a persistent background page:
If you handle this event and call chrome.runtime.reload(), the extension is unloaded and then updated before being loaded again.
If you handle this event and do not call chrome.runtime.reload(), then the update will only apply when the extension is next reloaded - likely the next full browser restart.
If you do not handle this event at all, the extension will be unloaded immediately to be updated.
If you have a non-persistent Event page:
If you handle this event and call chrome.runtime.reload(), the extension is updated before being loaded again.
If you do not call chrome.runtime.reload(), or do not handle the event at all, Chrome will update the extension when the Event page next gets unloaded.
There is no way to programmatically prevent the update once the background page gets unloaded for whatever reason.
Are content scripts detached from background script after extension update?
Yes, and it's not pretty. They enter an "orphaned" state when using Chrome API gives inconsistent errors (some do nothing, some trigger exceptions), but are still running — for example, any DOM event listeners will still trigger.
As such, if you want the content scripts to work immediately again, your job is to:
Inject scripts programmatically in existing tabs, without making an assumption that it did not execute before: cleanup first if necessary.
Make sure orphaned copies stop executing: either by noticing in the old copy that it's orphaned, or by broadcasting a DOM event from the new copy.
Important note about WebExtensions: Firefox, unlike Chrome, always reinjects content scripts on load into pages that match manifest entries. Make sure to take that into account.
There are a few question that cover this; for example:
Sending message from a background script to a content script, then to a injected script (See addendum to the answer)
How to properly handle chrome extension updates from content scripts
Chrome extension content script re-injection after upgrade or install
If there's an onInstalled event handler in background script, what happens with that event handler when chrome updates extension (is this event handler detached, and when update finishes, the new handler is attached and then executed or some other flow is exercised)?
Since an update can only happen while the background page is unloaded, there is no complex logic; it will simply fire on first load of the extension afterwards with details.reason == "update". Be sure to register the handler synchronously on script load (e.g. in top level code), or else you may miss the event — normally this only concerns Event pages, but I suspect it's also important here.
Is there a way to simulate update process during development in order to debug events that happen during the update process, for example to host extension on some local server and update from there?
Sadly, this is no longer possible to the best of my knowledge, unless you can use Enterprise Policy install. Your best bet is to have an extension in CWS that's published as Private.
To a certain extent, pressing "Reload" after making some changes to an unpacked extension simulates what happens during the update - with the exception of onInstalled event.
Where to search for documentation on topics like this and similar, is the chromium source code the right place or at least the starting point?
Well.. For detailed questions Chromium code is, of course, the authoritative source. You should search StackOverflow as well, as there's quite a body of knowledge amassed here already. Finally, the official docs provide a lot of information, even if it's not immediately evident - the chrome.runtime API docs, for example.

JavaScript + onunload event

I want to fire onunload event to do some clean up operations, I have multiple tabs(Navbar) showing multiple links to different web pages,my problem is that even when I'm in some other page my unload function which is in tag of some other jsp is fired. Please help to resove this, I want unload function to be called when user closes browser in that page.
I'm not sure how you got the onunload event to work....The problem I've found with using the onunload event is that it is fired after the page has been unloaded. This means that no more JavaScript can be executed because the page has been unloaded.
You should be looking into using the onbeforeunload event.
This event is a little unique because if the function that handles this event returns anything a pop up is displayed asking the user if they would like to continue with the operation. So, in your case make sure that your function doesn't return anything. The other thing to note about the onbeforeunload event is that, at this time, Opera does not support it (Safari, FireFox, and Internet Explorer do though).
Both the onbeforeunload and onunload events are executed every time the page is unloaded. If a control on your page submits the page to the server code, the page is unloaded and the JavaScript is executed.
If you don't want the JavaScript to be executed when a control on your page is submitting it to the server you have to implement something that checks to see whether or not your code should be executed.
This is simple, add a JavaScript boolean to the page and a function that set's this boolean to true. Make sure that every element in your page that posts back to your server code sets this boolean to true before it submits the page. Check this boolean in your onbeforeunload event to see if your cleanup code should be executed.
Hope this helps,
-Frinny
It seems that the unload function has been created in a global scope. Try placing that function only on the page you want to act.
You have a frameset page? And you want to be notified when they navigate away from the frameset? Add an onbeforeunload on the frameset. I don't know what you mean by clean up, but you can't send XHRs during unload safely across browsers

Categories

Resources