onDOMContentLoaded not always fires - google-chrome-extension

I inject my content script using filtered events instead of manifest content-scripts match rules.
When I navigate to a target page, content script doesn't run always, but only sometimes. When it fails to run I refresh the page (sometimes more than once), untill the content script starts.
For simplicity, I don't want to put the code I know is working. onDOMContentLoaded is not always firing when the filter meets the condition.
var filtro = {'url': [
{hostSuffix: 'somedomain.com', pathPrefix: '/search'},
{hostSuffix: 'somedomain.org', pathPrefix: '/search'}
]};
chrome.webNavigation.onDOMContentLoaded.addListener(listener, filter);
chrome.webNavigation.onErrorOcurred.addListener (listener, filter);
So I added onErrorOcurred, to fire when the page fails to load. But I still missing something, because it doesn't execute every time (but it executes on refreshing the page).
As many other pages, this one trows a lot of error messages to console. I imagine some of these may prevent onDOMContentLoaded to fire, but I think it should fire onErrorOcurred. Am I missing some event?
It happens to me in many different extensions and pages when using filtered events.
Thank you very much.

Related

chrome.webNavigation.onHistoryStateUpdated executes multiple times on redirects

When I click links on webpages, the script gets executed once, as expected, but when the webpage redirects the user (such as using history.pushState() the script gets executed twice, how can I stop this?
It's mind boggling that the history API doesn't make a difference between redirects and click on links, it reports them both as type link of transitionType.
The executeScript by default only executes on the top frame (frameId 0) so that's not the problem.
And I also used frameId and parentFrameId properties of the callback function although I knew it wouldn't fix the problem because as I've said, on clicking links, the script is executed only once, but when the browser redirects, it's executed twice.
BackgroundScript:
chrome.webNavigation.onHistoryStateUpdated.addListener(function(obj){
if (obj.frameId === 0 && obj.parentFrameId === -1){
chrome.tabs.executeScript({
file: 'js/contentScript.js',
runAt: 'document_end'
});
console.log(obj.transitionType);
}
});
ContentScript:
console.log("executed!");
onHistoryStateUpdated event is triggered not only on the browser back/forward actions but also when the site scripts use window.history methods so it can happen even 1000 times per navigation if the page calls the API that many times, there's no limit.
If the problem here is the multiple execution of the script you can simply send a message first and inject only if no response is received. Or use a global variable in the content script, see how to avoid dynamically injecting the same script multiple times when using chrome.tabs.executeScript.

Liferay IPC listener runs multiple times

First of all sorry if this question has been already asked somewhere, but after a few hours on google I still can't find an answer.
I am pretty new in portlet development, (but we have a shortage of developers and I have to work with it time to time), so the solution might be something trivial, but I really don't have enough experience with it.
The problem is I have two portlets on a page and I try to let one of them know about changes in the other. For this I use IPC. In the first one I have a Liferay.fire function:
function fire(key,value){
Liferay.fire(
'category',{
id: key,
name: value
}
);
}
In the other I have a Liferay.on('category',function(category){...}) function with an ajax call inside and some rendering methods.
Now if I visit the mentioned page and click on the corresponding buttons, at first everything works just fine. However, if I navigate from this page and come back, the listener will run two times. Navigating again -> three times. And so on... But if I reload the page (with F5 or CTRL+F5), it starts over, so until further navigation the listener runs only once.
The other strange thing is no matter how many times the function runs, the input parameters are all the same for each.
For example, if I have left the page and went back to it 3 times and last time I chose the category with 'id=1', then the function will run 3 times with 'id=1'. Now if I choose 'id=2' it will run 3 times with 'id=2'.
If anyone has any idea I would be really grateful as I am stuck for almost a day now.
Thank you very much in advance and please let me know if you need any further info.
the problem you're having is caused by the global Liferay.on listeners that are being created but never removed.
In Liferay Portal 7.x, SPA navigation is enabled by default. This means that when you are navigating, the page isn't being completely refreshed, but simply updated with new data coming from the server.
In a traditional navigation scenario, every page refresh resets everything, so you don't have to be so careful about everything that's left behind. In an SPA scenario, however, global listeners such as Liferay.on or Liferay.after or body delegates can become problematic. Every time you're executing that code, you're adding yet another listener to the globally persisted Liferay object. The result is the observed multiple invocations of those listeners.
To fix it, you simply need to listen to the navigation event in order to detach your listeners like this:
var onCategory = function(event) {...};
var clearPortletHandlers = function(event) {
if (event.portletId === '<%= portletDisplay.getRootPortletId() %>') {
Liferay.detach('onCategoryHandler', onCategory);
Liferay.detach('destroyPortlet', clearPortletHandlers);
}
};
Liferay.on('category', onCategory);
Liferay.on('destroyPortlet', clearPortletHandlers);

Wait for chrome.tabs.update tab to finish loading

I'm trying to work on a chrome extension and am trying to clean up some of my code by relying on the sendMessage. However the callback function activates before the page has finished loading so in the case of a new tab, nobody receives and in the case of an existing tab the page that is being moved from is getting the message (but that isn't what I want). I've looked for other people asking about that problem with new tabs and there wasn't a clear answer, the best suggestion I've seen is to create a global variable and create a listener for tab loads and compare it against this global variable.
So the question is, is there a way to wait in the callback function until the page has loaded, or do I create an array of JS objects that contain the tab I'm waiting on and the information I want to send to that tab.
For reference here is the relevant code in the background javascript file.
chrome.tabs.sendMessage(tab.id, {info: "info"}, function(response)
{
//This line isn't used when I am navigating without changing tabs
chrome.tabs.create({url: response.info.linkUrl}, function(tab1)
{
chrome.tabs.update(tab1.id, {url: response.info.linkUrl}, function(tab2)
{
chrome.tabs.sendMessage(tab2.id, {info: "More Info"});
});
});
});
Otherwise I am able to confirm that all of my tab side code works, once my sendMessage was delayed enough for me to see that with my own eyes. My code is able to consistently make it past validation on the page being navigated away from, confirmed by checking document.url.
You can try injecting a second content script instead of a message.
It will execute in the same context as your other script.
Something along the lines of
chrome.tabs.executeScript(tab2.id,
{code: 'showInfo("More Info);', runAt: 'document_idle'}
);
where showInfo does the same as your message handler.
It's a bit of a hack and I'm not 100% sure the load order will be correct.
Other possible solutions are more complex.
For example, you can make the content script report back that it is ready and have a handler for that, for instance you can register a listener for onMessage in the background that waits for a message from that specific tab.id, sends "More Info" and then deregisters or disables itself.
Or, you could potentially switch to programmatic injection of your content script, which would let you control load order.

Need to reload content script for chrome extension on facebook

I created a Chrome extension that adds links to items (things your friends share with you) on your facebook feed. Facebook loads about 10 or 20 items on the feed on page load, and then the rest are loaded via ajax when you scroll down.
I managed to get my content script to work for the first items, but not for the rest. I've read everything i could find, and it seems that I have to reload my content script.
How can i do that considering that the URL does not change?
You can use document.addEventListener with the DOMNodeInserted event to listen for new items getting rendered in the feed. The callback will have to check each node insertion to see if it is a feed item or not. Something like the following should work.
function nodeInsertedCallback(event) { console.log(event); });
document.addEventListener('DOMNodeInserted', nodeInsertedCallback);
Well, you can hang some time driven listener that runs every XX seconds, to verify if there's new items to work with.
Unfortunately there's no event you can hang from, fired when the page's code do some Ajax.
May be you can figure out what evet you can han from to detect the user has reached the end of the loaded item's list, knowing that de page's code will do some Ajax to retrieve more items. Then you start you time driven listenter.

Web: The system will record the length of time the user displayed each page

I have this requirement:
The system will record the length of time the user displayed each page.
While trivial in a rich-client app, I have no idea how people usually go about tracking this.
Edited: By John Hartsock
I have always been curious about this and It seems to me that this could be possible with the use of document.onunload events, to accurately caputure star and stop times for all pages. Basically as long as a user stays on your site you will always be able to get the start and stop time for each page except the last one. Here is the scenario.
User enters your site. -- I have a
start time for the home page
User goes to page 2 of your site -- I have a stop time for the home page and a start time for page 2
User exits your site. -- How do you get the final stop time for page 2
The question becomes is it possible to track when a user closes the window or navigates away from your site? Would it be possible to use the onunload events? If not, then what are some other possibilities? Clearly AJAX would be one route, but what are some other routes?
I don't think you can capture every single page viewing, but I think you might be able to capture enough information to be worthwhile for analysis of website usage.
Create a database table with columns for: web page name, user name, start time, and end time.
On page load, INSERT a record into the table containing data for the first three fields. Return the ID of that record for future use.
On any navigation, UPDATE the record in the navigation event handler, using the ID returned earlier.
You will end up with a lot more records with start times than records with both start and end time. But, you can do these analyses from this simple data:
You can count the number of visits to each page by counting start times.
You can calculate the length of time the user displayed each page for the records that have both start and end time.
If you have other information about users, such as roles or locations, you can do more analysis of page viewing. For example, if you know roles, you can see which roles use which pages the most.
It is possible that your data will be distorted by the fact that some pages are abandoned more often than others.
However, you certainly can try to capture this data and see how reasonable it appears. Sometimes in the real world, we have to make due with less than perfect information. But that may be enough.
Edit: Either of these approaches might meet your needs.
1) Here's the HTML portion of an Ajax solution. It's from this page, which has PHP code for recording the information in a text file -- easy enough to change to writing to a database if you wish.
<html>
<head>
<title>Duration Logging Demo</title>
<script type=”text/javascript”>
var oRequest;
var tstart = new Date();
// ooooo, ajax. ooooooo …
if(window.XMLHttpRequest)
oRequest = new XMLHttpRequest();
else if(window.ActiveXObject)
oRequest = new ActiveXObject(“Microsoft.XMLHTTP”);
function sendAReq(sendStr)
// a generic function to send away any data to the server
// specifically ‘logtimefile.php’ in this case
{
oRequest.open(“POST”, “logtimefile.php”, true); //this is where the stuff is going
oRequest.setRequestHeader(“Content-Type”, “application/x-www-form-urlencoded”);
oRequest.send(sendStr);
}
function calcTime()
{
var tend = new Date();
var totTime = (tend.getTime() – tstart.getTime())/1000;
msg = “[URL:" location.href "] Time Spent: ” totTime ” seconds”;
sendAReq(‘tmsg=’ msg);
}
</script>
</head>
<body onbeforeunload=”javascript:calcTime();”>
Hi, navigate away from this page or Refresh this page to find the time you spent seeing
this page in a log file in the server.
</body>
</html>
2) Another fellow proposes creating a timer in Page_Load. Write the initial database record at that point. Then, on the timer's Elapsed event, do an update of that record. Do a final update in onbeforeunload. Then, if for some reason you miss the very last onbeforeunload event, at least you will have recorded most of the time the user spent on the page (depending upon the timer Interval). Of course, this solution will be relatively resource-intensive if you update every second and have hundreds or thousands of concurrent users. So, you could make it configurable that this feature be turned on and off for the application.
This has to be done with some javascript. As the other said, it is not completely reliable. But you should be able to get more than enough accurate data.
This will need to call your server from javascript code when the page is unloaded. The javascript event to hook is window.unload. Or you can use a nicer API, like jQuery. Or you could use a ready made solution, like WebTrends, or Google Analytics. I think that both record the length of time that the page was displayed.
Good web analytics is pretty hard. And it becomes harder if you have to manage a lot of traffic. You should try to find an existing solution and not reinvent your own ...
I've put some work into a small JavaScript library that times how long a user is on a web page. It has the added benefit of more accurately (not perfectly, though) tracking how long a user is actually interacting with the page. It ignores time that a user switches to different tabs, goes idle, minimizes the browser, etc. The Google Analytics method suggested has the shortcoming (as I understand it) that it only checks when a new request is handled by your domain. It compares the previous request time against the new request time, and calls that the 'time spent on your web page'. It doesn't actually know if someone is viewing your page, has minimized the browser, has switched tabs to 3 different web pages since last loading your page, etc.
As multiple others have mentioned, no solution is perfect. But hopefully this one provides value, too.
Edit: I have updated the example to include the current API usage.
http://timemejs.com
An example of its usage:
Include in your page:
<script src="http://timemejs.com/timeme.min.js"></script>
<script type="text/javascript">
TimeMe.initialize({
currentPageName: "home-page", // page name
idleTimeoutInSeconds: 15 // time before user considered idle
});
</script>
If you want to report the times yourself to your backend:
xmlhttp=new XMLHttpRequest();
xmlhttp.open("POST","ENTER_URL_HERE",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
var timeSpentOnPage = TimeMe.getTimeOnCurrentPageInSeconds();
xmlhttp.send(timeSpentOnPage);
TimeMe.js also supports sending timing data via websockets, so you don't have to try to force a full http request into the document.onbeforeunload event.
In a web based system, there's no way to reliably do this. Sure, you can record each page that a user displays and record the length of time between each view but what happens when they close the browser on the last page they're displaying on? That's just one of dozens of problems with this requirement.
What about an AJAX based approach? It would only work when Javascript is on the client side, but sending a POST to some script every 15 seconds will get you a reasonable amount of granularity.
There are also more complicated "reverse-AJAX" things you might be able to do... but I don't know much about them.
You can use onunload to do what you need. Have it send a AJAX request to your server to update a database. You may want to return false then do document.close once the AJAX request has completed such that it won't quit prematurely and the ajax won't get discarded.
In the database you'll just want to store the page, the ip address, the time of the event, and whether it was a onload or onunload event.
That is all there is too it.
I recently made a example of recording html page spent time.
refresh would not interrupt the recording, and close would
I use sessionStorage to sotre "time" that page spent
if refresh
I would put it in to sessionStorage
if close
I can not get it from sessionSotrage , so I set time=0
here is my code
`
<body>
time spent :<div id="txt"></div>
</body>
<script>
$(function () {
statisticsStay();
})
function statisticsStay(){
var second=0;
if(sessionStorage.getItem('testSecond')!=null)
second=sessionStorage.getItem('testSecond');
var timer = setInterval(function(){
second++;
document.getElementById('txt').innerHTML=second;
},1000);
window.onbeforeunload = function(){
sessionStorage.setItem('testSecond',second);
};
}
</script>
`

Resources