Chrome extension popup close fired at wrong time - javascript

I have a chrome extension which is currently consists of a background page and a popup page. There are some initialization happens when the popup is opened. I am using DOM event
doc.addEventListener('DOMContentLoaded', function() { ... }
And the behaviour is correct.
The issue is when closing the popup. Since chrome popup does not throw unload event I am using what was suggested here. Here is my code
popup.js
bgPage = chrome.extension.getBackgroundPage();
bgPageManager = bgPage.manager(); // This is the exposed api from bg page
bgPageManager.init(chrome.runtime.connect({
name: 'P1'
}));
Here connecting to the runtime and sending the port to background page so that it can listen to onDisconnect event.
Background.js
function init(port) {
port.onDisconnect.addListener(function() {
// Clean up happens here
stateManager.unregister();
});
}
This works as well.
But the issue is, this onDisconnect getting fired when the popup is getting opened, not when it is getting closed
The documentation for onDisconnect event is
An object which allows the addition and removal of listeners for a Chrome event.
Which is not every helpful ;)
So is there anything wrong I am doing, or any way when I can detect the popup close?

Unless there's something listening to chrome.runtime.onConnect in the popup page (from your comment, doesn't seem so), chrome.runtime.connect() will return a Port that immediately closes (as no-one was willing to listen).
You're definitely making it more difficult than it should be with involving getBackgroundPage though. The popup can initiate the port itself. All you need to do is:
// popup code
var bgPort = chrome.runtime.connect({name: "P1"});
// background code
var popupPort;
chrome.runtime.onConnect.addListener(function(port) {
if(port.name == "P1") {
popupPort = port;
popupPort.onDisconnect.addListener(function() {
/* Clean up happens here */
});
}
});
In case you want to preserve what you already have, the minimal code to put into the popup is this:
var bgPort;
chrome.runtime.onConnect.addListener(function(port) {
if(port.name == "P1") {
bgPort = port;
}
});
Note that in all cases you need to keep a reference to the Port object on both sides. If the garbage collector collects it, the port will be disconnected.
Finally, a port name is optional; if it's the only port you use, you can drop the code that sets/checks the name.

Related

Guaranteeing at most 1 chrome extension popup page open at a time

On my chrome extension, I have a popup page and a background script.
As default, when I click on the extension's icon in two different windows, a popup will open in both windows.
I want to limit the amount of popups opened by the extension to be at most one at a time.
Here's how the full scenario that I'm trying to create:
At first no pop up is activated.
Window A opened a popup.
Window B opened a popup, in which case, Window A's popup will close.
Window C is created, go to 2, but this time Window A<-Window B and Window B<-Window C
If in any time The only popup that is open was closed, return to 1.
I know that a popup windows was created because I have a simple port connection that is invoked on the popup startup. Thus, the background is in theory aware of all popup windows that are created, namely, that is the code that I run in the popup to connect:
const port = chrome.runtime.connect({ name: 'popup-communications' });
I attempted to solve the problem in 3 ways, all of them failed.
Attempt 1
Hold the last popup that was connected. If a new one appears, close the old one before you save the new one. Use chrome.extension.getViews to get the new port. I imagined this would work, but rapid clicks on the extension icon (to invoke browserAction) makes this popUp state confused.
let popUp;
chrome.runtime.onConnect.addListener(function connect(port) {
if (port.name === 'popup-communications') {
// attempt 1
if (popUp) {
popUp?.close?.();
popUp = null;
console.log('removed old pop up');
}
[popUp] = chrome.extension.getViews({
type: 'popup',
});
});
Attempt 2
Close all popups that received from chrome.extension.getView, but the last one. The problem with this approach is that the chrome.extension.getView does not guarantee any order.
let popUp;
chrome.runtime.onConnect.addListener(function connect(port) {
if (port.name === 'popup-communications') {
// attempt 2
const popUps = chrome.extension.getViews({
type: 'popup',
});
console.log(popUps);
for (let i = 0; i < popUps.length - 1; i++) {
popUps[i].close();
}
});
I also experimented with chrome.browserAction.disable and chrome.browserAction.enable. This solution maintains indeed maintains 1 popup at all time, but I want it the popup to be available whenever I click on the extension icon, and this will not happen with this approach (instead I will need to find the relevant window with this popup)
Is there a way to achieve what I'm trying to do here?
I was able to achieve this behavior in the following way.
background.js
The background listens to connecting popups.
When a popup connects, it will broadcast a message to all open popups to close. Note this message is not sent over the port since the port connection does not broadcast.
There should exist a way to optimize, since at most one other popup can be open, but I will leave that for another time and prefer not to create global variables.
chrome.runtime.onConnect.addListener(function connect(port) {
if (port.name === 'popup-communications') {
port.onMessage.addListener(function (msg) {
if (msg.here) {
const activeTabId = msg.here;
// broadcast close request
chrome.runtime.sendMessage({closeUnmatched: activeTabId});
}
});
}
});
popup.js
Perform a lookup of its tab id.
Add a message listener to know when to close. If the message to close does not match current tab id, popup will close. window.close() is sufficient to close a popup.
"announce" to background that popup is ready by sending the tab Id over the port.
async function getCurrentTab() {
let queryOptions = {active: true, currentWindow: true};
let [tab] = await chrome.tabs.query(queryOptions);
return tab;
}
function addListener(myTabId) {
chrome.runtime.onMessage.addListener(function (msg) {
if (msg.closeUnmatched && msg.closeUnmatched !== myTabId) {
window.close();
}
});
}
(async function constructor() {
const port = chrome.runtime.connect({name: 'popup-communications'});
// whoami lookup
const {id: myTabId} = await getCurrentTab();
// add handler to self-close
addListener(myTabId);
// tell background I'm here
port.postMessage({here: myTabId});
// do whatever with port..
}());
I assume steps 1-3 can be done faster than user switching tabs/windows to activate another popup. The port connection was complicating things, but I left it in the answer, since you may have a use case for it.

Capture screenshot of Electron window before quitting

In an Electron app, I can take a screenshot of my window from the main process using this:
let win = new BrowserWindow(/* ... */);
let capturedPicFilePath = /* where I want that saved */
win.capturePage((img) => {
fs.writeFile(capturedPicFilePath, img.toPng(), () => console.log(`Saved ${capturedPicFilePath}`))
})
Awesome. Now I'd like to do that right before app quits. Electron emits a particular event for that, that I tried to use:
Event: 'before-quit' : emitted before the application starts closing its windows.
Problem: if I use the same code as above in a handler for that event, the file is created but empty.
I'm guessing it's because the screenshot is taken in an asynchronous way, and the window is already closed when it happens.
So this does not work for me:
app.on('before-quit', (event) => {
win.capturePage(function(img) {
fs.writeFile(capturedPicFilePath, img.toPng(), () => console.log(`Saved ${capturedPicFilePath}`))
})
})
Edit 1 : Doing this in the renderer process with window.onbeforeunload fails too. It's also too late to perform the screenshot. I get this in the main console (i.e. it goes to terminal):
Attempting to call a function in a renderer window that has been closed or released.
Context: for now I'm exploring the limits of what is possible to do with screen capture (essentially for support purposes), and found that one. Not sure yet what I would do with that edge case, I thought about it not just for support, I'm also considering displaying at startup a blurred pic of previous state (some parts of my app take 1-2 seconds to load).
Any ideas?
I have had a similar problem before, I got around it by using the window close event and then preventing it from closing. Once my action had performed I then ran app.quit().
window.on('close', function (event) {
event.preventDefault();
let capturedPicFilePath = /* where you want it saved */
window.capturePage((img) => {
fs.writeFile(capturedPicFilePath, img.toPng(), () =>
console.log(`Saved ${capturedPicFilePath}`));
app.quit(); // quit once screenshot has saved.
});
});
Hope this helps!

chrome.notification.create with chrome.notification.onClicked in a Firefox WebExtension add-on

I have a problem understanding the documentation for the WebExtensions notification.onClicked event.
Ultimately, I'm trying to get the text of the notification copied to the clipboard when you click on it. However, right now I am having a problem understanding the callback thing, or where I have to insert the notification.onClicked function.
At the moment, I don't know why the notification.onClicked listener does nothing.
My code (all the code needed to demonstrate the problem as a WebExtension Firefox add-on):
manifest.json
{
"description": "Test Webextension",
"manifest_version": 2,
"name": "Σ",
"version": "1.0",
"permissions": [
"<all_urls>",
"notifications",
"webRequest"
],
"background": {
"scripts": ["background.js"]
}
}
background.js
'use strict';
function logURL(requestDetails) {
notify("Testmessage");
chrome.notifications.onClicked.addListener(function() {
console.log("TEST TEST");
});
}
function notify(notifyMessage) {
var options = {
type: "basic",
iconUrl: chrome.extension.getURL("icons/photo.png"),
title: "",
message: notifyMessage
};
chrome.notifications.create("ID123", options);
}
chrome.webRequest.onBeforeRequest.addListener(
logURL, {
urls: ["<all_urls>"]
}
);
First, you need to be testing this in Firefox 47.0+, as support for chrome.notifications.onClicked() was added in version 47.0. While this is probably not your problem, it is one contributing possibility.
There are multiple issues with your code. Some are in your code, but primarily you are running into a Firefox bug.
Firefox Bug:
Your primary issue is that you are running into a Firefox bug where Firefox gets confused if you try to create notifications too rapidly. Thus, I have implemented a notification queue and rate limited the creation of notifications. What is "too rapidly" is probably both OS and CPU dependent, so you are best off erroring on the side of caution and set the delay between calls to chrome.notifications.create() to a higher value. in the code below, the delay is 500ms. I have added a note regarding this issue in the chrome.notifications.create() page on MDN and on the Chrome incompatibilities page.
Adding multiple copies of the same listener:
The main thing that you are doing wrong in your code is that you are adding an anonymous function as a listener, using chrome.notifications.onClicked.addListener(), multiple times to the same event. This is a generic issue with event handlers. When you use an anonymous function it is a different actual function each time you are trying to add it, so the same functionality (in multiple identical functions) gets added multiple times. You should not be adding functions, which do the exact same thing, multiple times to the same event. Doing so is almost always an error in your program and results in unexpected operation.
In this case, the multiple functions would have ended up outputing multiple lines of TEST TEST to the console each time the user clicked on a notification. The number of lines output per click would increase by one for each web request which resulted in a call to logURL.
The way to prevent doing this is to be sure to add the listener only once. If you are using an anonymous function, you can only do this by being sure you only execute the addListener (or addEventlistener) once (usually by only adding the listener from your main code (not from within a function), or from a function that is only called once. Alternately, you can name/define your listener function directly within the global scope (or other scope accessible to all places where you try to add the listener) (e.g. function myListener(){...}). Then, when you are adding myListener you are always referring to the same exact function which JavaScript automatically prevents you from adding in the same way to the same event more than once.
It should be noted that if you are trying to add a anonymous function as a listener from another listener, you are almost always doing something wrong. Adding copies of identical anonymous listeners multiple times to the same event is a common error.
Access to the notification text:
While you do not implement anything regarding using the text of the notification, you state that you want to add the text of the notification to the clipboard when the user clicks on the notification. You can not obtain the notification text from any portion of the chrome.notifications API. Thus, you have to store that information yourself. The code below implements an Object to do that so the text can be accessed in the chrome.notifications.onClicked() handler.
Example code:
The code below implements what I believe you desire. It is just creating and clicking the notification while having access to the notification text in the chrome.notifications.onClicked() listener. It does not implement the part about putting the text into the clipboard, as that was not actually implemented in the code in your Question. I have added liberal comments to the code to explain what is happening and provided quite a bit of console.log() output to help show what is going on. I have tested it in both Firefox Developer Edition (currently v51.0a2) and Google Chrome.
background.js (no changes to your manifest.json):
'use strict';
//* For testing, open the Browser Console
var isFirefox = window.InstallTrigger?true:false;
try{
if(isFirefox){ //Only do this in Firefox
//Alert is not supported in Firefox. This forces the Browser Console open.
//This abuse of a misfeature works in FF49.0b+, not in FF48
alert('Open the Browser Console.');
}
}catch(e){
//alert throws an error in Firefox versions below 49
console.log('Alert threw an error. Probably Firefox version below 49.');
}
//*
//Firefox gets confused if we try to create notifications too fast (this is a bug in
// Firefox). So, for Firefox, we rate limit showing the notifications.
// The maximum rate possible (minimum delay) is probably OS and CPU speed dependent.
// Thus, you should error on the side of caution and make the delay longer.
// No delay is needed in Chrome.
var notificationRateLimit = isFirefox ? 500:0;//Firefox:Only one notification every 500m
var notificationRateLimitTimeout=-1; //Timeout for notification rate limit
var sentNotifications={};
var notificationsQueue=[];
var notificationIconUrl = chrome.extension.getURL("icons/photo.png");
function logURL(requestDetails) {
//console.log('webRequest.onBeforeRequest URL:' + requestDetails.url);
//NOTE: In Chrome, a webRequest is issued to obtain the icon for the notification.
// If Chrome finds the icon, that webRequest for the icon is only issued twice.
// However, if the icon does not exist, then this sets up an infinite loop which
// will peg one CPU at maximum utilization.
// Thus, you should not notify for the icon URL.
// You should consider excluding from notification all URLs from within your
// own extension.
if(requestDetails.url !== notificationIconUrl ){
notify('webRequest URL: ' + requestDetails.url);
}
//Your Original code in the Question:
//Unconditionally adding an anonymous notifications.onClicked listener
// here would result in multiple lines of 'TEST TEST' ouput for each click
// on a notification. You should add the listener only once.
}
function notify(notifyMessage) {
//Add the message to the notifications queue.
notificationsQueue.push(notifyMessage);
console.log('Notification added to queue. message:' + notifyMessage);
if(notificationsQueue.length == 1){
//If this is the only notification in the queue, send it.
showNotificationQueueWithRateLimit();
}
//If the notificationsQueue has additional entries, they will get
// shown when the current notification has completed being shown.
}
function showNotificationQueueWithRateLimit(){
if(notificationRateLimitTimeout===-1){
//There is no current delay active, so immediately send the notification.
showNextNotification();
}
//If there is a delay active, we don't need to do anything as the notification
// will be sent when it gets processed out of the queue.
}
function showNextNotification() {
notificationRateLimitTimeout=-1; //Indicate that there is no current timeout running.
if(notificationsQueue.length === 0){
return; //Nothing in queue
}
//Indicate that there will be a timeout running.
// Neeed because we set the timeout in the notifications.create callback function.
notificationRateLimitTimeout=-2;
//Get the next notification from the queue
let notifyMessage = notificationsQueue.shift();
console.log('Showing notification message:' + notifyMessage);
//Set our standard options
let options = {
type: "basic",
//If the icon does not exist an error is generated in Chrome, but not Firefox.
// In Chrome a webRequest is generated to fetch the icon. Thus, we need to know
// the iconUrl in the webRequest handler, and not notify for that URL.
iconUrl: notificationIconUrl,
title: "",
message: notifyMessage
};
//If you want multiple notifications shown at the same time, your message ID must be
// unique (at least within your extension).
//Creating a notification with the same ID causes the prior notification to be
// destroyed and the new one created in its place (not just the text being replaced).
//Use the following two lines if you want only one notification at a time. If you are
// actually going to notify on each webRequest (rather than doing so just being a way
// to test), you should probably only have one notification as they will rapedly be
// off the screen for many pages.
//let myId = 'ID123';
//chrome.notifications.create(myId,options,function(id){
//If you want multiple notifications without having to create a unique ID for each one,
// then let the ID be created for you by using the following line:
chrome.notifications.create(options,function(id){
//In this callback the notification has not necessarily actually been shown yet,
// just that the notification ID has been created and the notification is in the
// process of being shown.
console.log('Notification created, id=' + id + ':: message:' + notifyMessage);
logIfError();
//Remember the text so we can get it later
sentNotifications[id] = {
message: notifyMessage
}
//Show the next notification in the FIFO queue after a rate limiting delay
// This is called unconditionally in order to start the delay should another
// notification be queued, even if one is not in the queue now.
notificationRateLimitTimeout = setTimeout(showNextNotification
,notificationRateLimit);
});
}
function logIfError(){
if(chrome.runtime.lastError){
let message =chrome.runtime.lastError.message;
console.log('Error: ' + message);
}
}
chrome.webRequest.onBeforeRequest.addListener(
logURL, {
urls: ["<all_urls>"]
}
);
//Add the notifications.onClicked anonymous listener only once:
// Personally, I consider it better practice to use a named function that
// is defined in the global scope. Doing so prevents inadvertantly adding
// it multiple times. Although, your code should be written such that you
// don't do that anyway.
chrome.notifications.onClicked.addListener(function(id) {
//We can not get the notification text from here, just the ID. Thus, we
// have to use the text which was remembered.
console.log('Clicked notification message text: ', sentNotifications[id].message);
//In Firefox the notification is automatically cleared when it is clicked.
// If you want the same functionality in Chrome, you will need to clear() it
// yourself:
//Always do this instead of only when not in Firefox so that it remains consistent
// Even if Firefox changes to match Chrome.
chrome.notifications.clear(id);
//This is the last place we use the text of the notification, so we delete it
// from sentNotifications so we don't have a memory leak.
delete sentNotifications[id];
});
//Test the notifications directly without the need to have webRequests:
notify('Background.js loaded');
notify('Second notification');
In the process of working on this, I found multiple incompatibilities between Chrome and Firefox. I am in the process of updating MDN to mention the incompatibilities in the documentation on MDN.

In a Firefox restartless add-on, how do I run code when a new window opens (listen for window open)?

I am starting to build a restartless Firefox add-on and I am having trouble setting up the bootstrap.js. Everyone seems to agree that the core of a bootstrap.js is pretty much boilerplate code, along these lines:
const Cc = Components.classes;
const Ci = Components.interfaces;
function startup() {
let wm = Cc["#mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
// then can control what happens with domWindow.document
}
}
function shutdown() {}
function install() {}
function uninstall() {}
This code works and I can control things in the existing windows. For example, domWindow.alert("text") successfully creates a standard alert saying "text" on every window that is currently open.
However, I can't find any code that will allow me to do things in new windows; i.e. those created after the script runs. What is the correct way to handle the creation of new windows and gain control over them, to the point where I could get another "text" alert from one when it is created?
Edit: Using the nsWindowMediator class and the code sample from MDN, I now have this:
var windowListener = {
onOpenWindow: function (aWindow) {
try {
let domWindow = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowInternal || Ci.nsIDOMWindow);
domWindow.addEventListener("load", function () {
domWindow.removeEventListener("load", arguments.callee, false);
//window has now loaded now do stuff to it
domWindow.alert("text");
}, false);
} catch (err) {
Services.prompt.alert(null, "Error", err);
}
},
onCloseWindow: function (aWindow) {},
onWindowTitleChange: function (aWindow, aTitle) {}
};
function startup(aData, aReason) {
// Load into any existing windows
try {
let wm = Cc["#mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
let windows = wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements()) {
let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
loadIntoWindow(domWindow);
}
} catch (err) {
Services.prompt.alert(null, "Error", err);
}
Services.wm.addListener(windowListener);
}
However, there is still no output from the onOpenWindow call - the "text" alert does not appear, nor does the error alert in the catch block. I can confirm that onOpenWindow is actually being entered; if I put a Services.prompt.alert() at the beginning of onOpenWindow, I get the alert when I create a new window. Unfortunately, I get an infinite loop of alerts and I have no idea why.
However, I can't find any code that will allow me to do things in new windows; i.e. those created after the script runs. What is the correct way to handle the creation of new windows and gain control over them, to the point where I could get another "text" alert from one when it is created?
The correct way to act on each window when it opens is to use addListener() from nsIWindowMediator. The example code below does this. The nsIWindowMediator is included in Services.jsm and is accessed through Services.wm.addListener(WindowListener). In order to use a window listener, you have to pass it an nsIWindowMediatorListener (ref2) object. An nsIWindowMediatorListener contains three keys: onOpenWindow, onCloseWindow, and onWindowTitleChange. Each should be defined as a function which will be called when the appropriate event occurs.
The MDN document How to convert an overlay extension to restartless in "Step 9: bootstrap.js" contains an example of a basic bootstrap.js which will run the code in the function loadIntoWindow(window) for each currently open browser window and any browser window which opens in the future. I have used code modified from this in a couple of different add-ons. The example is substantially similar to the code you are already using. The example is (slightly modified):
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
function startup(data,reason) {
// Load this add-ons module(s):
Components.utils.import("chrome://myAddon/content/myModule.jsm");
// Do whatever initial startup stuff is needed for this add-on.
// Code is in module just loaded.
myModule.startup();
// Make changes to the Firefox UI to hook in this add-on
forEachOpenWindow(loadIntoWindow);
// Listen for any windows that open in the future
Services.wm.addListener(WindowListener);
}
function shutdown(data,reason) {
if (reason == APP_SHUTDOWN)
return;
// Unload the UI from each window
forEachOpenWindow(unloadFromWindow);
// Stop listening for new windows to open.
Services.wm.removeListener(WindowListener);
// Do whatever shutdown stuff you need to do on add-on disable
myModule.shutdown();
// Unload the module(s) loaded specific to this extension.
// Use the same URL for your module(s) as when loaded:
Components.utils.unload("chrome://myAddon/content/myModule.jsm");
// HACK WARNING: The Addon Manager does not properly clear all add-on related caches
// on update. In order to fully update images and locales, their
// caches need clearing here.
Services.obs.notifyObservers(null, "chrome-flush-caches", null);
}
function install(data,reason) { }
function uninstall(data,reason) { }
function loadIntoWindow(window) {
/* call/move your UI construction function here */
}
function unloadFromWindow(window) {
/* call/move your UI tear down function here */
}
function forEachOpenWindow(todo) {
// Apply a function to all open browser windows
var windows = Services.wm.getEnumerator("navigator:browser");
while (windows.hasMoreElements())
todo(windows.getNext().QueryInterface(Ci.nsIDOMWindow));
}
var WindowListener = {
onOpenWindow: function(xulWindow) {
var window = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
function onWindowLoad() {
window.removeEventListener("load",onWindowLoad);
// Only add UI changes if this is a browser window
if (window.document.documentElement.getAttribute("windowtype")
== "navigator:browser")
loadIntoWindow(window);
}
window.addEventListener("load",onWindowLoad);
},
onCloseWindow: function(xulWindow) { },
onWindowTitleChange: function(xulWindow, newTitle) { }
};
While there is quite a bit more that your might want to do in your bootstrap.js code, the above is organized reasonably well and keeps all of the code to load into the Firefox UI within loadIntoWindow(window) and unloading the UI within unloadFromWindow(window). However, it should be noted that some UI elements you should only be adding/removing once (e.g. australis widgets, like buttons) and other elements (e.g. direct changes to the Firefox DOM) have to be added once in each window.
Unfortunately, I get an infinite loop of alerts and I have no idea why.
One of the significant differences between this example and what you are currently using is the test for the type of window that has opened. This is done so that we are only acting on newly opened windows which are browser windows instead of all newly opened windows:
if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser")
loadIntoWindow(window);
The problem you describe of getting an infinite loop of alert() popups is caused by not checking to make sure that you are only acting on browser windows. The alert() popup is a window. Thus, you are calling alert() for every alert() window you open which, of course, just opens another alert() window on which you call alert(). This is your infinite loop.
Additional references:
1. Working with windows in chrome code
However, I can't find any code that will allow me to do things in new windows
When working with XPCOM objects you generally want to study their interfaces, which are often found on MDN. In this case your starting point would be nsIWindowMediator, since that's the service you're using in line 5.
As you can see it has an addListener function, which takes a parameter implementing nsIWindowMediatorListener. There's a code-example right there on the page.
But let's assume for the moment there isn't a code example. You could search for the interface on MDN, but it isn't listed. The next step would be searching MXR for the .idl. idl = interface description language
Once you got the interface contract you can more or less just implement it in javascript, at least for listeners. Implementing your own xpcom services would be a little more complicated.
Searching the addon sdk can often provide some hints too. In this case they don't seem to be using .addListener, but the file hints at another interesting service, which in turn you can find on MDN: nsIWindowWatcher.
Basically, if you're writing restartless addons you're rummaging through the entrails of firefox and will have to do some detective work to find the exact components you need. If you want something more convenient I would recommend the addon sdk, which provides a more organized but also more restricted set of commonly used APIs

How can i close MessageDialog from code in HTML5 Windows Store app?

As in the title,
how can i close a MessageDialog from code in HTML5 Windows Store app?
my code so far:
var msg = new Windows.UI.Popups.MessageDialog("Please wait");
msg.commands.append(new Windows.UI.Popups.UICommand("OK",
function (command) {
...
}));
msg.showAsync();
and then i would like to close this popup FROM THE CODE, i have not found any methods
in the specification like
msg.close();
is there a way?
thanks
The fact that your message is "Please wait" suggests to me that you might want to use a different tool for this job.
If what you're trying to do is inform the user that you're doing something in the background that they need to wait for, consider using a Progress control instead, as documented here:
http://msdn.microsoft.com/en-us/library/windows/apps/hh465487.aspx
If you use a Progress control, you can both include a text label with your desired text, and dismiss the progress control when you've finished whatever task it is you're asking the user to wait for.
And to answer your original question, I don't believe there's any API for dismissing a MessageDialog programmatically, as that would break the interaction pattern of this control, which is for the app to display a message, and then allow the user to dismiss it when they're ready to.
Hope that helps.
For more information on Windows Store app development, register for App Builder.
I think you want to use a flyout, similarly to this answer. The link solves a slightly different problem in that it closes the flyout after a timeout.
However, you should be able to define a flyout that you, as well as the user, can close. In both cases, you end up calling something like:
flyout.winControl.hide(); // Dismiss the flyout
Take a look at this...
(function(){
"use strict";
var page = WinJS.UI.Pages.define("/html/cancelcommand.html", {
ready: function (element, options) {
document.getElementById("cancelCommand").addEventListener("click", cancelCommand_Click, false);
}
});
// Click handler for the 'cancelCommand' button.
// Demonstrates setting the command to be invoked when the 'escape' key is pressed.
// Also demonstrates retrieval of the label of the chosen command and setting a callback to a function.
// A message will be displayed indicating which command was invoked.
// In this scenario, 'Try again' is selected as the default choice, and the 'escape' key will invoke the command named 'Close'
function cancelCommand_Click() {
// Create the message dialog and set its content
var msg = new Windows.UI.Popups.MessageDialog("No internet connection has been found.");
// Add commands and set their command handlers
msg.commands.append(new Windows.UI.Popups.UICommand("Try again", commandInvokedHandler));
msg.commands.append(new Windows.UI.Popups.UICommand("Close", commandInvokedHandler));
// Set the command that will be invoked by default
msg.defaultCommandIndex = 0;
// Set the command to be invoked when escape is pressed
msg.cancelCommandIndex = 1;
// Show the message dialog
msg.showAsync();
}
function commandInvokedHandler(command) {
// Display message
WinJS.log && WinJS.log("The '" + command.label + "' command has been selected.", "sample", "status");
}
}());
http://code.msdn.microsoft.com/windowsapps/Message-dialog-sample-00c928f5/sourcecode?fileId=50972&pathId=1064922824

Categories