I'm new to Chrome Apps, however I did successfully get my app to work with GCM. I'm so happy! What I would like the app to do however, is open a popup window when the user gets a notification. I'm making it a video chat app. Please Any Help at all would be greatly appreciated! This code that I have is currently not working for popups, tabs or anything of the sort... :(
background.js
// Returns a new notification ID used in the notification.
function getNotificationId() {
var id = Math.floor(Math.random() * 9007199254740992) + 1;
return id.toString();
}
function messageReceived(message) {
// A message is an object with a data property that
// consists of key-value pairs.
// Concatenate all key-value pairs to form a display string.
var messageString = "";
for (var key in message.data) {
if (messageString != "")
messageString += ", "
messageString += key + ":" + message.data[key];
}
console.log("Message received: " + messageString);
// Pop up a notification to show the GCM message.
chrome.notifications.create(getNotificationId(), {
title: 'GCM Message',
iconUrl: 'gcm_128.png',
type: 'basic',
message: messageString
}, function() {});
chrome.tabs.create({url:"http://www.google.com"});
}
var registerWindowCreated = false;
function firstTimeRegistration() {
chrome.storage.local.get("registered", function(result) {
// If already registered, bail out.
if (result["registered"])
return;
registerWindowCreated = true;
chrome.app.window.create(
"register.html",
{ width: 500,
height: 400,
frame: 'chrome'
},
function(appWin) {}
);
});
}
// Set up a listener for GCM message event.
chrome.gcm.onMessage.addListener(messageReceived);
// Set up listeners to trigger the first time registration.
chrome.runtime.onInstalled.addListener(firstTimeRegistration);
chrome.runtime.onStartup.addListener(firstTimeRegistration);
Manifest.json
{
"name": "GCM Notifications",
"description": "Chrome platform app.",
"manifest_version": 2,
"version": "0.3",
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": ["gcm", "storage", "notifications", "tabs", "<all_urls>"],
"icons": { "128": "gcm_128.png" }
}
chrome.notifications.create should appear outside the browser. I can't see anything wrong with the call you made, though. We need to determine if you're not breaking the Content Security Policy
In Chrome Apps, due to a strict Content Security Policy these URLs must point to a local resource or use a blob or data URL. Use a 3:2 ratio for your image; otherwise a black border frames the image.
Additional references you can look at is the official notification sample repo as well as how to integrate notification with GCM.
Related
I'm trying to create a simple extension for Chrome and Firefox which just gets some content from the DOM of certain pages and adds other content.
But the issue is that it only works the first time I load it as a temporary Add-on for testing on both Chrome and Firefox, or when I hit the extension's reload button on about:debugging on Firefox.
My manifest.json only contains the following information:
{
"manifest_version": 2,
"name": "ft_blackhole",
"version": "0.1",
"description": "Shows how many days you have left before you get absorbed by the Blackhole.",
"icons": {
"48": "icons/blackhole.png"
},
"content_scripts": [
{
"matches": [
"https://profile.intra.42.fr/",
"https://profile.intra.42.fr/users/*",
"https://profile.intra.42.fr/users/*/"
],
"js": [
"ft_blackhole.js"
]
}
],
"browser_specific_settings": {
"gecko": {
"id": "ft_blackhole-0.1#intra.42.fr"
}
}
}
I use almost the same exact manifest file (same exact matches) for another extension that works and depends on the same URLs. That extension works fine. The difference between it and this one is that while they both insert content in the DOM, my older extension does it after it fetches data from an API, but this one just gets some text from the current page's DOM:
ft_blackhole.js:
console.log("Hello World");
let blackholeDiv = document
.getElementById("bh")
.getElementsByClassName("emote-bh")[0];
let daysLeft = blackholeDiv.getAttribute("data-original-title");
let daysNum = daysLeft.split(" ")[0];
let status = (() => {
if (daysNum <= 14)
return {cat: "😿", color: "#D8636F"};
else if (daysNum <= 42)
return {cat: "🙀", color: "#F0AD4E"};
else
return {cat: "😸", color: "#5CB85C"};
})();
let daysLeftDiv = document.createElement("div");
daysLeftDiv.innerText = daysLeft + ' ' + status["cat"];
daysLeftDiv.style.color = status["color"];
daysLeftDiv.style.fontSize = "0.7em";
daysLeftDiv.style.fontWeight = "400";
blackholeDiv
.children[1]
.appendChild(daysLeftDiv);
I searched all over the internet, I couldn't understand what is causing it to run only the first time the extension is installed, but then when I refresh the page, it doesn't add anything to the page anymore, until I reload the extension again from about:debugging.
I hope someone could help.
Edit:
I also noticed that it also works after a hard reload of the page on Chrome, but not after a normal reload.
Edit 2:
When I console.log() something in the content script, it works all the time, always logs when I reload the page (normal reload), but the other code for DOM manipulation doesn't...
Edit 3:
I have uploaded a static copy of the profile page if anyone wants to take a look at the HTML content: https://haddi.me/intra-example/intra.html
Edit 4:
It seems the issue is caused after everything is loaded in the DOM, by some javascript that runs after that and makes changes to it, I used Mutation Observer on the target node to log those changes, and there were indeed some few ones, what I can't figure out is how am I supposed to run my code only after that last change (Which is their modification of span#bh-date's style attribute)? This the code I added:
const blackholeDiv = document
.getElementById("bh")
.getElementsByClassName("emote-bh")[0];
// Callback function to execute when mutations are observed
• const callback = function (mutationsList, observer) {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
console.log('A child node has been added or removed: ', mutation);
}
else if (mutation.type === 'attributes') {
console.log('The ' + mutation.attributeName + ' attribute was modified: ', mutation);
}
}
};
const config = {attributes: true, childList: true, subtree: true};
const observer = new MutationObserver(callback);
observer.observe(blackholeDiv, config);
And these are the logs:
Screenshot from Chrome's Devtools Console
In your manifest.json file, I do not see a "run_at", to make sure the DOM is ready and then immediately inject the script. Use the "document_end" string in your code.
Source:
https://developer.chrome.com/docs/extensions/mv3/content_scripts/#run_time
manifest.json
{
"name": "Test Extenstion",
"description": "Content test!",
"version": "1.0",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["https://haddi.me/*"],
"js": ["content.js"],
"run_at": "document_end"
}
],
"permissions": ["activeTab"]
}
Tested in Google Chrome web browser version 99.0.4844.83.
i am trying to program an extension to intercept downloads and rename them
manifest.json:
{
"name": " Ebooks Downloader",
"description": "Automatically rename ebooks downloaded from gutenberg.org",
"version": "1.0",
"author": "",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["https://gutenberg.org/ebooks/*"],
"js": ["content_script.js"]
}
],
"permissions": [
"https://gutenberg.org/*",
"storage"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": [
"downloads"
]
}
content_script.js :
// Get the content of the h1 title
var nameProp = document.querySelector('[itemprop=name]').textContent;
// Set everything to lower case, remove special characters and standardize format
nameProp = nameProp.toLowerCase().replace(/[^a-z0-9 ]/gi, '');
var filename = nameProp.replace(' by ', ' - ');
// use the storage API
chrome.storage.local.set({[document.URL]: filename}, function() {
console.log('Book filename is stored as: ' + filename);
});
background.js:
chrome.downloads.onDeterminingFilename.addListener(function(item, suggest) {
if (item.referrer.search("gutenberg.org") == -1) {
// If the file does not come from gutenberg.org, suggest nothing new.
suggest({filename: item.filename});
} else {
// Otherwise, fetch the book's title in storage...
chrome.storage.local.get([item.referrer], function(result) {
if (result[item.referrer] == null) {
// ...and if we find don't find it, suggest nothing new.
suggest({filename: item.filename});
console.log('Nothing done.');
}
else {
// ...if we find it, suggest it.
fileExt = item.filename.split('.').pop();
var newFilename = "gutenberg/" + result[item.referrer] + "." + fileExt;
suggest({filename: newFilename});
console.log('New filename: ' + newFilename);
}
});
// Storage API is asynchronous so we need to return true
return true;
}
});
I have two problems:
the console gives two errors particularly at chrome.storage.local.set and chrome.storage.local.get it says Uncaught TypeError: Cannot read properties of undefined (reading 'local') i tried running the code only with chrome.storage.local.set({[document.URL]: "hi"}) in console and still gave error
i know that i used suggest but i want the extension to just rename the file without having me to press the pop-up
Moving my observation to an answer, since it worked:
I just noticed that you have 2 "permissions" sections in your manifest.json. It's possible the 2nd one is overriding the first one. Try combining them and see if that works.
Basically what I am trying to do is send a message from the popup.js by grabbing values from the input fields, and onMessage in the content_script to assign those values from that local scope to the global scope. I can't manage to do it! I can close the whole code in the onMessage function, but then to get to next function of the code I have to keep clicking save on the chrome extension. If someone knows this way better than I do with chrome extensions and what not please review my code and guide me to progress. I have been on this for like a week and my head is about to expload!
I have tried enclosing all the code in the onMessage function in the content_script. I have tried flipping which .js files are sending the message and trying to send the input fields from the extension as a response. I have tried to exclude the sendMessage out of the .click DOM function. Reviewed Messaging API on Google Chrome Extension and im puzzled.
content.js file
chrome.runtime.onMessage.addListener (
function(request) {
item_name = request.name;
item_color = request.color;
item_category = request.category;
item_size = request.size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
});
var url = window.location.href;
var i;
var item_category;
var item_name;
var item_color;
var item_size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
popup.js file
document.getElementById("save_input").onclick = function() {
var item_name_save = document.getElementById("item_name_save").value;
var item_color_save = document.getElementById("item_color_save").value;
var item_category_save = document.getElementById("item_category_save").value;
var item_size_save = document.getElementById("item_size_save").value;
chrome.storage.sync.set({'item_name_save' : item_name_save}, function() {
console.log('Value is set to ' + item_name_save);
});
chrome.storage.sync.set({'item_color_save' : item_color_save}, function() {
console.log('Value is set to ' + item_color_save);
});
chrome.storage.sync.set({'item_category_save' : item_category_save}, function() {
console.log('Value is set to ' + item_category_save);
});
chrome.storage.sync.set({'item_size_save' : item_size_save}, function() {
console.log('Value is set to ' + item_size_save);
});
const msg = { name: item_name_save,
color: item_color_save,
category: item_category_save,
size: item_size_save
};
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg, function() {
});
});
location.reload();
// window.close();
}
manifest.json
{
"manifest_version": 2,
"name": "Supreme bot",
"description": "A bot for Supreme",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Supreme bot"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["bot-functionallity.js"]
}
],
"permissions": [
"activeTab",
"tabs",
"storage",
"tabs"
]
}
(When all code is in the onMessage function, works but not as intended)
The Incorrect Functionality
I basically want the content.js to NOT be all inside of the onMessage function because that doesn't work properly, and have it assign values in the global scope from the onMessage function. I just want that info to be sent 1 time from the popup.js to the content.js.
I just set the value with chrome.storage.sync.set and use chrome.storage.sync.get in the other file...
im trying to share my screen and record it but the problem its that when i choose the source of the stream, it doesnt start streaming, it only shows the window with the screen and window options to share but after selecting one, i checked that i get the ChromeMediaSource and the ChromeMediaSourceID but as i said, the dialog that says sharing wont appear and because of this i cant record anything.
This is an error i found debugging that is not logged into the console:
" at chrome-extension://mnoggiilghljimfhpghalhngdciecafi/background.js:16:31"
which is in "chrome.desktopCapture.chooseDesktopMedia" in the background file
this is my background file
background.js
chrome.runtime.onConnect.addListener( function( port ){
// listen for messages from the port
port.onMessage.addListener( function( message ){
// send back a "pending" answer on the port
port.postMessage( {
"answer": 1,
"state": "pending",
"requestId": message.requestId
} );
chrome.desktopCapture.chooseDesktopMedia(["screen", "window"], port.sender.tab, function( id ){
var response = {
"answer": 1,
"state": "completed",
"requestId": message.requestId,
"streamId": id || undefined
};
// send back a "completed" answer on the port
port.postMessage( response );
} );
} );
} );
this is the file that its loaded to the website using the extension
bridge.js
// open a port to communicate with background
var port = chrome.runtime.connect();
// create node
var node = document.createElement( 'div' );
// listen for messages from webpage and forward them to the background, through the previously opened port
window.addEventListener( 'message', function ( event ){
if ( event.source != window || !event.data ){
return;
}
// prevent to return answer to the background
if( event.data.answer ){
return;
}
port.postMessage( event.data );
} );
// listen for messages from background and forward them to the webpage
port.onMessage.addListener( function( data ){
window.postMessage( data, '*' );
} );
// insert tag into parent page
node.id = 'extension-screensharing-installed';
document.body.appendChild( node );
manifest.js
{
"name": "extension name",
"description": "This extension allows you to share your screen",
"version": "0.1",
"manifest_version": 2,
"minimum_chrome_version": "34",
"icons": {
"16": "img16.png",
"48": "img48.png",
"128": "img128.png"
},
"background": {
"scripts": ["background.js"]
},
"content_scripts": [ {
"js": [ "bridge.js" ],
"matches": [
"https://*/tester"
]
} ],
"permissions": [
"desktopCapture",
"https://*/tester"
]
}
could it be something wrong in my code?
i dont fully understand the error found by debuging
thanks!
edit: added manifest.json file!
Developing a Chrome screen-sharing extension includes instructions and sample code on developing the extension wherein values for apiKey, sessionId and token were entered and OT.registerScreenSharingExtension() method was also used to register the screen-sharing extension.
// API key and generate a test session ID and token:
var apiKey = "<YOUR_API_KEY>";
var sessionId = "<YOUR_SESSION_ID>";
var token = "<YOUR_TOKEN>";
var session = OT.initSession(apiKey, sessionId);
session.connect(token, function(error) {
var publisher = OT.initPublisher('camera');
session.publish(publisher, function() {
screenshare();
});
});
session.on('streamCreated', function(event) {
session.subscribe(event.stream);
});
OT.registerScreenSharingExtension('chrome', '<YOUR_CHROME_EXTENSION_ID>', 2);
And for the recording part, you may check how they did it in Screensharing with WebRTC. Hope it helps!
When i use this following script it works with normal browser. But when iframe is used then its showing me this error:
Does anyone know what is causing this and can be resolved?
ERROR:
channel message Object {type: "getScreenPending", id: 24504, request: 6} content.js:4
channel message Object {type: "gotScreen", id: 24504, request: 6} content.js:4
>>> ShareScreen: if any err NavigatorUserMediaError {constraintName: "", message: "", name: "InvalidStateError"} test.js:1616
manifest.json:
{
"name": "Screen sharing",
"description": "Screensharing utility",
"version": "0.0.2",
"manifest_version": 2,
"minimum_chrome_version": "34",
"icons": {
"48" : "icon.png"
},
"permissions": [
"desktopCapture"
],
"background": {
"scripts": ["background.js"]
},
"content_scripts": [ {
"js": [ "content.js" ],
"all_frames": true,
"run_at": "document_start",
"matches": ["*://*.a.com/*", "*://*.b.com/*"]
}],
"web_accessible_resources": [
"icon.png"
]
}
background.js:
/* background page, responsible for actually choosing media */
chrome.runtime.onConnect.addListener(function (channel) {
channel.onMessage.addListener(function (message) {
switch(message.type) {
case 'getScreen':
var pending = chrome.desktopCapture.chooseDesktopMedia(message.options || ['screen', 'window'],
channel.sender.tab, function (streamid) {
// communicate this string to the app so it can call getUserMedia with it
message.type = 'gotScreen';
message.sourceId = streamid;
channel.postMessage(message);
});
// let the app know that it can cancel the timeout
message.type = 'getScreenPending';
message.request = pending;
channel.postMessage(message);
break;
case 'cancelGetScreen':
chrome.desktopCapture.cancelChooseDesktopMedia(message.request);
message.type = 'canceledGetScreen';
channel.postMessage(message);
break;
}
});
});
content.js:
/* the chrome content script which can listen to the page dom events */
var channel = chrome.runtime.connect();
channel.onMessage.addListener(function (message) {
console.log('channel message', message);
window.postMessage(message, '*');
});
window.addEventListener('message', function (event) {
if (event.source != window)
return;
if (!event.data && (event.data.type == 'getScreen' || event.data.type == 'cancelGetScreen'))
return;
channel.postMessage(event.data);
});
This is caused by the fact that the a stream can only be used by frames whose URL match the origin of the tab. Starting with Chrome 40, you can use the stream in frames as well if you set tab.url to a URL whose origin matches the frame (crbug.com/425344).
The stream is only valid for ten seconds, so you have to follow the following flow:
Load the iframe that contains the page that should handle the stream. This page must be served from a secure scheme, e.g. https: or chrome-extension:.
Send the frame's origin (location.origin) to the background page.
Request the desktop stream using the tab info, with tab.url set to the frame's URL or origin.
Send the streamId back to the frame and use it (within ten seconds).
Example (based on the code in the question):
var tab = channel.sender.tab;
// NEW (Chrome 40+)
tab.url = message.url; // Your custom message, e.g. {url: location.origin}
chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], tab,
function (streamid) {
// ... see question for the rest of the code
});
1 - this is not a code problem, browser problem
2 - this is not working because i am launching the extension from HTTP (http://www.maindomain.com) using iframe a HTTPS (https://subdomain.maindomain.com) link which is using the browser extension
So to fix it. I needed to use HTTPS (https://www.maindomain.com) opening HTTPS iframe links (https://subdomain.maindomain.com) . Since then it works now.
Hope this help others.
NOTE: problem occurred: when i run the iframe from same subdomain subdomain.maindomain.com/test.php (iframe src=subdomain.maindomain.com/core.php) then it works. But when i am running it as maindomain.com/otherpages (iframe src=subdomain.maindomain.com/core.php) then this is not working. Was very confusing.
EDIT: This still did not resolved the problem. screen share dialog box opens but when i press share screen then it gives same error and fails.