Event like "DOMLastNodeRemoved" to prevent multiple executions of code - javascript

First of all: I know that there are some questions similar to this, but I could not found any alternative to it, but to ask.
Hello!
Is there any event like "DOMLastNodeRemoved" (or FirstNode, since u can't know for sure when it will be the last node removed) to prevent multiple executions of code?
Here's the deal, I'm using Genexus. The only way to make an event work in this weird SPA concept is using DOMNodeRemoved event.
But whenever you click a button, it will insert a lot of nodes, making the function execute like 25 times instead of just one.
Let's go to an example:
I'm currently with this code:
$(document).on("DOMNodeRemoved", function() {
$(".Button").click(function() {
setTimeout(
function() {
toastrgx();
$('.gx-warning-message').css('display', 'none');
}, 800);
});
function toastrgx() {
var meuLoader = new Loader($);
meuLoader.Insert();
setTimeout(function() {
meuLoader.Remove();
var text = $('.gx-warning-message').text();
toastr.error(text, '');
}, 500);
};
});
I need to use timeout because the DOM is still loading up the message.
Anyways, with this code I'll get this result presented in the image:
If i use something like preventPropagation (at least the way I used) it will run my code only once, but returns a lot of errors in the console (one for each execution) and also breaks my loader.
BTW, I have power only over one custom script, everything else is generated by Genexus.
So, if I use any other normal event like document.ready, it will fire the first time I load a page within my MasterPage, but, if I click in any other page, it will not fire the event again.
So, my question is:
There is any way to execute a code only once in my case?
Is it something that I'm not seeing clearly like another event or another way to make the code work?
If the question sounds silly, sorry, i'm a newbie in JS.

The way to go in this case is creating a User Control and using its show function, that is executed by the generated application every time the control should update (for example, when the page is loaded or when a property of the control is modified).
Why a User Control?
User Controls offer a standard interface to interoperate with GeneXus generated applications. This interface is well documented and abstracts you from the inner workings of the generated applications.
If you try to extend GeneXus by including scripts, you will be exposed to unexpected behavior and changes without previous notice.
How to create a User Control
To create a User Control I recommend using the GeneXus User Control Generator, for Atom. Using this Atom package you can easily create a GeneXus User Control.
Follow the steps to create a User Control and choose Web when prompted for the supported platforms by the package.
How to have code execute only once, when the page is loaded
After creating the User Control, open the src/<YourUserControlName>Render.js file and edit the show function this way:
this.show = function() {
if (!this.IsPostBack) {
// This code will be executed only, when the page is loaded.
}
}
Inside the if you can code whatever you need to execute only once per page.
How to deploy a User Control
To build and deploy the User Control to your GeneXus installation directory, follow the build process steps described in the package documentation.
Once you have deployed your User Control, you should include it in the master page object, to start using it, instead of the script you are using now.

Related

selenium + chrome.fileSystem.chooseEntry = invalid calling page error

I am writing a Selenium script to test a Chrome app that uses the Chrome.fileSystem.chooseEntry API to select a directory. When I do this manually, it works fine. But when I do this in a Selenium script, I get back this error:
Unchecked runtime.lastError while running fileSystem.chooseEntry: Invalid calling page. This function can't be called from a background page.
Any ideas on how to make Selenium and chooseEntry play nicely together?
I updated to the latest Chromedriver, but still no luck. I also looked at ChromeOptions, but didn't see anything that looked like it would be helpful. The interwebs doesn't seem to have much to say about Selenium and chooseEntry. I'm on version 51 of Chrome.
I'm down to thinking I'll need a special javascript entry point to set the path values for testing instead of using chooseEntry. But I would strongly prefer to not have a separate code execution path for my tests. Anybody have a cleaner solution?
EDIT: per commenter's request, here's the offending code:
chrome.fileSystem.chooseEntry({type:'openDirectory'},function(entry) {
chrome.fileSystem.getWritableEntry(entry,function(writeable_entry) {
console.log("got writeable entry");
});
}, function(e) { errorHandler(e); });
EDIT #2: I've gone with the special javascript entry point hack. In manual mode -- i.e., not running under Selenium -- I run code that executes chooseEntry, and then use the retainEntry API to get the entry id. I added an entry point in my javascript to take an entry id and call the restoreEntry API to turn it back into an entry. I also modified my code so if this entry object is set, then use that as the file instead of calling chooseEntry. Lastly, I modified my Selenium script to call the restoreEntry entry point before running the rest of the script.
This is not ideal, since now my test code execution path is somewhat different from my actual live-human-being-at-the-controls code execution path. But at least it lets me use Selenium scripts now. Of course, if anyone can think of a non-horrible way to solve this solution, I'd love to hear about it.
EDIT #3: Per #Xan's comment, corrected my terminology from "extension" to "Chrome App."
I can only offer this horrible hack. For Chrome Apps under OSX I created folder favorites and use Robot keyPress to navigate and select the 'favorite' folders needed for the App. The only possible redeeming factor is that it does mirror a valid/possible actual human interaction with the file interface.
private void selectOSXFolderFavorite(int favorite) {
// With an OSX file folder dialog open, Shift-Tab to favorites list
robot.keyPress(KeyEvent.VK_SHIFT);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_SHIFT);
// move to the top of favorites list
int i = 40;
while (i-- > 0) {
robot.keyPress(KeyEvent.VK_UP);
robot.keyRelease(KeyEvent.VK_UP);
}
while (favorite-- > 0) {
robot.keyPress(KeyEvent.VK_DOWN);
robot.keyRelease(KeyEvent.VK_DOWN);
}
// Send an enter key to Select the selected folder
robot.keyPress(KeyEvent.VK_ENTER);
robot.keyRelease(KeyEvent.VK_ENTER);
}

How can I define a property on the tab's main window object?

I'm trying to define the property jQuery on the window object from a content script with code in the setter. That way, when the actual jQuery object is defined, I can use it right away. I seem to be unable to get it right, though.
The target website is Outlook.com. That's the webmail version of Outlook.
I tried to put the code in the content script directly, but even if I put "all_frames": true in the content_scripts section of the manifest (so the code gets injected into every frame), it isn't working.
function afterInit(){
var _jQuery;
console.log('This gets logged');
Object.defineProperty(window, 'jQuery', {
get: function() { return _jQuery; },
set: function(newValue) {
_jQuery = $ = newValue;
console.log('This is never logged!');
$(document.body).append($('<span>I want to use jQuery here</span>'));
}
});
}
afterInit();
I verified that window.jQuery is properly defined afterwards by the actual jQuery function/object, but my setter code is never executed.
I also tried it with message passing: I send a message with the code as a string to a background script, and use executeScript to execute it on the correct tab, but this also doesn't work.
chrome.runtime.sendMessage(
{action:'jQueryPreInit', value: '('+afterInit.toString()+')();'});
And in my background script:
chrome.runtime.onMessage.addListener(function(message, sender, callback) {
switch (message.action){
case "jQueryPreInit": chrome.tabs.executeScript(sender.tab.id, {code: message.value});
}
});
If I put something else than the Object.defineProperty code in the executeScript code, that works fine. I only have problems defining the property.
(quotes are from a comment)
I want to use the jQuery provided by the page itself. I could try inserting the same jQuery file as Outlook does and hope it gets loaded from cache, but I'd rather just keep my extension as clean as possible, and use what is already available.
Your attempt at optimizing the extension is not workable / not recommended.
First off, you will not be able to use the page's code anyway because of the isolation between content script and webpage code. You cannot obtain a reference to page's own jQuery/$.
But let's for a moment suppose that you could. And then the site updates jQuery to another version, renames the jQuery object or stops using it entirely, which is outside your control. Result: your extension is broken. This is, partially, the rationale behind the isolation in the first place.
As a result of the context isolation, you are guaranteed there are no conflicts between your copy of jQuery and whatever runs on the site. So you don't need to worry about that: use your copy, and use the standard $ to access it.
Bundling a <100 KB file with your extension as a one-time download that makes sure code is available 100% of the time and with at worst disk-access latency is not making it less "clean", quite the opposite. It's a common practice and is enshrined in the docs.
Looking at your actual code, it executes in the content script context (regardless whether it's through manifest or executeScript), not in the page context. As such, no matter what the page does, $ will not be defined there.
I verified that window.jQuery is properly defined afterwards by the actual jQuery function/object [...]
I assume that you tried to execute window.jQuery in the console; by default, that executes it in the page context, not in your content script context (therefore, not reflecting the state of the content script context and not invoking your getter/setter). If you want to test your content script, you need to change top in the context drop-down above the console to your extension's context.
All that said, however,
When all is said and done, I want to use jQuery's ajaxSuccess function to execute code every time an e-mail is opened in the read pane.
Here we've got a problem. Since the content script code and webpage code are isolated, your code will never know about AJAX executing in the page's copy (not through ajaxSuccess, anyway).
Possible courses of action:
Rely on other methods to detect the event you want. Perhaps monitoring the DOM.
Inject some code into the page itself; the only way to do so is by injecting a <script> tag into the page from the content script. There, you can access the page's copy of jQuery, attach your listener and message your content script when something happens.
Rely on the background page to detect activity you need with webRequest API. This will likely intercept the AJAX calls, but will not give you the reply contents.
Final note: this may not be as simple as AJAX calls; perhaps the page maintains a WebSocket connection to get realtime push updates. Tapping into this is trickier.
Thanks to Xan, I found there are only two ways to do this.
The first is by adding a <script> element to the DOM containing the appropriate code. This is a pretty extensive StackOverflow answer on how to do that: https://stackoverflow.com/a/9517879/125938.
The second is using Javascript pseudo-URLs and the window.location object. By assigning window.location a bookmarklet-style URL containing Javascript, you also bypass certain security measures. In the content script, put:
location = 'javascript:(' + (function(){
var _jQuery;
Object.defineProperty(window, 'jQuery', {
get: function() { return _jQuery; },
set: function(newValue) {
_jQuery = $ = newValue;
console.log('totally logged!');
$('<span>jQuery stuff here</span>');
}
});
}).toString().replace(/\n/g, ' ')+')();';
The reason I/you were originally failing to define it, was because both methods of code injection we were using, caused our code to be sandboxed into isolated worlds: https://developer.chrome.com/extensions/content_scripts#execution-environment. Meaning, they share the page's DOM and could communicate through it, but they can't access each other's window object directly.

When does the browser's event loop start?

I'm using a framework which features auto-connecting to server on page load. I can disable it by passing options arguments, but the line that confuses me is this:
You can prevent this initial socket from connecting automatically by disabling io.sails.autoConnect before the first cycle of the event loop elapses.
My questions are:
When does the first cycle of the event loop elapses?
Is this behaviour the same across ALL modern (IE9+) browsers?
I have a bunch of scripts (in <body>) loading between the lib and my entry file. Does this affect when the first cycle elapses? EDIT: Yes, it does.
How can I ensure my code runs before the first cycle elapses?
Is this kind of implementation of auto-connect considered good practice?
The documentation for the source file is a little more explicit; it says "This can be disabled or configured by setting io.socket.options within the first cycle of the event loop."
Basically what's happening is that there exists within the library a setTimeout(fn, 0) call, which is idiomatic for starting a parallel process. However, in the JS standards it's explicitly stated that JS is single-threaded: in other words, even though setTimeout and setInterval are asynchronous they are not actually parallel in the sense that any of their code will be executing simultaneously with any other code. So they wait until the current function is over before they execute. This queueing mechanism is known as the JavaScript event loop.
I believe that what you are asked to do by the script author is to modify the source to include the relevant change, perhaps at the bottom of the file for your convenience.
It is also likely that a similar effect will be achieved by putting a <script> tag underneath the one that loads the given JS. This has not been explicitly standardized by HTML 4, but may be implicitly standardized in the new HTML 5 spec (it's a complicated interaction between different parts of the specs).
In terms of HTML5, it looks like the current specs say that there is a afterscriptexecute event and a load event which occur immediately after any remote script is loaded (or, if it's an inline script, the load event is scheduled as a task -- I am not sure when those occur). So you might be able to guarantee it without modifying the script by instead doing:
<script>
function do_not_autoload() { /* ... */ }
</script>
<script onload="do_not_autoload()" src="./path/to/sails.io.js"></script>
but I'm not sure what the compatibility table for script#onload is going to look like.
I made you a jsfiddle which can be used to grab a 'fingerprint' for different browsers to get an idea of what evaluation orders are out there in the wild. The * is the document.body.onload event. On my system it produces:
Firefox 32.0.3 : cafdbe*
Chrome 37.0.2062 : cafd*be
IE 11.0.9600 : cafd*be
In other words,

Firefox Extension: Access the DOM Before It's Loaded

I'm trying to create a Firefox extension that fires my Javascript code before any of the current page's Javascript is fired. My Javascript code will basically control whether or not the page's Javascript code can be executed or denied.
I first started out by trying to follow this answer, but I couldn't really figure out how to get it to work and realized I was relying on onDOMContentLoaded, which loads after the Javascript has already executed.
I then turned my attention toward XPCOM, but once again didn't really understand what the Firefox tutorials were telling me.
I've recently been trying to make an extension through Firebug, but I seem to hit the same problem... only having access to the Javascript after it's been parsed/executed. Here's the resulting code that I wrote. I think if I could access the file's objects in the onExamineResponse event, my problem could be solved, but I don't know how to do that... I'm talking about this code:
BeepbopListener.prototype = {
onRequest: function(context, file) {
...
},
onExamineResponse: function(context, file) {
FBTrace.sysout("onexamineresponse " + file); // this returns something like
// '[xpconnect wrapped (nsISupports, nsIHttpChannel, nsIRequest, nsIUploadChannel, nsITraceableChannel, nsIHttpChannelInternal)]'
// but I don't know how to access those elements...
var pattern = /\.js$/;
if (pattern.test(file.href) && FBTrace.DBG_BEEPBOP) {
FBTrace.sysout("ONEXAMINE DOESN'T EVEN GET IN THIS IF SO YOU WON'T SEE THIS");
}
},
...
};
So my question is... is there a tutorial out there that shows me how I can get access to all Javascript code on a page before it's executed? Also, if anyone has any helpful insight, I'd love to hear it. Oh, and if y'all need more code from me, just let me know, and I'll post it.
You can access a new document before any JavaScript code runs by listening to the content-document-global-created observer notification. However, the document will be empty at this point and JavaScript code will run as soon as the parser adds a <script> tag - you cannot really prevent it. Here are the options to control script execution that I am aware of.
1) Disable all JavaScript for a window using nsIDocShell.allowJavascript:
wnd.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShell)
.allowJavascript = false;
This is an all or nothing approach. Note that JavaScript stays disabled even when a new document loads into the same frame.
2) Implement the nsIContentPolicy interface in an XPCOM component and register it in the content-policy category (via nsICategoryManager). Your shouldLoad() function will be able to block scripts selectively - but it will only called for external scripts (meaning <script src="...">), not for inline scripts on the page.
3) Use JavaScript debugger service to intercept script execution. You could use jsdIDebuggerService.interruptHook to step through JavaScript execution and abort the script whenever you like. But that would slow down JavaScript execution very significantly of course. At the very least you should use jsdIDebuggerService.addFilter() to restrict it to a particular document, otherwise you will slow down the entire browser (including browser UI).
I'm trying to create a Firefox extension that fires my Javascript code before any of the current page's Javascript is fired. My Javascript code will basically control whether or not the page's Javascript code can be executed or denied.
Start by completely preventing the document from getting parsed altogether then on the side, fetch the same document, do any processing on this document and then inject the resulting document in the page. Here is how I currently do just that https://stackoverflow.com/a/36097573/6085033

Connect Firefox extension with C# application

I need to be able to make an event such that every time a user loads a new page and closes firefox, I need it to call a method in my C# application that takes care of maintaining the user model. I know for sure I need to create some type of firefox extension where I use javascript to check such an event. However, I have no idea how I am going to integrate my C# application with the firefox extension. Can someone provide me with some guidance?
I'll help you out with the parts of the question that I'm familiar with (Javascript based add-ons), and offer some suggestions for the other parts. Here goes nothing!
Add-ons
Firefox add-ons easily provide the tools you need to detect page loads and opening / closing firefox.
To detect page loads you can register a listener to the DOMContentLoaded event in window.
window.addEventListener("DOMContentLoaded", function(event){
var url = event.originalTarget.location.href;
alert("Oh yeah, a document is loading: " + url);
}, false);
Alternatively, you can register a nsIWebProgressListener to listen for location changes. This probably closer to what you want, since DOMContentLoaded is also triggered for iframes.
var listener = {
//unimplemented methods (just give functions which do nothing)
onLocationChange: function(aWebProgress, aRequest, aLocation){
var url = aLocation.asciiSpec;
alert("Oh yeah, a the location changed: " + url);
}
};
gBrowser.addTabsProgressListener(listener);
To detect firefox open / close you need to first understand how firefox add-ons work with respect to multiple windows. When a new window of firefox is launched, you basically have 2 separate copies of your code running. So, if you care about firefox windows being opened and closed you can simply do:
window.addEventListener("load", function(event){
alert("Looks like you just opened up a new window");
}, false);
window.addEventListener("unload", function(event){
alert("Awh, you closed a window");
}, false);
But, most likely you want to detect opening / closing firefox as an entire application. This is achieved using a code-sharing mechanism called Javascript Modules. Javascript modules are loaded just once for the lifetime of the application. So, they enable you to share information between windows. Simply counting the number of windows opened and closed should be sufficient for this functionality.
var EXPORTED_SYMBOLS = ["windowOpened", "windowClosed"];
var windowsOpened = 0;
function windowOpened(){
if( windowsOpened === 0) {
alert("The first window has been opened!");
}
windowsOpened++;
}
function windowClosed(){
windowsOpened++;
if( windowsOpened === 0) {
alert("The last window has been closed!");
}
}
Then you can simply attach the aforementioned event handlers to call these 2 methods from their corresponding load and unload events.
So, this is all great and everything, but now you have to twiddle with the details of getting a baseline Firefox add-on setup. Fortunately, Mozilla has provided a handy Addon Builder to ease this. All the code about (except the Javascript module) should be placed in the ff-overlay.js file (assuming you use the linked builder).
C# communication
I'm a little less knowledgeable about the interprocess communication with C#. However, maybe I can point you in the right direction and let the smart people at SO fill in the rest.
I believe COM Objects are a method of communication between processes on Windows. So, you could build in a Binary Component to your add-on to perform the communication. However, as far as I understand it, setting up binary components is much more difficult than a standard javascript-based add-on. Either way, Mozilla provides a guide for setting it up in Visual Studio.
If you want to stay away from binary components you are left with the javascript enabled components of the SDK. This includes socket communication, files, pipes, a sqlite database etc. This SO question addresses exactly the question you're asking. If it were me, I would choose them in this order.
Sqlite Database
Named Pipes
Sockets
(1) because there is a lot of code samples available for this, and would be easy to implement on both sides. (2) because this would be the way I'd implement IPC if I were given full control of both sides of the application. (3) is last because I hate that crap (maybe I'm biased from Distributed Systems in college).
tl;dr
The page load stuff should be pretty simple. Check out the Addon Builder to get going with a FF addon, and here to see about detecting page loads.
The C# communication is doable, and addressed in this SO Question. I'd do it with a sqlite database for ease if it were me.

Categories