AppCache events firing late - WebkIt (Chrome and Safari) - javascript

In my web app, I use the applicationCache feature. I register to the appCache events like
appCache.addEventListener(<event_name>, handleCacheEvent, false);
function handleCacheEvent(e) {
console.log('handleCacheEvent appcache event: '+ e.type+' appCache.status: '+appCache.status);
}
(in event_name I register to all events - 'cached', 'checking', 'downloading', etc).
However, in Chrome logs, I see these logs:
Application Cache Progress event (1 of 15)
...
Application Cache Progress event (15 of 15)
at the very beginning of the run, then some of my JS business logic, and after few seconds I see my logs
appcache event: progress appCache.status: 1...
It seems that the event listener are not triggered right when the status of the appCache changes, but rather later at some point. Is it so?

Yes, I've seen the same thing. When I add event listeners to those events and log them, my log message always appears after all of the browser's log message. I think this is because the browser starts loading the appcache before the contents of the page are done loading, since the appcache does its work in the background. This still gives you a chance to handle appcache events programmatically while letting the browser do its thing to honor the specification.

Related

Why is the voiceschanged event fired on page load?

In Chrome, the voiceschanged is firing on page load, so I don't need to call the function that has speechSynthesis.getVoices() initially in order for my empty array to be filled with voices as long as I have an event listener that calls it when voiceschanged is fired.
// Store voices
let voices = [];
function getVoices() {
voices = speechSynthesis.getVoices();
// Create an option for each voice in array
voices.forEach((voice) => {
const option = document.createElement('option');
option.value = voice.name;
option.innerText = `${voice.name} ${voice.lang}`;
voicesSelect.appendChild(option);
});
}
// Voices changed
speechSynthesis.addEventListener('voiceschanged', getVoices);
// Not needed (in Chrome at least) because voiceschanged event fires on page load, calling getVoices due to event listener (trying to figure out why)
// getVoices();
I'm just trying to understand this behavior - MDN's explanation of when voiceschanged fires doesn't explain it as far as I can tell:
The voiceschanged event of the Web Speech API is fired when the list
of SpeechSynthesisVoice objects that would be returned by the
SpeechSynthesis.getVoices() method has changed (when the voiceschanged
event fires.)
The event fires, because the list of voices changes when Chrome finishes making an API call to get the list of voices available only to Chrome users. Proof:
If I load my Speech Synthesis API-based web app, with Internet connection, I have 21 available voices, a few months ago, I only remember 10 or 15 or so.
If I do the same, without Internet connection, I only have two voices: Microsoft David Desktop and Microsoft Zira Desktop.
You probably notice that the two voices without Internet connection are rather boring and almost recognizable for being used in cheap audio production. But the Google Chrome ones are fluid and almost inflective. This event has to fire when the voices are loaded, of course. Take a quick glance at the W3C Errata in the Web Speech API Specification. Any time the voices are loaded, the voiceschanged event is fired....
voiceschanged: Fired when the contents of the SpeechSynthesisVoiceList, that the getVoices method will return, have changed. Examples include: server-side synthesis where the list is determined asynchronously, or when client-side voices are installed/uninstalled.
And, in fact, look at the last line of the MDN web docs you linked...
With Chrome however, you have to wait for the event to fire before populating the list, hence the bottom if statement seen below.
Speech Synthesis API-Based Source Code (from my open-source project PronounceThat)

Chrome Extension API not firing onChanged event

I am using chrome.downloads.onChanged.addListener to find out when a download completes.
The callback does not fire sometimes, and there is no apparent reason for it.
The code looks like this
chrome.downloads.onChanged.addListener(function (downloadDelta) {...});
and it is in background.js and file is mentioned in background section of manifest. I added console.log(downloadDelta); at the very begining of callback, but it is not fired.
The API is mentioned here
EDIT: Test extension
Most probably this has to do with peculiarities of Event pages - something not working properly when your page gets unloaded.
This is easy to test - remove "persistent": "false" from the manifest.
That said, your test extension does not violate any Event page recommendations and chrome.downloads API does not list any incompatibilities. Therefore, 2 scenarios are possible:
It's quite possible that you are misinterpreting results due to the page being unloaded. For instance, if the page gets unloaded between the callback executing and you opening the devtools for it - the console and all local state will be wiped.
To test for that, make sure to write your diagnostics to persistent storage - chrome.storage API is one option for this.
If you are 100% sure the above is not the case, there may be a bug related to event pages and chrome.downloads. In that case, it should be reported.

Chrome App messaging and 'loadstop' vs. .load() timing

I am developing a Chrome App with webviews. Pages intended for the webviews may also run in a regular browser. If inside a webview, pages send messages to the main App, but apparently they need to get a message from the App first, in order to know where to send their messages.
No problem - the main App sends a message as soon as it sees a 'loadstop' event which tells the pages where to send messages to. If a page is not in a webview then it never gets the message.
The problem is, I need to know when a page should stop waiting for the message and assume it is NOT in a webview.
When does 'loadstop' occur, relative to events in the page such as jQuery's .ready or .load? Is there a way to trap or trigger an event guaranteed to occur after 'loadstop' MIGHT be seen in the main App and a message sent and received by the webview's JavaScript.
When does 'loadstop' occur, relative to events in the page such as jQuery's .ready or .load?
According to the documentation for the loadstop event:
Fired when all frame-level loads in a guest page (including all its subframes) have completed. This includes navigation within the current document as well as subframe document-level loads, but does not include asynchronous resource loads.
This would suggest it's more akin to jQuery's .ready(), which executes after the DOM tree is loaded, but before waiting for asset (.css, .js) downloads.
Keep an eye on that documentation page; it's already much improved since two weeks ago.
Is there a way to trap or trigger an event guaranteed to occur after 'loadstop' MIGHT be seen in the main App and a message sent and received by the webview's JavaScript?
Your manifest.json declares your my-app-main.js background script (and your webview permission) which launches your my-webview-wrapper.html which includes your <webview> tag and also inlines some javascript (or sources a my-webview-wrapper.js file) that assigns event listeners to your webview via an onload function as such:
onload = function() {
webview = document.getElementById("the-id-attribute-of-my-webview");
webview.addEventListener("<EVENT>", function() {
// the cool stuff you want to do
}
}
<EVENT> can be any of the webview DOM events listed in the documentation I linked (including loadstop). Your main app shouldn't really care that any of this is happening. (It's async! It's javascript! It's magic!)
If you're still confused, just poke around Google's webview sample on GitHub.

how to fix iframe load never terminating the browser visual spinner and 'Transfer ...' status

I have pages loading json into hidden iframes from javascript.
The Firefox browser seems to never acknowledge fully receiving the iframe content, and reports 'Transferring data from ...' on the status line, and shows the twirly 'busy' icon on the tab, indefinitely.
I am using jQuery to bind the 'load' handlers, and would prefer a solution that does not involve over-riding jQuery functionality.
btw, the load handler does fire, the json received is complete, and the iframe itself gets .remove()d in the cleanup code. The browser still waits for something to signal completeness.
Ben Nadel posted a blogpost on just this topic.
It seems that Firefox acknowledges that an iframe document was completely received only after the onload handler returns. If the iframe is deleted from the DOM within the handler, Firefox never detects completion. The solution Mr Nadel suggested, and which I used, is to use the javascript timer to call a deletion function to run after a brief delay. This allows the handler to return while the iframe persists, but does not let the iframe linger around.

Event onBrowserClose for Google Chrome Extension?

I am developing an extension for Google Chrome. My background script, everytime, authorizes on a server that uses the XMPP API, and subscribes for a PubSub node. I need to unsubscribe on the exit, otherwise the dummy subscriptions will remain on the server. Is There any onBrowserClose event in the Google Chrome Extension APIs?
There is no such event in the Chrome Extension API.
There is however a chrome.windows.onRemoved event that fires each time a window closes. I figured you could check in this event if you closed the last window, but unfortunately due to the asynchronous nature of Chrome this doesn't work.
What I tried was running a simple AJAX request in the onRemoved event handler. The AJAX request never got to the server, as Chrome had already closed before running the event (or just disregarded it).
Making the final answer be: No, currently you can't, as far as I know. You might want to star the following bug report at http://crbug.com/30885 to get noticed on updates.
If you catch the case when the number of open tabs is 0, you can treat that as a Chrome onClose event. In my case, I have to cancel a desktop notification before Chrome closes because it was crashing otherwise.
This is how I did it:
1. Initialize a variable num_tabs by using the following:
chrome.tabs.getAllInWindow( null, function( tabs ){
console.log("Initial tab count: " + tabs.length);
num_tabs = tabs.length;
});
2. Increment num_tabs when a tab is created:
chrome.tabs.onCreated.addListener(function(tab){
num_tabs++;
console.log("Tab created event caught. Open tabs #: " + num_tabs);
});
3. Decrement num_tabs when a tab is removed and run your browser onclose event handler if num_tabs = 0
chrome.tabs.onRemoved.addListener(function(tabId){
num_tabs--;
console.log("Tab removed event caught. Open tabs #: " + num_tabs);
if( num_tabs == 0 )
notification.cancel();
});
Adding a browser close event is a pretty frequent request. Star http://crbug.com/30885 for updates. And read the bug report for a clever hack to detect when the browser is shut down via a key press.
This one works for me:
chrome.windows.onRemoved.addListener(function(windowId){
alert("!! Exiting the Browser !!");
});
It takes chrome.windows rather than chrome.tabs.
TL;DR: Try window.onunload event, it works for some cases.
As it was mentioned before we generally can't handle something like onBrowserClose event and prevent browser from closing. But for some cases we can use window.onunload event for doing something synchronously, and it does work if it really synchronously.
From my experience you can at least:
Save some information in (for example logs) in HTML5 localStorage (which is synchronous).
Call some asynchronous chrome extension API functions but you can't get a result. (It works for me! Yay!)
Perform synchronous XMLHTTPRequest (yeah, sometimes it works).

Categories