How to trigger a command defined in another extension with chrome.commands - events

I have a chrome extension that listens keydown on ctrl+shift+u.
If I trigger an event ctrl+shift+u in a content script, the plugin's listener won't fire.
// ( I am using jQuery )
var e = jQuery.Event( "keydown" );
e.which = 85;
e.keyCode = 85;
e.shiftKey = true;
e.ctrlKey = true;
$( document ).trigger( e );
How can I properly trigger this event? ( preferably from a content script or from another extension )
I would not say this question is a duplicate. I succeeded to trigger the event, only the extension's handler won't fire. This question rather refers to emulating browser actions, not content-based events.
UPDATE:
The extension that listens the event is a third party software, there is no way to dig inside it's code. This keyboard event can be configured on browser-level in chrome://extensions/configureCommands

So, your question is thus: How to trigger a command defined in another extension with chrome.commands
That is not possible from extension code if you don't control the extension receiving the event.
You may have success with a Native Host, but that's a very roundabout route.

Related

How to click UI element using JXA

Please tell me how do I click in point coordinates in application window?
I trying to UI automate my application on OSX 10.10 using JXA technology.
In documentation I found that it's possible using click at event. By I'am beginner of JXA and cant find how make a call.
Code snippet which I tried in Script Editor:
var app = Application('my_application_path')
app.window.click.at('{100,100}')
Thank you for help
You can interact with an application's user interface using the System Events application. Here is a script that clicks at certain coordinates in Safari:
// Activate Safari, so you will be able to click like a user
Application("Safari").activate()
// Access the Safari process of System Events
var SystemEvents = Application("System Events")
var Safari = SystemEvents.processes["Safari"]
// Call the click command, sending an array of coordinates [x, y]
Safari.click({ at: [300, 100] })
If you want to click a specific button (or other element of the user interface), it is more appropriate to click that specific element. For example:
// Click the third button of Safari's first window to minimize it
Safari.windows[0].buttons[2].click()
To learn what user interface elements can be interacted with and how, check out the Processes Suite in System Events' scripting dictionary. To open the dictionary, in Script Editor's menu bar, choose Window > Library, then select System Events in the Library window.
See https://github.com/dtinth/JXA-Cookbook/wiki/System-Events#clicking-menu-items
For example:
var fileMenu = proc.menuBars[0].menuBarItems.byName('File');
Below is an example of a portion of a script I wrote that automates creating mailboxes (aka folders) in Mail. I ended up using the UI file menus and click because using make() in the Mail DOM had issues for me. Hope it helps someone.
(() => {}
//this is part of a script that automates creating mailboxes (ie folders) in Apple Mail
//I used the file menu UI because when I tried the Mail library and make() method
//there was strange behavior when trying to interact with the new mailbox.
//However, when creating the new mailboxes thru the file menu, all seems to work fine
const Mail = Application('Mail');
const strId = Mail.accounts.byName('Exchange').id();
const exchange = Mail.accounts.byId(strId);
const activeClientFolder = exchange.mailboxes.byName('ActiveClient');
const SysEvents = Application('System Events');
const mail = SysEvents.processes.byName('Mail');
//next two lines insure Mail will be open and in front
mail.windows[0].actions.byName('AXRaise').perform();
mail.frontmost = true;
const mailboxMenu = mail.menuBars[0].menus.byName('Mailbox');
//below shows EXAMPLES of using .click(), keystroke(), and keyCode()
let newFolder = function (parentFolder, newFolderName, addTrailingDelay = true) {
//next line will select the parent mailbox (aka folder) where the new mailbox will be inserted
Mail.messageViewers[0].selectedMailboxes = parentFolder;
mailboxMenu.click();
delay(.2);
mailboxMenu.menuItems.byName('New Mailbox…').click();
delay(.2);
SysEvents.keystroke(newFolderName);
SysEvents.keyCode(36);
//delay is needed when creating multiple mailboxes with a loop
if (addTrailingDelay == true){
delay(1);
}
}
//now the payoff
const count = newActiveClients.length;
for(let i=0;i<count;i++){
/* Client Root Mailbox */
newFolder(activeClientFolder, newActiveClients[i], true);
/* Client Email Folders */
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]), 'Client', true);
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_FYI_Sent');
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_FYI_Inbox');
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_FYI_Client_To');
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_From', false);
}
})()

How to properly remove an event listener?

I've got a few event listeners than i need to able to remove once they are no longer needed.
I've followed the advice of a few others and included the RemoveEventListener inside the function itself like:
object1.addEventListener(MouseEvent.MOUSE_DOWN, function StartMove(event:MouseEvent):void
{
mousePoint = new Point(MC.mouseX, MC.mouseY);
objectOffset = new Point(int(mousePoint.x - object1.x), int(mousePoint.y - object1.y));
mousePoint.y -= objectOffset.y;
mousePoint.x -= objectOffset.x;
mousePressed = true;
event.currentTarget.removeEventListener(event.type, StartMove);
});
but this simply creates the listener and then deletes it when its used once.
The event listener functions are created inside a larger function (MouseMoves()) as they make use of local variables within MouseMoves(). (not sure whether this is an appropriate way to do this.)
So if i try to remove the listeners externally they cant see the function name to remove, and i cant declare the functions outside MouseMoves() cus they use variables inside it.
feel like this is bad design on my part but i'm not sure where?
is there a 'proper' more standard way to remove an event listener?
thanks.
When you ready to have you event listener removed you could use the same syntax that you used to add it in the first place. So somewhere in your code when you're ready to remove this listener you could have this
object1.removeEventListener(MouseEvent.MOUSE_DOWN, function StartMove(event:MouseEvent):void
This way your being specific about which object you want to remove the listener from.
Your mouse function would look like this:
object1.addEventListener(MouseEvent.MOUSE_DOWN, function StartMove(event:MouseEvent):void
{
mousePoint = new Point(MC.mouseX, MC.mouseY);
objectOffset = new Point(int(mousePoint.x - object1.x), int(mousePoint.y - object1.y));
mousePoint.y -= objectOffset.y;
mousePoint.x -= objectOffset.x;
mousePressed = true;
});
Yes, your coding of:
event.currentTarget.removeEventListener(event.type, StartMove);
can be used without problem in the StartMove function. It is just to unregister the event in the listener, don't worry if it is in the event function. And of course you can also use:
object1.removeEventListener(MouseEvent.MOUSE_DOWN, StartMove);
in the StartMove function as well if you do not want to use event.currentTarget.removeEventListener.

Extension: How to intercept AJAX responses?

Since extensions can not access unsafeWindow, like Firefox can, to hook into DOM scripts am I looking for other ideas so I come to SO for help!
How about using some code to inject into DOM and sending the intercepted response to a background page, which then does some initial processing before calling a content script for final processing. When done, it answers to the background with a modified response, or the original (it depends), and the background page sends the response back to DOM which handles it to the DOM script response function.
There is just one problem with this, a background page cant communicate with the DOM.
I did a small test with injecting some code, where I output something to the console and an alert. The result wasnt good, as the alert fired but the console was empty - not even an error, which makes me wonder - what console received the output ?
function injectCode(fn){ // Executing an anonymous script
var script = document.createElement('script');
script.type = 'application/javascript';
script.textContent = '(' + fn + ')();';
document.documentElement.appendChild(script); // run the script
document.documentElement.removeChild(script); // clean up
}
var code = function(){
console.log('dom',window);
alert('code injected');
}
injectCode(code);
I also tried addEventListener, with DOMAttrModified DOMSubtreeModified DOMNodeInserted, on DOM elements that change when the DOM ajax response is fully parsed but all failed to fire.
Am I trying to do the impossible, by any means ?
Before continuing, make sure that you know the differences between the script contexts in an extension.
To inject a script from the background page, you have to execute a Content script, which on his turn injects the script as mentioned in your question / here.
Examples (using chrome.tabs.executeScript):
// null = current active tab
// Simple code, background:
chrome.tabs.executeScript(null, {
code: [
'var s = document.createElement("script");',
's.textContent = "console.log(window);";',
'(document.head||document.documentElement).appendChild(s);',
's.parentNode.removeChild(s);'
].join('\n')
});
I can imagine that this method is not doable for a big chuck of code. For a set of pre-defined scripts, you can then use two scripts: the code itself, and a helper script:
// config.js
var fn_code = function() {
console.log(window); ....
};
// helper.js
var s = document.createElement('script');
s.textContent = '(' + fn_code + ')();';
(document.head||document.documentElement).appendChild(s);
s.parentNode.removeChild(s);
// Background:
chrome.tabs.executeScript(null, {file: 'config.js'}, function() {
chrome.tabs.executeScript(null, {file: 'helper.js'});
});
Note: I did not directly link to "config.js", because that complicates the use when using manifest version 2, see "web_accessible_resources".
The previous method only shows how to execute code in one direction (background -> page). If there's a need to activate a background's function from the injected script, you have to define and listen to a custom event handler. See this answer + demo.
Because the code is injected, thus runs in the scope of the page, you have to check the console at the page.
When chrome.tabs.executeScript fails to execute the Content script (eg. because the extension does not have the permission to access a certain page), an error is logged at the console in the background page. This console can be accessed by following these steps.

event listener in jquery for variable change

I want to track a variable as I navigate around my site. How can I use jQuery to output this value without having to worry about tracking down wherever it might be affected and having it update.
var num = 1;
$(#trackvariable).text(num)
I want the above to execute constantly as it were.. I don't necessarily want to do it on a timer. Is there any way to do that?
If you want to listen to changes on a textnode, you can do something like this:
function InitListener () {
var container = document.getElementById ("textContainer");
var textNode = container.firstChild;
if (textNode.addEventListener) {
textNode.addEventListener ('DOMCharacterDataModified', OnCharacterModified, false);
}
}
This will only work in Firefox (and probably Chrome in Safari also):
https://developer.mozilla.org/en/JavaScript/Guide/Working_with_Objects#Defining_Getters_and_Setters
Off the top of my head I would say that the functions changing the variable should just call some sort of update function at the end of them? just an idea. I think even if you set up some kind of custom event it would still have to manually triggered in each function.

Is there a way to break out of event recursion in jQuery without using a global variable?

I have a form with 2 text inputs and 2 span controls. Normally, when textbox A is changed an event is fired to change span A, and when textbox B is changed, an event is fired to change span B.
However, in one particualar case I would like a change either textbox A or textbox B to update both span A and B. I tried wiring the events up to the corresponding controls in this case, but it didn't work because there is much state that is set up in the event building code (not to mention each event calls 'this', which would make the logic use the wrong control if it were fired from a different one than it was intended).
To make things easy, it would be best to pass a string (representing the other text input id) to the event handler at the time it is created, and then calling the change() event manually on the second control. However, this puts things in an infinite loop of recursion. I thought of a way to get around the recursion, but it reqires a global variable.
Is there a better way than this, preferably one that doesn't require a global variable?
ml_inEvent = false;
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event) {
// Other processing happens here...
if (event.data.controlToFire != '') {
var $controlToFire = $('#' + event.data.controlToFire);
if ($controlToFire.length) {
// Use a global variable to ensure this event is not fired again
// as a result of calling the other one
if (!ml_inEvent) {
ml_inEvent = true;
$controlToFire.change();
ml_inEvent = false;
}
}
}
});
You can use the extraParameters argument on .trigger() to break out, for example:
$ctlToVal.bind('keyup change', {feedbackCtl: ml_feedback, controlsToMonitor: ary, validationGroup: this.validationGroup, controlToFire: ctlToFire}, function(event, fire) {
// Other processing happens here...
if(fire !== false) $('#' + event.data.controlToFire).trigger('change', false);
});
You can give it a try here. What this does is the event handler callback not only receives the event object but also any other arguments you pass in, in this case we're just using a simple false and a !=== check this in important so undefined (the parameter not passed at all) still changes both controls. So for instance $("#control").change() would change both controls, but still not loop...you can test that here.

Resources