Call a userscript function from within a webpage to close the current tab - tampermonkey

How can I call a userscript function from within a webpage to close the current tab? Below is a small example of what I am trying to accomplish.
Tampermonkey script (Executed at document start):
// ==UserScript==
// #name Demo
// #namespace http://tampermonkey.net/
// #match *
// #grant none
// #grant window.close
// ==/UserScript==
function tm_function() {
window.close();
}
The webpage that is trying to call the userscript function tm_function().
<html><body><script>tm_function();</script></body></html>
The error I am getting is
Scripts may close only the windows that were opened by them.
Which is the default behavior. However, the userscript has permission to close the tab.

Just like the error says you can't close a window that wasn't opened by them. Its for security reasons. Scripts only have so much power. They can only close windows that were opened by them. I have tried to do that in the past and there is no solution unfortunately. But you can open tabs using javascript you just can't close ones that weren't opened by the code.

Related

CodenameOne Navigating from Splash Screen to "first time use" form in UIBuilder app

I have a tricky situation that only appears in mobile devices, not the simulator, so I need some expert help to troubleshoot. It's like the stateMachine takes me back to the SplashScreen briefly (or does some strange back-transition) after displaying my "FirstTimeSetup" form.
Here's my setup: I used the GUIBuilder to build the app with a form SplashScreen that will show first and then auto-transition to a form Main after some network connections happen in processBackground.
That works fine but on the First time starting, (when a Preference is not set) I want to display a different form: FirstTimeSetup. The best way I could see to do this was the following:
1) in processBackground, immediately return false if it's the first time running so that it doesn't transition to Main.
protected boolean processBackground(Form f) {
if ("SplashScreen".equals(f.getName())) {
if (Preferences.get(PREFS_FIRST_TIME_SETUP,true)){
//return false to indicate that we should not proceed to next_form specified in property
//we do this because postSplashScreen will trigger the load of the "FirstTimeSetup" form
return false;
}
//...continue with normal app initialization if this is not the first time
2) In postSplashScreen, I again check if it's a first-time load and then disable back-commands and call showForm to the First Time Setup form.
#Override
protected void postSplashScreen(Form f) {
if (Preferences.get(PREFS_FIRST_TIME_SETUP, true)) {
//disable back command for this form
setBackCommandEnabled(false);
showForm("FirstTimeSetup", null);
}
}
3) in postFirstTimeSetup method, I display some dialogs to explain the next steps to the user, do some network checks to ensure we can proceed with registration, and then end the method so the user can interact with the dialog and register. It's at this point when on the iOS or Android Device, I see a slide transition to the SplashScreen and then immediately it re-displays the FirstTimeSetup form, and it does this twice before the user is able to interact with the form.
4) Sometimes (it's inconsistent), the postFirstTimeSetup method will execute again (the same dialog boxes prompting the user are displayed again!).
It feels like some SplashScreen automatic transition is still trying to execute after I've returned false from processBackground and the postSplashScreen method is already complete... any suggestions would be helpful to eliminate this strange double-transition!
UPDATE:
With further tweaking and Investigating I realized that this appears to be caused by Android Permissions dialogs which is why it only happens on initially installing the app.
In processBackground, I make the first network calls (prompting network usage permission dialog) and access the device parameters IMEI And UUID to get a device identifier (prompting the "allow access to Phone" permission). After dismissing each dialog, the SplashScreen form appears to be re-entered causing a re-display of the form with transition animation (and I think also re-running the processBackground! re-doing all my splash screen initialization work!).
So here's the updated question: How can I get the Android Permissions dialogs to stop the SpashScreen from reloading?
I've tried moving the commands that trigger out to initVars, but then I get the permissions dialogs on a Blank screen, and then the splash screen just transitions twice rapidly like I shared in this video: youtu.be/2QpdaeigNZ8
I've tried wrapping the two "triggers" (commands that cause the permissions) in a callSerially() so that it would delay the dialogs until the SplashScreen form is at least displayed, but then the form displays, and a permission dialog shows, I click "allow" and then the splashScreen Form display again, then the second permission dialog pops up. I clock allow and then AGAIN a re-display of splashScreen.
The solution is to perform your logic in processBackground() and get rid of postSplashScreen, then return false at the end of this method.
#Override
protected boolean processBackground(final Form f) {
if ("SplashScreen".equalsIgnoreCase(f.getName())) {
new UITimer(() -> {
if (Preferences.get(PREFS_FIRST_TIME_SETUP, true)) {
showForm("FirstTimeSetup", null);
} else {
showForm("Main", null);// or any other form you want to show
}
}).schedule(3000, false, f); //wait 3 seconds before proceeding
}
return false; //always return false at the end
}
Then in your FirstTimeSetup form's postShow() (i.e. postFirstTimeSetup() method), remember to set PREFS_FIRST_TIME_SETUP to false.
Preferences.set(PREFS_FIRST_TIME_SETUP, false);

How can I close the InAppBrowser automatically when specific content is returned?

How can I automatically close the $cordovaInAppBrowser when it goes to my website and returns content "OK"? I would like to do this to avoid displaying the close button in the browser window.
Does anyone know how I can achieve this?
I did find the website that can solve this for those who also need to self close the browser in some condition.Here is the reference link : https://developer.salesforce.com/forums/?id=906F00000009ALSIA2
sample coding:
authWindow.addEventListener('loadstop', function(e) {
var loc = e.url;
//when url is changed check if the url contains your specific callbackURL
if (loc.search(data.callbackURL) >= 0) {
//at this point close your inapp browser
//you will land on the index page within your application.
authWindow.close();
//your code after successful authentication
}
});
you can use something like a timeout function or other than that a even listener other than i would suggest make a custom plugin and use it. The solution provided by #Blouraf is very hacky.
I did find some documentation on the cordova plugin that recommends a listener.

ionic + android back button + sidemenu isOpen detection - not working reliably

I have an odd situation. I am writing an angular + ionic app with a left slide menu and here is what I am trying to do:
1) Trap the Android back button
2) When you tap the back button, if the menu is not open, take you to the menu (open it)
3) If the menu is open and you hit the back button, exit the app
To trap the back button, I have this code in app.run (also tried relocating to a base controller and injecting it with $controller into other controllers, did not make a difference)
What I am noticing is that if I actually tap the 'menu icon' of the menu, the "isOpen" reliably prints true/false alternately as a I open and close the menu
But, when I tap the Android back button, it works the first time (prints true or false), but every subsequent tap does not change the isOpen state, while the menu actually toggles.
This therefore makes it impossible for me to programmatically detect if the menu is open or close within the android back button handler.
It's confusing me why this is only a problem within the android back handler, and not a problem when I tap the menu item. It's the same code that is called in both cases, which is
$ionicSideMenuDelegate.toggleLeft();
My Android handler code:
$ionicPlatform.registerBackButtonAction(function (event) {
$ionicSideMenuDelegate.toggleLeft();
$ionicSideMenuDelegate.$getByHandle('sideMenu').toggleLeft();
$timeout ( function() {
console.log ("Status of SIDE MENU IS : " + $ionicSideMenuDelegate.$getByHandle('sideMenu').isOpen());
},1000);
}, 100);
I've also set up a codepen, though not sure how one can test it on and android device, because every time I try and hit the backbutton on a codepen or jsfiddle, it makes the browser go back a page.
Any insight into what is going on? I've asked in the ionic forum, but haven't been able to find out why (yet) --> hence the post to the SO community in the hopes it reaches a wider audience.
registerBackButtonAction needs a priority to override the other actions:
The priorities for the existing back button hooks are as follows:
Return to previous view = 100 Close side menu = 150 Dismiss modal =
200 Close action sheet = 300 Dismiss popup = 400 Dismiss loading
overlay = 500
Your back button action will override each of the above actions whose
priority is less than the priority you provide. For example, an action
assigned a priority of 101 will override the 'return to previous view'
action, but not any of the other actions.
I've tested this code and it works as expected:
.run(function($ionicPlatform, $ionicSideMenuDelegate, $ionicPopup) {
$ionicPlatform.registerBackButtonAction(function(e) {
e.preventDefault();
if (!$ionicSideMenuDelegate.isOpenLeft()) {
$ionicSideMenuDelegate.toggleLeft();
} else {
navigator.app.exitApp();
}
}, 1000);
});
As you can see I've used priority 1000 to make sure that I override all the default actions.
I've also used preventDefault(). I don't think you need this but, just in case.
This bit of code only works for the left-side menu as I only check:
$ionicSideMenuDelegate.isOpenLeft()
and only open the left one:
$ionicSideMenuDelegate.toggleLeft()
but you can change it to work with the right menu as well.
UPDATE:
If someone is interested to find out more about Android and Back Button this is the best article I've read so far.

How can I tame multiple event listeners in a Fennec extension?

I'm trying to write a restartless add-on for Firefox Mobile that will insert content onto specific web pages. It all seems to work OK until I try disabling then re-enabling the add-on, at which point I get multiple responses to the page load event, and I can't figure out a way to sort them out.
Since Fennec uses the Electrolysis multi-process platform, I know that I need to split my code into chrome and content scripts. My bootstrap.js looks something like this (trimmed for clarity):
function startup(data, reason) {
mm = Cc["#mozilla.org/globalmessagemanager;1"].getService(Ci.nsIChromeFrameMessageManager);
mm.loadFrameScript(getResourceURISpec('content.js'), true);
}
function shutdown(data, reason) {
let mm = Cc["#mozilla.org/globalmessagemanager;1"].getService(Ci.nsIChromeFrameMessageManager);
mm.sendAsyncMessage("GeoMapEnh:Disable", {reason: reason});
}
function install(data, reason) {}
function uninstall(data, reason) {}
Basically, the bootstrap.js just launches a content script, and sends a message to tell it to clean up on shutdown. The content.js sets up an eventListener to watch for page loads, that looks a bit like this:
addMessageListener("GeoMapEnh:Disable", disableScript);
addEventListener("DOMContentLoaded", loadHandler, false );
function loadHandler(e) {
LOG("Page loaded");
// Do something cool with the web page.
}
function disableScript(aMessage) {
if (aMessage.name != "GeoMapEnh:Disable") {
return;
}
LOG("Disabling content script: " + aMessage.json.reason);
try {
removeEventListener("DOMContentLoaded", loadHandler, false );
removeMessageListener("GeoMapEnh:Disable", disableScript);
} catch(e) {
LOG("Remove failed: " + e);
}
}
function LOG(msg) {
dump(msg + "\n");
var consoleService = Cc["#mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
consoleService.logStringMessage(msg);
}
When I first run the extension, everything works fine. An instance of content.js is executed for each browser tab (and any new tabs I open) and my eventListener detects the page loads it is supposed to via the DOMContentLoaded event. When I disable the extension, everything still seems OK: page loads stop being detected.
When I re-enable the extension, it all goes wrong. I still get an instance of content.js executing for each open tab, but now, if I open new tabs, DOMContentLoaded triggers mutiple eventListeners and I can't distinguish which one should handle the event. Worse yet, some of the eventListeners are active, but do not give debug info via my LOG function, and do not all of them get removed if I disable the extension a second time.
I do want to watch all browser tabs, including any new ones, but I only want my extension to insert content on the page that triggers it, and only once per page load. I've tried the following without success:
Calling e.stopPropagation() to stop the event being passed to other listeners. No effect.
Calling e.preventDefault() and testing e.defaultPrevented to see if the event has already been handled. It never has.
Testing if (this === content.document) to see if the eventListener has been triggered by its own page content. Doesn't work, as I get multiple "true" responses.
Making the eventListener capturing. No effect.
Using the load event rather than DOMContentLoaded.
I can't set a shared variable to say the event has been handled as under Electrolysis, the different eventListeners will be executing in different contexts. Also, I wouldn't be able to distinguish between multiple tabs loading the same page and one page load being detected multiple times. I could do this via IPC message passing back to the chrome bootstrap script, but I then I wouldn't know how to address the response back to the correct browser tab.
Any ideas? Is this a Firefox bug, or am I doing something silly in my code? I am using Fennec Desktop v4 for development, and will target Fennec Android v6 for production.

Mobile Firefox (Fennec) add-on: executing code on page loads

I am trying to write a mobile firefox plugin that executes a piece of javascript code automatically every time a page loads. I had written some code for an earlier version of Fennec, but with the multi-processing system in the newer Fennec version (https://wiki.mozilla.org/Mobile/Fennec/Extensions/Electrolysis/), this code had to be ported. I based myself on a tutorial from http://people.mozilla.com/~mfinkle/tutorials/ to get a version working that executes a piece of code whenever an option is selected in the browser menu. This solution consists of two parts, namely overlay.js (for the main (application) process) and content.js (for the child (content) process). Overlay.js is loaded in overlay.xul, while content.js is loaded for new tabs via the following code in overlay.js:
window.messageManager.loadFrameScript("chrome://coin/content/content.js", true);
The code in overlay.js sends a message to content.js whenever the option in the browser menu is clicked, and the required code is then correctly executed (some script tags are simply added to the head of the page). However, I don't know how to execute code automatically on a page load. I tried the following in content.js:
function addCoin(aMessage) { ... }
// this executes the desired code every time an option is clicked in the browser menu
addMessageListener("coin:addCoin", addCoin);
// this attempts to execute the code on every page load; i.e., after this script has
been loaded for the new tab
addCoin(null);
The last statement however has no effect. Then, I tried adding the following statement at the end:
sendAsyncMessage("coin:scriptLoaded", { });
This statement sends a message to the overlay.js script, which registers a listener for this message and in response simply sends the same message as when the option in the browser menu is clicked, i.e., "coin:addCoin". However, this didn't work either. Finally, I tried looking for certain events the overlay.js script could listen for (something like "tabOpened" or something), but couldn't find anything.
Does anyone have any ideas on how to automatically execute code on every page load?
Regards,
William
In your content.js script you can simply register an event listener for the "load" event, just like you would have in the old single process Firefox:
addEventListener("load", someFunc, true);
This will call "someFunc" any time a webpage loads in the tab.
Any global code in content.js is executed when the tab is initial created, not when a page loads. Use global code to set up event listeners or message listeners. The web content will still fire events you can catch in the content.js (child script).
This worked for me.
in content.js:
var addEventListener;
if (window.BrowserApp) { // We are running in Mobile Firefox
addEventListener = window.BrowserApp.deck.addEventListener;
} else {
var appcontent = document.getElementById("appcontent");
if (appcontent) {
addEventListener = appcontent.addEventListener;
}
}
if (addEventListener) {
var onDOMContentLoaded = function() { /* Code here */ };
addEventListener("DOMContentLoaded", onDOMContentLoaded, true);
var onLoad = function() { /* Code here */ };
addEventListener("load", onLoad, true);
// etc ...
}

Resources