Run a function from injected js - google-chrome-extension

I have been going around in circles with this, so I would appreciate some help
This is what I want to achieve
User presses my extension ison
Popup appears with two buttons, 'run function a' and 'run function b'
When they press a button it runs the function in my own js file, that I have injected.
Function a for example, could be to count the number of elements of a certain type in the active tab
So, I can inject my js file on page load (this is in my contentscript.js)
var s = document.createElement('script');
s.src = chrome.extension.getURL('temp-file.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
This works, and I can see the js being excuted
But what I can do is to have a function run that is in temp-file.js
For example in the popup I have
chrome.tabs.executeScript(null, {code:"shows();"});
I get this: Uncaught ReferenceError: shows is not defined
If I enter shows(); into the console, it works as expected
I presume that the issue is all about the context. I tried various things in the popup.js page to also inject the file but nothing seemed to work
Is anyone able to point me in the right direction please
Thanks
Grant

I presume that the issue is all about the context.
You're right about it.
The file "temp-file.js" has been injected into host page, so it is now part of host page context. Extension can mess with it - since it is in different context.
Run a function from injected js
Solution:
Not sure about what you are trying to achieve. pick what suits you:
Split injected file
Code/functions you want to execute on a page - use them as contentscript.
In this case split you "temp-file.js" - part extension has execute (becomes part of contentscript) and part host page has to execute(your code snippet).
use custom event
Use custom event - generate custom event in contenscript - listen for it injected script. custom event

Your question does not say what exactly you are trying to achieve.
This is what I understood. You want to execute a function on your contentscript.js from your popup.js.
If that is the case then you can call a method on contentscript from popup.js like mentioned here https://developer.chrome.com/extensions/messaging

Related

Trigger a dialog box in background script

I have a chrome extension, where I periodically throw out an alert based on something.
The thing is that the default alert in Javascript is very ugly and I am trying to replace it with something more beautiful.
The problem is that currently the alert is triggered from the background script. Google doesn't allow us to include any external libraries in the background html.
Given this problem, how do I go about replacing the default alert with a more modern UI alert?
I was looking to replace the default alert with something like the SweetAlert.
My background.js currently looks like this:
// on some alarm trigger
function showpopup() {
console.log(" in show popuup");
console.log(Date());
alert("ugly alert");
}
I also explored the option of injecting another js file from my background file.
function showpopup() {
console.log(" in show popuup");
console.log(Date());
var s = document.createElement('script');
// added "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
}
My script.js currently just calls an alert
alert("ugly alert now in script.js");
I am not able to figure out how to create my own dialog box in this javascript file script.js.
The problem is where your alert will be shown?
In an browser/OS dialog window? That's what alert() and friends do; as you see yourself, it's ugly and inflexible. In addition, it's technically challenging: it's an old mechanism that stops execution of JS code until closed, which can lead to API malfunctioning; Firefox WebExtensions specifically don't support calling this from the background page.
In the background page? By definition, it's invisible. You can add DOM nodes with an alert there, but you will not see it. Your problem isn't loading a library, your problem is where to display results.
(invisible, so no picture here!)
In the currently open tab? Hijacking an arbitrary page to show your own UI is hard, prone to break, would require draconian permissions with user warnings at install, won't always work. Wouldn't recommend.
In a fresh window? Possible (see chrome.windows API), but hardly "modern UI" at all (at least you can hide the URL bar).
In a browser action popup? Still not possible to trigger it to open in Chrome, so that's out.
The de-facto standard for informing the user about such things is the chrome.notifications API. It offers limited customization, but that's the "modern" approach considering that your extension has no UI surfaces already open at alert time.
You can insert your code into the tab content via
JS: chrome.tabs.executeScript()
CSS: chrome.tabs.insertCSS()
The second possibility would be to use a content script (content.js). But then you would have to use messaging to communicate between background.js and content.js.

Wait for chrome.tabs.update tab to finish loading

I'm trying to work on a chrome extension and am trying to clean up some of my code by relying on the sendMessage. However the callback function activates before the page has finished loading so in the case of a new tab, nobody receives and in the case of an existing tab the page that is being moved from is getting the message (but that isn't what I want). I've looked for other people asking about that problem with new tabs and there wasn't a clear answer, the best suggestion I've seen is to create a global variable and create a listener for tab loads and compare it against this global variable.
So the question is, is there a way to wait in the callback function until the page has loaded, or do I create an array of JS objects that contain the tab I'm waiting on and the information I want to send to that tab.
For reference here is the relevant code in the background javascript file.
chrome.tabs.sendMessage(tab.id, {info: "info"}, function(response)
{
//This line isn't used when I am navigating without changing tabs
chrome.tabs.create({url: response.info.linkUrl}, function(tab1)
{
chrome.tabs.update(tab1.id, {url: response.info.linkUrl}, function(tab2)
{
chrome.tabs.sendMessage(tab2.id, {info: "More Info"});
});
});
});
Otherwise I am able to confirm that all of my tab side code works, once my sendMessage was delayed enough for me to see that with my own eyes. My code is able to consistently make it past validation on the page being navigated away from, confirmed by checking document.url.
You can try injecting a second content script instead of a message.
It will execute in the same context as your other script.
Something along the lines of
chrome.tabs.executeScript(tab2.id,
{code: 'showInfo("More Info);', runAt: 'document_idle'}
);
where showInfo does the same as your message handler.
It's a bit of a hack and I'm not 100% sure the load order will be correct.
Other possible solutions are more complex.
For example, you can make the content script report back that it is ready and have a handler for that, for instance you can register a listener for onMessage in the background that waits for a message from that specific tab.id, sends "More Info" and then deregisters or disables itself.
Or, you could potentially switch to programmatic injection of your content script, which would let you control load order.

How To Make Page To Interact With Background Page?

My Chrome extension has a background page that execute script on current tab.
The script change elements on current tab by adding code to existing elements to call function 'myFunction' defined at background page when 'onClick' events occur.
The problem is that exception is thrown that 'myFunction' is not defined on current tab.
what is the best way to enable this interaction? to enable current page to go to function defined on background page?
Thanks in advance!
The background page is executed in an independent context, and thus its functions can't be directly executed in the currently opened tab.
What you need is a content script executed on all the tabs, that then communicates with the background page, using the message passing mechanism.
Without more information, it's difficult to help you more.
As mentioned in the first answer, "Without more information, it's difficult to help you more.", but for your second question it sounds like what you need is a reference to the function defined in your background page. This can be achieved with the getBackgroundPage function. The code looks like this;
var bgPage = chrome.extension.getBackgroundPage();
bgPage.myFunction();

Does view.postscript() allow you to call functions loaded as output scripts?

I am using <xpScriptBlock> to store the contents of two rather long client side functions that loads an ExtJS grid. I can call the function using clientside javascript just fine.
I discovered that I need to show a different grid based on a condition in the underlining document. To reference the backend I moved the code to serverside and then tried to call the grid using view.postScript. This does not work and is the basis of my question.
Is this approach even possible? I do not wish to put all the code into the event. The functions are quite long and better kept in a script block for readability and maintainability. The functions are definitely loaded in the client, as I can manually load them using the firebug console. Perhaps I am missing something simple so I wanted to ask before changing my approach.
var typePO = document1.getItemValueString("typePO");
if(typePO == "AFS"){
view.postScript("loadGridAFS();")
} else {
view.postScript("loadGridOther();")
}
This code is in the serverside onClientLoad event of a panel. I have tried adding the 'return' keyword and it makes no difference.
UPDATE: I can't even get simple alerts to work using view.postscript(). Does this method only work in certain types of events in SSJS???
After some experimenting using a simple alert I can say that view.postScript() does NOT work everywhere.
For a test, I put the same code in an six event of the xpage. Here is an example of the code I used: view.postScript("alert('onClientLoad');"); I just changed the message to match the event.
Here are the results:
onClientLoad = nothing
beforePageLoad = XSP error
afterPageLoad = WORKS!
afterRestoreView = nothing
beforeRenderResponse = WORKS!
afterRenderResponse = nothing
I haven't tried every available event out there, but the bottom line here is that you shouldn't count on view.postscript() to work everywhere. And if it does do nothing, try a simple alert first to see of the event supports view.postscript before questioning the client javascript code you are attempting to run.

Open and pass data to a popup (new tab) from content script

I'm writing a chrome extension and have a question.
My extension has some .html page in it, let it be 'popup.html'. I inject a content script into some page and this script opens a 'popup.html' in a new tab with something like 'var p = window.open(chrome.extension.getURL('/popup.html'), "popup")', which works perfectly. Next, I need to pass some data to this window and I can't figure how to do it in a simple way.
For some reason I can't call child window's function from a content script with
var p = window.open(chrome.extension.getURL('/popup.html'), "popup");
p.foo(data);
In the console I see Uncaught TypeError: Cannot call method 'foo' of undefined message.
I can't pass data in a query string, because the data is simply too big.
Is there an elegant and simple way to pass data to such kind of window? I thought about messaging, but how do I effectively get tab ID of a newly opened window w/out using a background page?
Thanks a lot in advance.
UPD:
I tried to inverse the logic and get a data from parent window with 'window.opener.foo()' but in a newly opened tab window.opener returns null.
Ok, I found two solutions to my problem.
1) Add a background page, which opens a popup with chrome.tabs.create(). Then send a message from a content script to a background page, which re-sends it to a corresponding tab via chrome.tabs.sendMessage(). It looks a little ugly, but works.
2) A better one, w/out background page. Extension (popup) page creates a listener for long-lived connection. Then content script sends a message to this connection. A problem here is that a listener is not created right after the page is opened, so there should be a mechanism for a content script to wait until popup is loaded. It can be a simple setTimeout or a notification from popup via same long-lived connection.
If anyone has a better solution I'd gladly check it out as well.

Resources