How to know when your Chrome application Window is fully open - javascript

I am creating a Chrome application, "not a extension."
People can receive and send messages. If i receive a message, i got a notification and a mp3 sound. If the app Window is open, I know how to focus the window to make it appear to the front. but if the window is closed. I do get the notification, the sound ect (from background.js) and i create a window to open, when i receive this notification, but i can't interact with the background.js script anymore. is like if the chrome.runtime.sendMessage fire to soon before my app window is fully open.
chrome.runtime.sendMessage({
action: "Messageincoming"
});
When a message is incoming, in my app, a popup appear telling the user that a message is incoming (called from background.js) and the user can accept it by clicking a button. but when the app window is closed i should open a new window and i open it programatically with chrome.app.window.create('myapp.html', {
Like i said the chrome.runtime.sendMessage seem to fire to soon before the window is fully open. I can't trigger this popup to make the user accept, and i am not sure that if i do that the button will work.
Is there a way to wait until the window is fully open? or use any other method.
i read all the chrome app documentation and i find nothing. I have a js scripts in the myapp.html page who control the click and everything in the UI. controler.js

There are a variety of ways to do this, so it really depends on what you're looking for. The likely simplest answer is through a callback function, which you can pass in to chrome.app.window.create as shown here. This will allow you to pass in a function to be executed during page load.
Another way is to receive a message as you've described. You can do that as well, but you need to set a message handler in the new page you've created, and it needs to be set immediately on page load or else you will miss the message as you've described. Here's more on message passing with some examples at the bottom. https://developer.chrome.com/extensions/messaging
A more elaborate method of doing this could be to hook into the OnFullscreened or other handlers for the new window. In that handler, send a request to your background script requesting whatever message data you need. In your background script, have a message handler that returns whatever stored message(s) you want.

Related

messaging popup.js from background when popup is open and just onConnected [duplicate]

I am running into an issue sending data from my background script to the script for my pageAction. My content script adds an <iframe /> and the JavaScript in the <iframe /> is receiving the data from my background script, but it does not seem to be retrieved in my pageAction.
In my background script I have something like:
chrome.tabs.sendMessage(senderTab.tab.id,
{
foo:bar
});
where senderTab.tab.id is the "sender" in onMessage Listener in my background script.
In the JavaScript loaded by the <iframe /> injected by my content script I have something like:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("received in iframe:", request);
}
});
The <iframe /> receives the message exactly as expected.
I put the same JavaScript in my page_action.js, but it does not receive any data from the background script. The pageAction is activated with chrome.pageAction.show(senderTab.tab.id); before I call chrome.tabs.sendMessage(senderTab.tab.id ...
Is the HTML page attached to my pageAction not part of the same tab? Since this tabId enabled me to activate/"show" the icon, I would think the listener in the JavaScript for the pageAction should also receive from chrome.tabs.sendMessage(senderTab.tab.id ...
In my content script I use the following to send data to the background script:
chrome.runtime.sendMessage({
foo: bar
});
When the content script sends the above message, the pageAction JavaScript is picking it up.
How do I get the background script to properly send data to my pageAction? I do not want to have pageAction request/poll, instead I want pageAction to just listen and receive. E.g., if the pageAction HTML it shown, it should be able to update in real time as the background page makes changes.
Communicating with a page in the background context
Pages which are open in the background context include:
background pages/scripts(MDN)
event pages (Firefox does not support event pages. All manifest.json background pages remain loaded at all times.)
browser action popups(MDN)
page action popups(MDN)
options pages(MDN1, MDN2) (in a popup, a tab, or window)
sidebar action pages (NDN) (not available in Chrome)
Any HTML content contained within your extension which is opened normally in a tab, window (e.g. a panel), or frame.1
Using tabs.sendMessage()(MDN) will not send a message to any of them. You would need to use runtime.sendMessage()(MDN) to send a message to them. The scope for any of them, except background pages and event pages, only exists when it is being displayed. Obviously, you can not communicate with the code when it does not exist. When the scope exists, you can communicate with any of them using:
Directly
From the background context, you can directly change variables, or call functions, in another page that is also in the background context (i.e. not content scripts), after having gotten a reference to its global scope, its Window, using extension.getViews()(MDN), extension.getBackgroundPage()(MDN), or other method(MDN).
For example, you can call a function created with function myFunction in the page of the first returned view by using something like:
winViews = chrome.extension.getViews();
winViews[0].myFunction(foo);
It should be noted that in your callback from tabs.create()(MDN) or windows.create()(MDN) the view for the newly opened tab or window will probably not yet exist. You will need to use some methodology to wait for the view to exist.2 See below for recommended ways to communicate with newly opened tabs or windows.
Directly manipulating values in the other page's scope allows you to communicate any type of data you desire.
Messaging
Receive messages using chrome.runtime.onMessage(MDN), 3 which were sent with chrome.runtime.sendMessage()(MDN). Each time you receive a message in a runtime.onMessage listener, there will be a sendResponse function provided as the third argument which allows you to directly respond to the message. If the original sender has not supplied a callback to receive such a response in their call to chrome.runtime.sendMessage(), then the response is lost. If using Promises (e.g. browser.runtime.sendMessage() in Firefox), the response is passed as an argument when the Promise is fulfilled. If you want to send the response asynchronously, you will need to return true; from your runtime.onMessage listener.
Ports
You can also connect ports, using chrome.runtime.connect()(MDN) and chrome.runtime.onConnect(MDN) for longer term messaging.
Use chrome.tabs.sendMessage() to send to content scripts
If you want to send from the background context (e.g. background script or popup) to a content script you would use chrome.tabs.sendMessage()/chrome.runtime.onMessage, or connect port(s) using chrome.tabs.connect()(MDN)/chrome.runtime.onConnect.
JSON-serializable data only
Using messaging, you can only pass data which is JSON-serializable.
Messages are received by all scripts in the background, except the sender
Messages sent to the background context are received by all scripts in the background context which have registered a listener, except the script which sent it.3 There is no way to specify that it is only to be received by a specific script. Thus, if you have multiple potential recipients, you will need to create a way to be sure that the message received was intended for that script. The ways to do so usually rely on specific properties existing in the message (e.g. use a destination or recipient property to indicate what script is to receive it, or define that some type of messages are always for one recipient or another), or to differentiate based on the sender(MDN) supplied to the message handler (e.g. if messages from one sender are always only for a specific recipient). There is no set way to do this, you must choose/create a way to do it for use in your extension.
For a more detailed discussion of this issue, please see: Messages intended for one script in the background context are received by all
Data in a StorageArea
Store data to a StorageArea(MDN) and be notified of the change in other scripts using chrome.storage.onChanged(MDN). The storage.onChanged event can be listened to in both the background context and content scripts.
You can only store data which is JSON-serializable into a StorageArea.
Which method is best to use in any particular situation will depends on what you are wanting to communicate (type of data, state change, etc.), and to which portion, or portions, of your extension you are wanting to communicate from and to. For instance, if you want to communicate information which is not JSON-serializable, you would need to do so directly (i.e. not messaging or using a StorageArea). You can use multiple methods in the same extension.
More on popups
None of the popups (e.g. browser action, or page action) are directly associated with the active tab. There is no concept of a shared or separate instance per tab. However, the user can open one popup in each Chrome window. If more than one popup is open (a maximum of one per Chrome window), then each is in a separate instance (separate scope; has its own Window), but are in the same context. When a popup is actually visible, it exists in the background context.
There is only ever one page action or browser action popup open at a time per Chrome window. The HTML file which will be open will be whichever one has been defined for the active tab of the current window and opened by the user by clicking on the page/browser action button. This can be assigned a different HTML document for different tabs by using chrome.browserAction.setPopup()(MDN), or chrome.pageAction.setPopup()(MDN), and specifying a tabId. The popup can/will be destroyed for multiple reasons, but definitely when another tab becomes the active tab in the window in which the popup is open.
However, any method of communication used will only communicate to the one(s) which is/are currently open, not ones which are not open. If popups are open for more than one Chrome window at a time, then they are separate instances, with their own scope (i.e. their own Window). You can think of this something like having the same web page open in more than one tab.
If you have a background script, the background script context is persistent across the entire instance of Chrome. If you do not have a background script the context may be created when needed (e.g. a popup is shown) and destroyed when no longer needed.
chrome.tabs.sendMessage() can not communicate to popups
As mentioned above, even if the popup did exist, it will exist in the background context. Calling chrome.tabs.sendMessage() sends a message to content scripts injected into a tab/frame, not to the background context. Thus, it will not send a message to a non-content script like a popup.
Action button: enable/disable (browser action) vs. show/hide (page action)
Calling chrome.pageAction.show()(MDN) just causes the page action button to be shown. It does not cause any associated popup to be shown. If the popup/options page/other page is not actually being shown (not just the button), then its scope does not exist. When it does not exist, it, obviously, can not receive any message
Instead of the page action's ability to show()(MDN) or hide()(MDN) the button, browser actions can enable()(MDN) or disable()(MDN) the button.
Programmatically opening a tab or window with HTML from your extension
You can use tabs.create()(MDN) or windows.create()(MDN) to open a tab or window containing an HTML page from within your extension. However, the callback for both of those API calls is executed prior to the page's DOM existing and thus prior to any JavaScript associated with the page existing. Thus, you can not immediately access the DOM created by the contents of that page, nor interact with the JavaScript for the page. Very specifically: no runtime.onMessage() listeners will have been added, so no messages sent at that time will be received by the newly opening page.
The best ways to resolve this issue are:
Have the data available so the newly opening page can get the data when it is ready for. Do this by, prior to beginning the process of opening the page:
If the source is in the background context: store the data in a variable available to the global scope of the sending page. The opening page can then use chrome.extension.getBackgroundPage() to read the data directly.
If the source of the data is in either the background context or a content script: place the data into storage.local(MDN). The opening page can then read it when its JavaScript is run. For example, you could use a key called messageToNewExtensionPage.
If you are using runtime.sendMessage(), then initiate the transfer of the data from your newly opening page by sending a message from the that page's code to the source of the data (using runtime.sendMessage(), or tabs.sendMessage() for content script sources) requesting the data. The script with the data can then send the data back using the sendResponse(MDN) function provided by runtime.onMessage().
Wait to interact with the newly opening page until after at least the DOM is available, if not until after the JavaScript for the page has run. While it's possible to do this without the newly opening page providing specific notification that it's up and running, doing so is more complex and only useful in some specific cases (e.g. you want to do something prior to the JavaScript in the new page being run).2
Additional references
Chrome
Message Passing
Chrome extension overview
architecture
Communication between pages
Firefox
WebExtensions
Anatomy of a WebExtension
With some minor exceptions: e.g. using a content script to insert content into the page context.
There are multiple methods which you can use. Which way is best will depend on exactly what you are doing (e.g. when you need to access the view with respect to the code being executed in the view). A simple method would be just to poll waiting for the view to exist. The following code does that for opening a window:
chrome.windows.create({url: myUrl},function(win){
//Poll for the view of the window ID. Poll every 50ms for a
// maximum of 20 times (1 second). Then do a second set of polling to
// accommodate slower machines. Testing on a single moderately fast machine
// indicated the view was available after, at most, the second 50ms delay.
waitForWindowId(win.id,50,20,actOnViewFound,do2ndWaitForWinId);
});
function waitForWindowId(id,delay,maxTries,foundCallback,notFoundCallback) {
if(maxTries--<=0){
if(typeof notFoundCallback === 'function'){
notFoundCallback(id,foundCallback);
}
return;
}
let views = chrome.extension.getViews({windowId:id});
if(views.length > 0){
if(typeof foundCallback === 'function'){
foundCallback(views[0]);
}
} else {
setTimeout(waitForWindowId,delay,id,delay,maxTries,foundCallback
,notFoundCallback);
}
}
function do2ndWaitForWinId(winId,foundCallback){
//Poll for the view of the window ID. Poll every 500ms for max 40 times (20s).
waitForWindowId(winId,500,40,foundCallback,windowViewNotFound);
}
function windowViewNotFound(winId,foundCallback){
//Did not find the view for the window. Do what you want here.
// Currently fail quietly.
}
function actOnViewFound(view){
//What you desire to happen with the view, when it exists.
}
From MDN:
In Firefox versions prior to version 51, the runtime.onMessage listener will be called for messages sent from the same script (e.g. messages sent by the background script will also be received by the background script). In those versions of Firefox, if you unconditionally call runtime.sendMessage() from within a runtime.onMessage listener, you will set up an infinite loop which will max-out the CPU and lock-up Firefox. If you need to call runtime.sendMessage() from within a runtime.onMessage, you will need to check the sender.url property to verify you are not sending a message in response to a message which was sent from the same script. This bug was resolved as of Firefox 51.

Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)

I am running into an issue sending data from my background script to the script for my pageAction. My content script adds an <iframe /> and the JavaScript in the <iframe /> is receiving the data from my background script, but it does not seem to be retrieved in my pageAction.
In my background script I have something like:
chrome.tabs.sendMessage(senderTab.tab.id,
{
foo:bar
});
where senderTab.tab.id is the "sender" in onMessage Listener in my background script.
In the JavaScript loaded by the <iframe /> injected by my content script I have something like:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("received in iframe:", request);
}
});
The <iframe /> receives the message exactly as expected.
I put the same JavaScript in my page_action.js, but it does not receive any data from the background script. The pageAction is activated with chrome.pageAction.show(senderTab.tab.id); before I call chrome.tabs.sendMessage(senderTab.tab.id ...
Is the HTML page attached to my pageAction not part of the same tab? Since this tabId enabled me to activate/"show" the icon, I would think the listener in the JavaScript for the pageAction should also receive from chrome.tabs.sendMessage(senderTab.tab.id ...
In my content script I use the following to send data to the background script:
chrome.runtime.sendMessage({
foo: bar
});
When the content script sends the above message, the pageAction JavaScript is picking it up.
How do I get the background script to properly send data to my pageAction? I do not want to have pageAction request/poll, instead I want pageAction to just listen and receive. E.g., if the pageAction HTML it shown, it should be able to update in real time as the background page makes changes.
Communicating with a page in the background context
Pages which are open in the background context include:
background pages/scripts(MDN)
event pages (Firefox does not support event pages. All manifest.json background pages remain loaded at all times.)
browser action popups(MDN)
page action popups(MDN)
options pages(MDN1, MDN2) (in a popup, a tab, or window)
sidebar action pages (NDN) (not available in Chrome)
Any HTML content contained within your extension which is opened normally in a tab, window (e.g. a panel), or frame.1
Using tabs.sendMessage()(MDN) will not send a message to any of them. You would need to use runtime.sendMessage()(MDN) to send a message to them. The scope for any of them, except background pages and event pages, only exists when it is being displayed. Obviously, you can not communicate with the code when it does not exist. When the scope exists, you can communicate with any of them using:
Directly
From the background context, you can directly change variables, or call functions, in another page that is also in the background context (i.e. not content scripts), after having gotten a reference to its global scope, its Window, using extension.getViews()(MDN), extension.getBackgroundPage()(MDN), or other method(MDN).
For example, you can call a function created with function myFunction in the page of the first returned view by using something like:
winViews = chrome.extension.getViews();
winViews[0].myFunction(foo);
It should be noted that in your callback from tabs.create()(MDN) or windows.create()(MDN) the view for the newly opened tab or window will probably not yet exist. You will need to use some methodology to wait for the view to exist.2 See below for recommended ways to communicate with newly opened tabs or windows.
Directly manipulating values in the other page's scope allows you to communicate any type of data you desire.
Messaging
Receive messages using chrome.runtime.onMessage(MDN), 3 which were sent with chrome.runtime.sendMessage()(MDN). Each time you receive a message in a runtime.onMessage listener, there will be a sendResponse function provided as the third argument which allows you to directly respond to the message. If the original sender has not supplied a callback to receive such a response in their call to chrome.runtime.sendMessage(), then the response is lost. If using Promises (e.g. browser.runtime.sendMessage() in Firefox), the response is passed as an argument when the Promise is fulfilled. If you want to send the response asynchronously, you will need to return true; from your runtime.onMessage listener.
Ports
You can also connect ports, using chrome.runtime.connect()(MDN) and chrome.runtime.onConnect(MDN) for longer term messaging.
Use chrome.tabs.sendMessage() to send to content scripts
If you want to send from the background context (e.g. background script or popup) to a content script you would use chrome.tabs.sendMessage()/chrome.runtime.onMessage, or connect port(s) using chrome.tabs.connect()(MDN)/chrome.runtime.onConnect.
JSON-serializable data only
Using messaging, you can only pass data which is JSON-serializable.
Messages are received by all scripts in the background, except the sender
Messages sent to the background context are received by all scripts in the background context which have registered a listener, except the script which sent it.3 There is no way to specify that it is only to be received by a specific script. Thus, if you have multiple potential recipients, you will need to create a way to be sure that the message received was intended for that script. The ways to do so usually rely on specific properties existing in the message (e.g. use a destination or recipient property to indicate what script is to receive it, or define that some type of messages are always for one recipient or another), or to differentiate based on the sender(MDN) supplied to the message handler (e.g. if messages from one sender are always only for a specific recipient). There is no set way to do this, you must choose/create a way to do it for use in your extension.
For a more detailed discussion of this issue, please see: Messages intended for one script in the background context are received by all
Data in a StorageArea
Store data to a StorageArea(MDN) and be notified of the change in other scripts using chrome.storage.onChanged(MDN). The storage.onChanged event can be listened to in both the background context and content scripts.
You can only store data which is JSON-serializable into a StorageArea.
Which method is best to use in any particular situation will depends on what you are wanting to communicate (type of data, state change, etc.), and to which portion, or portions, of your extension you are wanting to communicate from and to. For instance, if you want to communicate information which is not JSON-serializable, you would need to do so directly (i.e. not messaging or using a StorageArea). You can use multiple methods in the same extension.
More on popups
None of the popups (e.g. browser action, or page action) are directly associated with the active tab. There is no concept of a shared or separate instance per tab. However, the user can open one popup in each Chrome window. If more than one popup is open (a maximum of one per Chrome window), then each is in a separate instance (separate scope; has its own Window), but are in the same context. When a popup is actually visible, it exists in the background context.
There is only ever one page action or browser action popup open at a time per Chrome window. The HTML file which will be open will be whichever one has been defined for the active tab of the current window and opened by the user by clicking on the page/browser action button. This can be assigned a different HTML document for different tabs by using chrome.browserAction.setPopup()(MDN), or chrome.pageAction.setPopup()(MDN), and specifying a tabId. The popup can/will be destroyed for multiple reasons, but definitely when another tab becomes the active tab in the window in which the popup is open.
However, any method of communication used will only communicate to the one(s) which is/are currently open, not ones which are not open. If popups are open for more than one Chrome window at a time, then they are separate instances, with their own scope (i.e. their own Window). You can think of this something like having the same web page open in more than one tab.
If you have a background script, the background script context is persistent across the entire instance of Chrome. If you do not have a background script the context may be created when needed (e.g. a popup is shown) and destroyed when no longer needed.
chrome.tabs.sendMessage() can not communicate to popups
As mentioned above, even if the popup did exist, it will exist in the background context. Calling chrome.tabs.sendMessage() sends a message to content scripts injected into a tab/frame, not to the background context. Thus, it will not send a message to a non-content script like a popup.
Action button: enable/disable (browser action) vs. show/hide (page action)
Calling chrome.pageAction.show()(MDN) just causes the page action button to be shown. It does not cause any associated popup to be shown. If the popup/options page/other page is not actually being shown (not just the button), then its scope does not exist. When it does not exist, it, obviously, can not receive any message
Instead of the page action's ability to show()(MDN) or hide()(MDN) the button, browser actions can enable()(MDN) or disable()(MDN) the button.
Programmatically opening a tab or window with HTML from your extension
You can use tabs.create()(MDN) or windows.create()(MDN) to open a tab or window containing an HTML page from within your extension. However, the callback for both of those API calls is executed prior to the page's DOM existing and thus prior to any JavaScript associated with the page existing. Thus, you can not immediately access the DOM created by the contents of that page, nor interact with the JavaScript for the page. Very specifically: no runtime.onMessage() listeners will have been added, so no messages sent at that time will be received by the newly opening page.
The best ways to resolve this issue are:
Have the data available so the newly opening page can get the data when it is ready for. Do this by, prior to beginning the process of opening the page:
If the source is in the background context: store the data in a variable available to the global scope of the sending page. The opening page can then use chrome.extension.getBackgroundPage() to read the data directly.
If the source of the data is in either the background context or a content script: place the data into storage.local(MDN). The opening page can then read it when its JavaScript is run. For example, you could use a key called messageToNewExtensionPage.
If you are using runtime.sendMessage(), then initiate the transfer of the data from your newly opening page by sending a message from the that page's code to the source of the data (using runtime.sendMessage(), or tabs.sendMessage() for content script sources) requesting the data. The script with the data can then send the data back using the sendResponse(MDN) function provided by runtime.onMessage().
Wait to interact with the newly opening page until after at least the DOM is available, if not until after the JavaScript for the page has run. While it's possible to do this without the newly opening page providing specific notification that it's up and running, doing so is more complex and only useful in some specific cases (e.g. you want to do something prior to the JavaScript in the new page being run).2
Additional references
Chrome
Message Passing
Chrome extension overview
architecture
Communication between pages
Firefox
WebExtensions
Anatomy of a WebExtension
With some minor exceptions: e.g. using a content script to insert content into the page context.
There are multiple methods which you can use. Which way is best will depend on exactly what you are doing (e.g. when you need to access the view with respect to the code being executed in the view). A simple method would be just to poll waiting for the view to exist. The following code does that for opening a window:
chrome.windows.create({url: myUrl},function(win){
//Poll for the view of the window ID. Poll every 50ms for a
// maximum of 20 times (1 second). Then do a second set of polling to
// accommodate slower machines. Testing on a single moderately fast machine
// indicated the view was available after, at most, the second 50ms delay.
waitForWindowId(win.id,50,20,actOnViewFound,do2ndWaitForWinId);
});
function waitForWindowId(id,delay,maxTries,foundCallback,notFoundCallback) {
if(maxTries--<=0){
if(typeof notFoundCallback === 'function'){
notFoundCallback(id,foundCallback);
}
return;
}
let views = chrome.extension.getViews({windowId:id});
if(views.length > 0){
if(typeof foundCallback === 'function'){
foundCallback(views[0]);
}
} else {
setTimeout(waitForWindowId,delay,id,delay,maxTries,foundCallback
,notFoundCallback);
}
}
function do2ndWaitForWinId(winId,foundCallback){
//Poll for the view of the window ID. Poll every 500ms for max 40 times (20s).
waitForWindowId(winId,500,40,foundCallback,windowViewNotFound);
}
function windowViewNotFound(winId,foundCallback){
//Did not find the view for the window. Do what you want here.
// Currently fail quietly.
}
function actOnViewFound(view){
//What you desire to happen with the view, when it exists.
}
From MDN:
In Firefox versions prior to version 51, the runtime.onMessage listener will be called for messages sent from the same script (e.g. messages sent by the background script will also be received by the background script). In those versions of Firefox, if you unconditionally call runtime.sendMessage() from within a runtime.onMessage listener, you will set up an infinite loop which will max-out the CPU and lock-up Firefox. If you need to call runtime.sendMessage() from within a runtime.onMessage, you will need to check the sender.url property to verify you are not sending a message in response to a message which was sent from the same script. This bug was resolved as of Firefox 51.

Chrome Extension: How to send data from Panel Window to Popup?

I am working on Chrome extension which on clicking extension Icon popsup a window(popup.html) which contain button for Video Play. Upon clicking of the button opens another button which is created by using window.create of type panel. Upon clicking of PLAY button on popup.html does following:
Make a DIV enabled
Create new window by calling window.create with name mypanel.html. It uses panel.js to make ajax call and DOM changes functions.
Make an Ajax call, get data and play Video
Now thing is, the data I am getting via AJAX call in panel.js. I want to send that data back to pop.html which itself is using file popup.js. How can I send data from panel window to back to Popup Window?
The standard method of communicating between parts of the extension is described in Messaging documentation.
However, you need to keep in mind that as soon as a popup is closed, it is unloaded and the JavaScript context is destroyed along with it. So it cannot receive messages when closed and will lose all state when reopened.
There are two solutions to this:
You could employ a background page to hold state (if set to be persistent) and route messages between parts of the extension, e.g. a popup can request the current state when opening. This is an old-school solution.
If all you need is to pass/persist some data, you can easily employ chrome.storage.local to do it. You can save data with panel.js and react to changes with chrome.storage.onChanged event. The extra bonus is that when a popup reopens, it can simply read the storage to build its initial state.

Why is my add-on panelĀ“s content script no longer responding to port messages after changing contentUrl?

I am building a Firefox add-on using the addon-sdk.
The add-on has one button that displays a panel on click. The panel has a content script running it. Now, I need the panel to look different depending on the user's current tab and occasionally show an external url.
So, the main.js script tracks the current tab of the user and sends messages to the content script using panel.port.emit() and the content script changes the HTML of the panel to match whatever is needed.
However, as I mentioned, some times the panel is just showing an external url, so I need to reset it back to its original url. This is my code:
function panelListMode(currentTab, data){
panel.resize(350,300);
//This is the line causing the trouble
panel.contentURL = self.data.url("panel.html");
console.log("sending message");
panel.port.emit("generateList",data);
}
If I emit the message after changing the contentURL the content script does not seem to receive a thing. I now that specific line is the one causing the problem because if I comment it the content script receives the message.
Something tells me this is because the panel needs to load the DOM again and I need to wait until it is ready before the content script is able to do anything. But... aren't content scripts something apart from just included scripts?
As far as I am able to tell the panel has no "ready" event of sorts that I can listen to for emitting the message.
What am I missing?
Edit: After some experimentation I have been able to find some more. If I copy panel.html to the same directory as panel2.html and change my function so it goes:
function panelListMode(currentTab, data){
panel.resize(350,300);
//This is the line causing the trouble
panel.contentURL = self.data.url("panel2.html");
console.log("sending message");
panel.port.emit("generateList",data);
}
The problem is gone. I have experimented a bit and it seems that when I try to change the contentURL to the html that is already set in the panel the content script stops working all together.
This is some really odd behavior... have I encountered a bug?
As far as I am able to tell the panel has no "ready" event of sorts that I can listen to for emitting the message.
I think it does, at least internally it seems to but I'm not sure whether they're exposed.
But even if it doesn't you can simply fire your own port message from the content script once it has been attached and registered its own listeners. Upon receiving that message in the addon you can then resume using the port on that side.

Opening a new window using Javascript after a timeout caught by popup blocker

I'm having a situation in which I want to allow the user to download a PDF. The generation of this PDF can take a couple of seconds, and I don't want a webserver thread to wait for the generation of the PDF, since that means the thread isn't available to handle other incoming requests.
So, what I would like to do is introduce the following URL patterns:
/request_download which invokes a background job to generate the PDF;
/document.pdf which will serve the PDF once it is generated
The use case is as follows.
A user clicks on 'Download PDF'. This invokes a piece of Javascript that'll show a spinner, make a request to /request_download and receive a HTTP 202 Accepted, indicating the request was accepted and a background job was created. The JS should then poll the /request_download url periodically until it gets HTTP 201 Created, indicating that the PDF has been created. A Location header is included that is used by the JS to forward the client to /document.pdf. This has to be in a new window (or tab, at least it shouldn't replace the current page). The level of expertise of our users is very low, so when I forward to the url, they might not know how to get back to the website.
The problem
Now, window.open works fine if it is invoked by the user via a click event. However, I want to invoke window.open in a callback function through setInterval as soon as I see that 201 Created response. However, browsers won't like that and will interpret it as a popup, causing it to get caught by popup blockers on IE8-10 as well as Chrome. Which makes sense.
I also can't open a new window and invoke the Javascript over there. In that case, users would see a blank page with a spinner. If that page then forwards to the /document.pdf, IE will show this yellow warning bar telling that it prevented files from being downloaded. Chrome and Firefox will do this without any problems, but a large percentage of our users is on IE.
What I've tried, and didn't work
window.open(location)
Setting the src of an iframe to the retrieve location
Manually adding an <a> tag to the document body and trying to click it using javascript
Adding an <a> tag to the document body and invoking a MouseEvent on it
Partially works: opening a new window on user click and storing a reference to it, then perform the asynchronous requests and upon completion, set the location of the earlier opened window. But: IE will block this and say it prevented files from being downloaded. So still not a fully working solution.
I'm straight out of ideas on how I can make this work and decided and decided to ask The Internet for help. I'm hoping you guys can help me with this, or recognise the problem. Thanks in advance!

Categories

Resources