in chrome extensions how to trigger an event on deactivation? - javascript

I have a chrome extension which injects some DOM event listeners through the content scripts. I want to remove those event listeners from the DOM in the event that the user deactivates the plugins, is there a method to do so?

It's an interesting question. It has to do with a concept of "orphaned" scripts. I talk at length about those in an addendum here.
Problem is, as soon as the script becomes detached from the parent extension, Chrome APIs will fail. As such, detecting this is not straightforward.
There are many possible approaches:
Maintain an open port to the background page. The port will fire an onDisconnected event in case the background page ceases to exist.
This is an event-based approach - you will be able to react immediately.
But this has an important downside: maintaining an open port will prevent an Event page from unloading. So if you use a non-persistent background page, this is not optimal.
Periodically, or better yet - in the beginning of your handlers, try to do something with Chrome API. This will fail, and you can catch the exception and assume that the extension is orphaned.
Please note that this is pretty much undefined behavior. How Chrome API reacts can change over time.
function heartbeat(success, failure) {
try {
if(chrome.runtime.getManifest()) {
success();
} else { // will return undefined in an orphaned script
failure();
}
} catch(e) { // currently doesn't happen, but may happen
failure();
}
}
function handler() {
heartbeat(
function(){ // hearbeat success
/* Do stuff */
},
function(){ // hearbeat failure
someEvent.removeListener(handler);
console.log("Goodbye, cruel world!");
}
);
}
someEvent.addListener(handler);
Finally, there is a proposal to make a special event for this situation, but it's not implemented yet.
Specifically for updates when the extension is reloaded, you can make it inject scripts into existing pages and let old scripts know they should deactivate; however, since your question is about extension being removed, it doesn't help.
With the hard part done, actual removal of event listeners depends on how you added them, but should be straightforward.

onDisabled fired when app or extension has been disabled.
chrome.management.onDisabled.addListener(function callback)
EventTarget.removeEventListener() removes the event listener previously registered.
var div = document.getElementById('div');
var listener = function (event) {
/* do something here */
};
div.addEventListener('click', listener, false);
div.removeEventListener('click', listener, false);
I think you don't need to this, since once your extension is disabled, your event listener will be removed and won't be injected.

Related

When reverse engineering code to make a web page plugin, how can I find what function is called when a certain element receives an event?

I run into this problem quite often when I decide to try tinkering with a 3rd party site for a browser plugin, let's say I want to make a simple auto-play plugin for a video site that doesn't have an autoplay feature. There's a UI element that I know triggers their internal function for playing the video, but I dont know how to identify that function by inspecting the element in the console.
What tricks / methods can I use to be able to trigger that function manually without the user actually clicking the element?
In Chrome dev tools I think you can add a breakpoint for when an event is fired which might allow you to find the function that the 3rd party calls or you could simulate the click event using this code from MDN:
function simulateClick() {
var event = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
var cb = document.getElementById('checkbox');
var cancelled = !cb.dispatchEvent(event);
if (cancelled) {
// A handler called preventDefault.
alert("cancelled");
} else {
// None of the handlers called preventDefault.
alert("not cancelled");
}
}
For more information see MDN.
If you know that clicking a button or an element somewhere results in the video playing, often it'll be simple enough to just call .click() on that button.
With the mouse hovered over the element with the functionality, right click and Inspect at that point, and the Elements panel of the developer console should bring you to the element the mouse is over. Figure out a selector or a process to uniquely identify the element, and then .click() it, eg
document.querySelector('.video-container .play-button').click();
You also may need to wait for the video container / play button to be created in the DOM first, in which case you can use MutationObserver or a setInterval to wait for the site to be ready.
An alternative, trickier method is to, when inspecting the element, go to the Event Listeners panel, and look for listeners attached to click / mousedown (etc) events. Sometimes, these functions are callable from the global scope, in which case you can call those functions directly instead of clicking the element.
If the function isn't callable from the global scope, a hacky method to get a reference to it anyway is to monkeypatch addEventListener before the page loads, listen for when the listener you want gets attached, and then call the function passed. (But the earlier methods are much simpler and preferable when possible)

Missing HTML elements to be hidden or shown

I've implemented a chat application using socket-io and nodejs. The application is running fine but, sometimes, I'm facing problems to treat HTML content because when I try to $('#id').hide() or $('#id').show() nothing happens because the element id is not available.
If I try to refresh page pressing F5, sometimes it works because that element is rendered before I try to hide or show it. I got this behavior while debugging using Google Developer tools but I'm not sure if it's the "real why".
I was trying to find on Internet what is the life cycle of DOM elements but I didn't find something that could clarify my thoughts.
I'm trying to reproduce this problem on development environment but I'm pretty far of reach the problem:
<script>
console.log('Creating socket');
var socket = io();
console.log('Socket created');
socket.on('connected', function (data) {
console.log('connected to server');
});
function timeout() {
setTimeout(function() {console.log('sleeping')}, 5000);
}
$(document).ready(function(){
timeout(); // is possible to stuck process on this point?
console.log('Ready');
});
</script>
No matter where I put socket.on('connected'.. because it's always called after console.log('Ready'). Based on this, my theory of F5 refresh is not correct and I feel that I'm running in circles.
Anyone have an idea why HTML elements are not present sometimes?
And, If I use socket.on('anyevent, {}) inside $(document).ready(function(){} do I have any guarantee that the event will only be processed after page being full rendered?
On a real world, all our sockets events are inside $(document).ready(function(){} but still not hiding or showing some html elements because they aren't present.
I am not sure about your HTML and code structure but this sounds like you are binding your event listeners to a dynamically added element but this element does not exist at the time of the binding.
If my understanding is correct, you need to add the binding on an element but base the action on the newly added element, something along the lines of:
// Add event listener, bound to the dynamically added element
$("button").on('click', $("#newElemId"), function(){
// if element exists, toggle it
if($("#newElemId").length){
$("#newElemId").toggle();
}else{
console.log('element not present yet');
}
});
See demo below:
$(function(){
// define function to add an element to the DOM
var addElement = function(){
var newElementAsObj = $(document.createElement('div'));
// add an id for querying later
newElementAsObj.attr('id', 'newElemId');
// add some text (so it's visible)
newElementAsObj.text('New Element');
$("#container").append(newElementAsObj);
console.log('new element added!');
}
// add a new element after a few secs
setTimeout( addElement, 5 * 1000); // time is in ms so 5*1000 = 5secs
// Add event listener, bound to the dynamically added element
$("button").on('click', $("#newElemId"), function(){
if($("#newElemId").length){
// if element exists, toggle it
$("#newElemId").toggle();
}else{
console.log('element not present yet');
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<button>Toggle</button>
</div>
First, regarding this:
$(document).ready(function(){
timeout(); // is possible to stuck process on this point?
console.log('Ready');
});
No, it's not possible. But you don't need to wait there. You can remove that timeout function entirely.
You should move the socket.on('connected', function ... into $(document).ready(... because you don't want to respond to any socket events until the document is ready.
<script>
console.log('Creating socket');
var socket = io(); // It's fine to do this before the document loads
console.log('Socket created');
$(document).ready(function(){
socket.on('connected', function (data) {
console.log('connected to server');
});
console.log('waiting for connection...');
});
</script>
JQuery documentation describes that you can use $(window).on('load', function() {}) to run your code after the entire page, not just the DOM, is ready. You might try that if it's not enough that only your DOM is ready, but you need to wait for the whole page to be loaded.
https://learn.jquery.com/using-jquery-core/document-ready/
If I try to refresh page pressing F5, sometimes it works because that element is rendered before I try to hide or show it. I got this behavior while debugging using Google Developer tools but I'm not sure if it's the "real why".
Are you dynamically creating that element for example with $().append() or similar? If so, make sure that you actually create the element before you try to interact with it. If the element is not created dynamically, make sure that the code that interacts with the element is inside the $(document).ready or $(window).on('load') callback.
No matter where I put socket.on('connected'.. because it's always called after console.log('Ready'). Based on this, my theory of F5 refresh is not correct and I feel that I'm running in circles.
This happens because establishing the socket connection takes more time than to render the DOM. It's generally good idea to attach the event listener as soon as possible to not miss any events. If you attach the event listener only after the DOM has loaded, you might miss some events. But be aware, that if you manipulate the DOM inside the event listener callback, then you cannot be sure that the DOM is loaded and your target element is there, unless you attach the event listener after the DOM has loaded. So I recommend to attach event listeners after the DOM has loaded. At least those that contains some code to modify the DOM.
Anyone have an idea why HTML elements are not present sometimes?
There are not many possible reasons for this. Either your elements are not yet loaded or your code has removed them for some reason. I suggest putting breakpoints to the places where you create the elements or manipulate them somehow and see what the execution order is.
And, If I use socket.on('anyevent, {}) inside $(document).ready(function(){} do I have any guarantee that the event will only be processed after page being full rendered?
You have a guarantee that the callback function will be executed when the anyevent occurs and when the DOM is ready, that is, all the static html elements are there.

Web Components ready flag

We are using Web Components and Polymer on our site, and have quite a few bits of Javascript which wait for the "WebComponentsReady" event to be fired before executing. However, we have some asynchronous JS files which occasionally add an event listener for the event after it has been fired, meaning the script we want to run is never run.
Does anyone know if there is a flag for Web Components being ready which can be checked?
Something like this is what we would need:
if(WebComponents.ready) { // Does this flag, or something similar, exist??
// do stuff
} else {
document.addEventListener('WebComponentsReady', function() {
// do stuff
}
}
Any help appreciated.
The following flag is set during bootstrap
window.CustomElements.ready

Should I remove event listeners in jQuery?

My site works on jQuery + AJAX and has the only javascript file, which loads once when a user opens any page, so I'm used to add event listeners to all elements like: $(document).on(...).
In a while I'd noticed that there are too many .on(...) in the code, and I got afraid. I'd taken 9 pills and forced it to delete useless listeners every time when a user click on a link / back button.
function page_reload(){
if(c.r == 'http://example.com/page1'){
$(document).on('click', '#send', func.send);
$(document).on({mouseenter: func.me, mouseleave: func.ml}, '#chan');
}else{
$(document).off('click', '#send');
$(document).off('*', '#chan');
}
}
So is there any sense? Maybe a big number of listeners do some bad thing I don't know about?
When you attach a listener to an event, it does take memory and it can (if totally unchecked) cause memory related issues. In my experience, it is best to employ cleanup methods in your objects that, when a certain event fires, you use your .off() methods to unregister your event listeners.
The particular logic to these types of methods will vary depending on your project but something of the form:
var MyApp = {
cleanup: function cleanMyApp(event) {
this.off('#myId1', myMethod1);
this.off('#myId2', myMethod2);
}
}
$('document').on('ready', function() {
$(document).on('importantEvent', function(event) {
event.preventDefault(); // if you need to
MyApp.cleanup();
});
// or
$('#elem').on('something', MyApp.cleanup);
});
So yes, having too many listeners registered at a time can cause issues but you can monitor memory usage with your browser's dev tools and the like. In particular you can run out of stack (and heap?) memory and possibly crash the browser.
There is also a great answer here on dealing with these kinds of issues.

Different actions with onbeforeunload

I am working on a simple chat script using Ajax and want to indicate when a user leaves the page. Have read several docs and found this works:
window.onbeforeunload = leaveChat;
function leaveChat(){
... my code
return 'Dont go...';
}
Unfortunately (and logically), if they cancel the exit, my code is still executed and they are flagged as leaving even though they are still on the page? It should only execute if the confirm leaving the page. Any suggestions?
I would use onunload, but it doesn't seem to work in any of my browsers (Chrome, IE).
First, you should add the event handler using:
window.addEventListener('beforeunload', function() {
// Confirmation code here
});
window.addEventListener('unload', function() {
// fire pixel tag to exit chat on server here
// UI interactions are not possible in this event
});
For further research:
unload event reference
beforeunload event reference
Window.onunload reference

Categories

Resources