I have a requirement to launch a chrome application from a web page button click. I have found the following resources,'
Run Google Chrome application from url
Activate chrome app from web page?
How can I launch a Chrome Packaged App through javascript?
Which suggests to use externally_connectable and url_handlers but doesn't seem to work. Is there a proper way I could launch a chrome application via button click on web page by calling the chrome app extension id?.
I am new to this field and any help from you experts would be greatly appreciated :)
PS
I tried the following command on a button click on my web page,
chrome.management.launchApp('app id');
This resulted in an error Uncaught TypeError: Cannot read property 'launchApp' of undefined.
I found how to achieve this incase someone else comes across this issue.
in your web page on button click for example, include the following code,
//data passed from web to chrome app
chrome.runtime.sendMessage('your_chrome_app_id', { message: "version" },
function (reply) {
if (reply) {
if (reply.version) {
//log the response received from the chrome application
console.log(reply.version);
}
}
}
);
in your chrome application manifest.json define the externally_connectable url/ urls as follows,
{
"name": "App name",
"description": "App Desc",
"version": "1",
...
"externally_connectable": {
"matches": ["*://localhost:*/"]
}
}
In your application background.js setup a listener to be invoked when a message is sent from the web page as follows,
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request) {
if (request.message) {
if (request.message == "version") {
//my chrome application initial load screen is login.html
window.open('login.html');
//if required can send a response back to the web site aswell. I have logged the reply on the web site
sendResponse({version: '1.1.1'});
}
}
}
return true;
});
Hope this helps :)
Hasitha's reply worked. However in the manifest file I needed to do a regex change.
For the URL
localhost:3905/manager
obviously the externally_connectible value should be set as
"externally_connectable": {
"matches": ["*://localhost:*/*"]
}
But for some reason, the same URL cannot be matched with
"externally_connectable": {
"matches": ["*://localhost*"]
}
did not work.
Generated error Invalid match pattern '://localhost'
Related
Sorry for my poor English, i hope you can understand the issue.
I'm new to chrome extension development,and for sure in my code there are a lot of
thing to change or optimize;
anyway i've written a simple code that, (seems) works at least from my chrome.
The code clicks a button every X minutes in specific page, then wait and parse the result in page.
I've :
a content script (loaded from manifest.json) which "inject" some button and text Input box in page, so user can sets some "filter params" before click a "start button"; the start button then sendMessage() to background.js to set Alarm Event for the click ;
an eventPage (which is set persistent true in actually ) which handle the request from tabs and set a countdown alarm for each tab; when X min are passed fire a message to the interested tab;
I also have a popup.html e popup.js not important here (i think).
I've to distribuite this extension manually, so i would distribuite a zip that user can load with "developer mode ".
*Now the issue is: why the code was working only on my Chrome ? *
I've tested with others 2-3 laptop with Chrome, the background script is loaded (i can see the background page printint console log)
but in webpage the contents.js seems no way executed .
In my chrome works well: i can see in console some initial output (i print the name of dir extension to check) and
the dynamic created element (button,input box ect.) in page.
And all is working, i can fire the start button and receive results of parsing.
During the development i've never run the extension on other machine. Yesterday i've succssfully tested on 2-3 laptop.. then i made only few change but nothing serious.
Today i can run only in my chrome.
In other pc nothing, neither the simple console.log output first line of script.
I can read in console log :
"Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist."
but this also in my working istance in my laptop chrome .
The zip file is the same and the extraction is good, in fact i can actually load the extension and i see the background page debug console.log() sentences
In some case, in laptop where it dosen't work, i've received a message relative jQuery and the fact that chrome.runtime.sendMessage() is not defined; and it points to code in webpage, not mine.
I've see that in webpage code there is something like:
var extid = "mcmhdskbnejjjdjsdkksmeadjaibo";
var extVer = "1.5";
var extStatus = 0;
$(document).ready(function () {
///...
chrome.runtime.sendMessage(extid, {message: "version"},
function (reply) {
if (reply) {
if (reply.version) {
if (reply.version == extVer) {
if (reply.gminfo != 'OK') {
extStatus = 1; /// ...
Seems that chrome.runtime is undefined, and the webpage can't call the sendMessage().
EDIT: this undefined occurs only when my extension is loaded
Maybe there is some conflict when i load my extension? But in my chrome browser works...
Can some expert indicate in where direction i've to investigate?
Thanks a lot for any suggestions.
My Manifest.json :
{"manifest_version": 2,
"name": "myAlarm",
"description": "This extension alerts.",
"version": "0.1",
"permissions": [
"alarms",
"system.cpu",
"storage",
"tabs",
"webNavigation",
"https://www.mytargetsite.com/subUrl/"
],
"web_accessible_resources": [
"icon.png",
"vanillaSelectBox.css"],
"content_scripts": [
{
"matches": ["https://www.mytargetsite.com/subUrl/"],
"css": ["vanillaSelectBox.css"],
"js": ["jquery-3.3.1.min.js","vanillaSelectBox.js","taffy-min.js","content.js"],
"run_at": "document_end"
}
],
"background": {
"scripts": ["eventPage.js"],
"persistent": true
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": {
....
}
}
My contents,js (stripped):
chrome.runtime.onMessage.addListener(
function(request, sender) {
// here i parse message "time'up" from background js
});
window.addEventListener('load', function() {
var pt=chrome.runtime.getURL('filterOff.wav');
var p=pt.split("/");
console.log("[myAlarm v0.1] started" );
console.log("[myAlarm v0.1] folder : ("+p[2]+")");
// here i start an active wait for the presence in page of button with ID= btntarget_id
waitForElementToDisplay("#btntarget_id", 500); //when function find button then create and add button and input text to webpage
});
My eventPage.js :
var curr_alarms =[];
chrome.extension.onMessage.addListener(function(request, sender)
{ /// here receive start countdown message from content.js and set alarm ...
}
chrome.alarms.onAlarm.addListener(function(alarm) {
/// here i manage each alarm for each tab
});
chrome.tabs.onRemoved.addListener(function(tabid, removed) {
// ...
});
chrome.tabs.onUpdated.addListener(function
(tabId, changeInfo, tab) {
//
});
edit : in browser where it dosen't work i can read also :
Access to XMLHttpRequest at 'https://mytargetsite.com/suburl/grid.php' (redirected from 'https://mytargetsite.com/suburl/grid.php') from origin 'https://mytargetsite.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request.
The fact that the declared content script runs or not, should be verified by inspecting in devtools => sources sub-tab => content scripts sub-sub-tab. If it really doesn't run, there can be just two explanations: the URL is different (for example not https) or extensions are blocked by their domain admin via runtime_blocked_hosts which you can see in chrome://policy.
Your development mode extension's id will be different on a different machine unless you pin it by adding a "key" in manifest.json
To use chrome.runtime to send messages to your extension from a webpage code (not from a content script!) your extension's manifest should declare "externally_connectable" and use a different event onMessageExternal, see also sending messages from web pages.
The CORS error may be irrelevant to your code (you can investigate the source of the error by expanding the error's call stack in devtools console).
How can I listen/track when chrome extension is installed from the web store?
I previously had an inline installation of the extension but by inline installations coming to an end soon, I want the user action to open the web store to install the extension and listen for when they add the extension for UI changes and act based on that.
I tried the messaging approach found in here but it seems not working.
manifest.json looks like:
"background": {
"scripts":["index.js"],
"persistent": false
},
"permissions": ["desktopCapture"],
"externally_connectable": {
"matches": [
"*://localhost:*/*",
"https://*.stageten.tv/*"
]
}
and index.js :
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request === screenShareExtensionId) {
if (request.message) {
if (request.message == "version") {
sendResponse({version: 1.0})
alert('Hiiii')
}
}
}
return true;
})
and inside my app:
chrome.runtime.sendMessage(screenShareExtensionId, { message: "version" },
function (reply) {
if (reply) {
if (reply.version) {
return true;
}
}
else {
return false;
}
})
and based on the value in my redux logic, the UI either changes or not/waits for the extension to get installed.
You can do it at the start of your background page.
You need to save a flag (for example, it can be a version of the extension) to the localStorage.
After that, on each start of the background page, you need to check if this flag is in your storage. If there is no flag - then you need to track install, otherwise, it's just usual reload of the background page.
The same way can be used to track updates of the extension from the store, just need to compare versions.
Solved this this in this self-answered question, which I can't mark as a duplicate of this because of no accepted/up votes.
Here's how I solved it from the background script (w/o using a content script):
background.js
Listen for onInstalled event.
Query all opened tabs that match the URL's you want to notify.
Execute a small script in each tab that will postMessage notifying
that installation was succesful.
chrome.runtime.onInstalled.addListener(function listener(details) {
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
chrome.tabs.query({
url: [
'https://localhost:3000/*',
'https://staging.foo.com/*',
'https://production.foo.com/*'
]
}, tabs => {
Array.from(tabs).forEach(tab => {
chrome.tabs.executeScript(tab.id, {
code: `window.postMessage('screenshare-ext-installed', window.origin);`
});
});
});
chrome.runtime.onInstalled.removeListener(listener);
}
});
manifest.json
Just make sure both externally_connectable and permissions declare
the URL patterns of the sites you want to notify.
"externally_connectable": {
"matches": [
"https://localhost:3000/*",
"https://staging.foo.com/*",
"https://production.foo.com/*"
]
},
"permissions": [
"desktopCapture",
"https://localhost:3000/*",
"https://staging.foo.com/*",
"https://production.foo.com/*"
],
Web page
Just listen somewhere for the postMessage message fired by
the extension on succesful installation.
window.onmessage = e => {
if (e.data === 'screenshare-ext-installed') {
// extension successfully installed
startScreenShare()
}
}
Credits
#wOxxOm's comment
I am trying to pass data from my webpage to the chrome extension.
manifest.json (I am trying to do this in my local environment first)
"externally_connectable": {
"matches": ["*://localhost/*"]
}
In listen.js (a background script):
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
alert('test2');
alert(request.openUrlInEditor);
});
None of the alerts display above.
I got the extension ID of the unpacked chrome extension by viewing the ID when I navigate to chrome://extensions/. In my webpage in localhost environment
// DEVELOPMENT extension ID
var editorExtensionId = "fppgjikaoolnlcmdjalbfkmlcadcckmb";
var url = 'test';
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
When I run this. in the browser, I get an error saying:
Error in event handler for (unknown): TypeError: Cannot read property 'success' of undefined
I'm not sure where to begin debugging this.
After making a few changes in your code I am able achieve your goal.
See below the complete code.
manifest.json
{
"manifest_version": 2,
"name": "CS to Bg Communication",
"version": "0.1",
"background": {
"scripts": ["listen.js"]
},
"content_scripts": [
{
"all_frames" : true,
"matches": ["<all_urls>"],
"js": ["contentscript.js"]
}
],
"browser_action": {
"default_popup": "popup.html"
},
"externally_connectable": {
"matches": ["*://localhost/*"]
}
}
listen.js - the background script
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
blacklistedWebsite = 'http : / / yourdomain . com /';
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor) {
alert('test2 - ' + request.openUrlInEditor);
sendResponse({"success": true, "AckFromBG": "I have received your messgae. Thanks!"}); // sending back the acknowlege to the webpage
}
});
contentscript.js - the content script - actually does nothing
console.log("this is content script");
web-page.html - The local web page
<html>
<body>
This page will will send some message to BG script
<script type="text/javascript">
// DEVELOPMENT extension ID
var editorExtensionId = "fjaedjckfjgifecmgonfmpaoemochghb"; // replace with your extension ID
var url = 'test';
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url}, function(response) {
console.log(response);
if (!response.success)
handleError(url);
});
function handleError(url) {
console.log(url);
}
</script>
</body>
</html>
Summary of changes:
Defined blacklistedWebsite - this was undefined.
Added sendResponse({"success": true, "AckFromBG": "I have received
your messgae. Thanks!"}); to send back the acknowledgment to the
webpage.
Define function handleError(url) {
console.log(url);
} this was not defined.
That's it. Hope this will solve your issue.
The error you are getting indicates that chrome.runtime.sendMessage is executed. This means that external messaging is set up correctly: otherwise, chrome.runtime.sendMessage wouldn't be exposed to your page's code.
I think the problem is here:
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
I suspect the problem is blacklistedWebsite being undefined; the code fails with a ReferenceError and no meaningful response is being sent. Since the onMessage listener terminates (even with an error), the calling code gets an undefined response.
Check your background console to confirm if that's the case; if you copied this partial example code but aren't using this blacklisting functionality, delete this branch.
In future, please make sure you understand what every line of code you copy does!
I want to be able to communicate from content-script (any tab) to background.
According to the documentation, I should be using chrome.extension chrome.runtime.onMessage with chrome.runtime.sendMessage.
So that's what I did:
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"*://*/*"
],
background.js
console.info('1');
chrome.runtime.onMessage.addListener((request, sender, sendReponse) => {
console.info('2');
sendReponse({ msg: 'foo' });
return true;
});
I'm not very sure the return true; is needed. I tried with and without.
After building and reloading the extension I accessed chrome background page through chrome://extensions
> 1 // background.js file was run
> chrome.runtime.sendMessage({ hello: 'so' }, v => console.info(v))
> undefined
> undefined // This is the callback response I get. Why?
More important, I also get an empty response callback when running sendMessage from other tabs (i.e. stackoverflow.com)
> chrome.runtime.sendMessage('extension id', { hello: 'so' }, v => console.info(v))
> undefined
> undefined
Why do I get an empty response callback?
Am I missing any special permission? Wrong parameters? Maybe wrong API functions to do so?
runtime.sendMessage doesn't send the message to the sender's context since Chrome 49.
If sending to your extension, the runtime.onMessage event will be fired in every frame of your extension (except for the sender's frame),
webpages can send messages to your background/event page in two cases:
externally_connectable key in manifest.json allowed that page
a content script of your extension is loaded for that page, in which case you can switch the context to your extension in the console toolbar to be able to send messages manually from console:
The content script can be declared in manifest.json in which case it's autoinjected by [re]loading the page. Or you can inject it explicitly with tabs.executeScript from the background/event page.
I am building an extension that requires access to history to provide one of the features.
After publishing a version which contained the permission as mandatory and consequently losing a part of my users because they got scared away by the big alert saying that the extension might be able to snoop into their history (I really didn't plan on doing that), I decided to publish a version with the offending part removed and the permission disabled as a temporary fix.
I'd like to implement this feature back using optional permissions.
First of all, I added the new optional permission to my manifest file:
...
"permissions": [
"https://news.ycombinator.com/",
"http://news.ycombinator.com/"
],
"optional_permissions": [ "history" ],
...
Then, I built a function to request permissions into the script which handles the extension's settings:
Settings.prototype.applyPermissions = function (permissions, map) {
Object.keys(permissions).forEach(function (key) {
if (map[key]) {
var checkbox = map[key].getElementsByTagName("input")[0];
checkbox.addEventListener("change", function (e) {
if (this.checked) {
chrome.permissions.request(permissions[key], function(granted) {
if (granted) {
// Permission has been granted
} else {
// Not granted
}
});
}
});
}
});
};
The key part here is this:
checkbox.addEventListener("change", function (e) {
if (this.checked) {
chrome.permissions.request(permissions[key], function(granted) {
if (granted) {
// Permission has been granted
} else {
// Not granted
}
});
}
});
I perform the request on an event caused by user interaction (the guide states that it won't work otherwise), and pass permissions[key], an object specified in my extension's settings which looks like this:
"permissions": {
"mark_as_read": {
"permissions": ["history"]
}
}
When accessing it as permissions[key], I get this part:
{
"permissions": ["history"]
}
Which is basically the format that the documentation shows for this kind of requests.
If I run this code and toggle the checkbox that should enable the feature, and look at the error log, I see this error:
chrome.permissions is not available: You do not have permission to
access this API. Ensure that the required permission or manifest
property is included in your manifest.json.
I also tried accessing this API from a background page, where it was actually available but I was not allowed to use because Chrome requires that you access it from a user interaction, and such interaction is lost if you send a message to the background page from your content script to request activation.
Am I missing something obvious here? Maybe I need to add something to the manifest, but I can't find any explicit documentation about it.
I assume you're trying to do this from a content script. You can't access most chrome.* APIs from content scripts, including chrome.permissions. However, you've correctly pointed out that a background page is also unsuitable, because you a permission change requires a direct user action.
Luckily, we have hardly exhausted our options. You could set the permission in:
The extension's options page
A browser action popup
A page action popup
Any page in your extension served through the chrome-extension:// scheme, provided you include the page and necessary sub-resources as web_accessible_resources in your manifest
In the last case, get the URL using chrome.extension.getURL. You could possibly use an iframe to inject it directly into the page, if you don't want the permission-requesting interface to be separate from the current page.
So, in fact, content scripts and background pages are the only two extension contexts where you can't use chrome.permissions.