Chrome Extensions: Extension not working - Content Script Communicating with Background Script - google-chrome

I am working on a Chrome Extension which changes the Browser Action Icon to "red.png" or "green.png" (which are both saved in the same directory as all other files) when the browser app detects sound is being played from an <audio> html5 tag.
My logical thought process is: User opens page, script starts running on page which constantly (perhaps a setInterval()) checks for if the sound is running. If the sound is running, change the icon to "green.png", else, set it to "red.png".
The way I am checking if sound is being played is this:
if (audio.duration > 0 && !audio.paused)
Where audio is a variable holding the <audio> element.
Here is my code:
manifest.json file:
{
"name": "Creative Sound Dedection",
"description": "Creatively detects sound.",
"version": "1.0",
"permissions": [
"activeTab"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "red.png"
},
"content_scripts" : [
{
"matches" : [ "<all_urls>" ],
"js" : [ "myscript.js" ]
}
],
"manifest_version": 2
}
background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var playing = request.playing;
if(playing == true)
{
chrome.browserAction.setIcon({path:"green.png"});
}else{
chrome.browserAction.setIcon({path:"red.png"});
}
});
myscript.js
function isAudioPlaying(var audio)
{
if (audio !== false && audio.duration > 0 && !audio.paused)
{
return true;
}
return false;
}
function getAudio()
{
if(document.getElementById("myAudio")) return document.getElementById("myAudio");
return false;
}
//if audio is playing: tell background that audio is playing, otherwise, tell it audio is not playing
function sendInfo()
{
chrome.runtime.sendMessage({
"playing": true
});
}
document.addEventListener('DOMContentLoaded', function () {
setInterval(sendInfo, 500);
});
I'm looking for help with why this isn't working and how to correct it, I'm very new to Chrome Extensions development as well as Javascript (although not necessarily new to programming).
Also if you can think of other ways (better) to accomplish the same task please let me know.
Any input would be greatly appreciated!

UPDATE:
Furthermore, the expression playing == true will always evaluate to false, since playing is a string. So:
// Change that:
var playing = request.playing;
if (playing == true) {...
// To this:
var playing = request.playing;
if (playing == "true") {...
setInterval(sendInfo(), 500); means:
"Every 500 milliseconds, execute the value returned by sendInfo()."
Since you want to say:
"Every 500 milliseconds, execute the function named sendInfo."
chagne the above to:
setInterval(sendInfo, 500);
BTW, chrome.extension.sendRequest/onRequest are obsolete and should be replaced with chrome.runtime.sendMessage/onMessage instead.
Besides being obsolete, the former does not properly load an event page (a.k.a. non-persistent background-page) before sending the message, so this might be the cause of your problem.

Related

How to develop chrome extensions that works with chromes pdf viewer? [duplicate]

I'm writing a Chrome Extention to manipulate pdf file so I want to get selected text in the pdf. How can I do that.
Some thing like that:
You can use the internal undocumented commands of the built-in PDF viewer.
Here's an example of a content script:
function getPdfSelectedText() {
return new Promise(resolve => {
window.addEventListener('message', function onMessage(e) {
if (e.origin === 'chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai' &&
e.data && e.data.type === 'getSelectedTextReply') {
window.removeEventListener('message', onMessage);
resolve(e.data.selectedText);
}
});
// runs code in page context to access postMessage of the embedded plugin
const script = document.createElement('script');
if (chrome.runtime.getManifest().manifest_version > 2) {
script.src = chrome.runtime.getURL('query-pdf.js');
} else {
script.textContent = `(${() => {
document.querySelector('embed').postMessage({type: 'getSelectedText'}, '*');
}})()`;
}
document.documentElement.appendChild(script);
script.remove();
});
}
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === 'getPdfSelection') {
getPdfSelectedText().then(sendResponse);
return true;
}
});
This example assumes you send a message from the popup or background script:
chrome.tabs.query({active: true, currentWindow: true}, ([tab]) => {
chrome.tabs.sendMessage(tab.id, 'getPdfSelection', sel => {
// do something
});
});
See also How to open the correct devtools console to see output from an extension script?
ManifestV3 extensions also need this:
manifest.json should expose query-pdf.js
"web_accessible_resources": [{
"resources": ["query-pdf.js"],
"matches": ["<all_urls>"],
"use_dynamic_url": true
}]
query-pdf.js
document.querySelector('embed').postMessage({type: 'getSelectedText'}, '*')
There is no one generic solution for all pdf extensions.
Every extention has is own API.
If you work with google-chrome extension i belive it's impossible.
How to get the selected text from an embedded pdf in a web page?
How extension get the text selected in chrome pdf viewer?

Recording/Catching push notifications with chrome extension

I'm trying to catch push notifications that get sent to my chrome browser with a chrome extension to then write it to a file or run code on receiving it.
I'm sure the issue I'm having is simple enough for some of you so I will share what I've found so far and my code.
notifhook.js
(function() {
// save the original function
var origCreateNotif = notifications.createNotification;
// overwrite createNotification with a new function
notifications.createNotification = function(img, title, body) {
// call the original notification function
var result = origCreateNotif.apply(this, arguments);
alert("Function was called");
// bind a listener for when the notification is displayed
result.addEventListener("display", function() {
alert("Triggered code");
// do something when the notification is displayed
});
return result;
}
})()
manifest.json
{
"name": "Push",
"description": "Relay push notifications",
"version": "3.0",
"permissions": ["notifications"],
"web_accessible_resources": ["notifhook.js"],
"content_scripts" : [{
"run_at" : "document_start",
"matches" : ["<all_urls>"],
"js" : ["inject.js"]
}],
"manifest_version": 2
}
inject.js
var s = document.createElement("script");
s.src = chrome.extension.getURL("notifhook.js");
document.documentElement.appendChild(s);
Most of the code used was taken here :
How can I listen to notifications?
I am using this webapp to test my extension :
http://ttsvetko.github.io/HTML5-Desktop-Notifications/
So, to my knowledge what is happening so far is that the block containing my main function is being added to the webpage just fine
since I can alert at the start of it and on every page load it will be triggered.
What is failing is when I receive a push notification the event listener isn't working or my function names are wrong. I read somewhere that webkitNotifications was replaced with just 'notifications'.
Any help is much appreciated.
If the code is in service worker, like when there are notifications when you close the browser tab/window for the app (like on facebook) then you need to overwirte registration of service worker.
// taken from https://googlechrome.github.io/samples/service-worker/post-message/index.html
function sendMessage(message) {
return new Promise(function(resolve, reject) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
if (event.data.error) {
reject(event.data.error);
} else {
resolve(event.data);
}
};
navigator.serviceWorker.controller.postMessage(message,
[messageChannel.port2]);
});
}
(function() {
if ('serviceWorker' in navigator) {
var register = navigator.serviceWorker.register;
navigator.serviceWorker.register = function() {
register.call(navigator.serviceWorker, "yourscript.js").then(function() {
sendMessage({args: [].slice.call(arguments)});
});
};
}
})();
and in service worker (yourscript.js) you need to call importScript with original script taken from post message
// put content of notifhook.js file here
self.addEventListener('message', function handler(event) {
if (event.data && event.data.args && event.data.args.length) {
importScripts(event.data.args[0]); // import original script
self.removeEventListener('message', handler);
}
});

Show video from chrome.desktopCapture.chooseDesktopMedia in a tab?

I am trying out chrome.desktopCapture.chooseDesktopMedia in a chrome extension and I can get a desktop stream just fine.
I am using the following to turn the stream into an blob:-URL in the background script as follows:
var objectUrl = URL.createObjectURL(stream);
What I can't seem to work out is how to set this as the src attribute of a video element on the injected page.
I have tried the following, each of which do not work:
In Background.js:
var video = document.getElementById("video");
var objectUrl = URL.createObjectURL(stream);
video.src = objectUrl;
In Content.js
//objectUrl is a string received in a message from the background page by the content page
var video = document.getElementById("video");
video.src = objectUrl;
I get the following in the javascript console:
Not allowed to load local resource: blob:chrome-extension://panahgiakgfjeioddhenaabbacfmkclm/48ff3e53-ff6a-4bee-a1dd-1b8844591a91
I also get the same if I post the URL in a message all the way to the injected page. Should this work? I'd really appreciate any advice here.
In my manifest I also have
"web_accessible_resources": [ "*" ] but that was only to see if it resolved this issue (it did not).
In a content script, the DOM is shared with the page, so any DOM operations (such as setting the video src) is subject to the same-origin policy of the page, not the extension.
If you want to show the content of a tab, then you MUST pass a tab.Tab object to chrome.desktopCapture.chooseDesktopMedia. This object can be obtained in many ways, including the message passing and tabs APIs. Here is an example using the extension button:
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
// NOTE: If you want to use the media stream in an iframe on an origin
// different from the top-level frame (e.g. http://example.com), set
// tab.url = 'http://example.com'; before calling chooseDesktopMedia!
// (setting tab.url only works in Chrome 40+)
chrome.desktopCapture.chooseDesktopMedia([
'screen', 'window'//, 'tab'
], tab, function(streamId) {
if (chrome.runtime.lastError) {
alert('Failed to get desktop media: ' +
chrome.runtime.lastError.message);
return;
}
// I am using inline code just to have a self-contained example.
// You can put the following code in a separate file and pass
// the stream ID to the extension via message passing if wanted.
var code = '(' + function(streamId) {
navigator.webkitGetUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
chromeMediaSourceId: streamId
}
}
}, function onSuccess(stream) {
var url = URL.createObjectURL(stream);
var vid = document.createElement('video');
vid.src = url;
document.body.appendChild(vid);
}, function onError() {
alert('Failed to get user media.');
});
} + ')(' + JSON.stringify(streamId) + ')';
chrome.tabs.executeScript(tab.id, {
code: code
}, function() {
if (chrome.runtime.lastError) {
alert('Failed to execute script: ' +
chrome.runtime.lastError.message);
}
});
});
});
manifest.json
{
"name": "desktopCapture.chooseDesktopMedia for a tab",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_title": "Show desktop capture request"
},
"permissions": [
"desktopCapture",
"activeTab"
]
}
ObjectURLs can't be shared cross-origin. A Data URL can be shared cross-origin if it works with your video stream (I'm not sure).

Chrome App web view using Fullscreen & Alwaysontop

I'm working on a Chrome app that opens up using the web view and loads our webpage. I'm trying to make a app that will lock down the web view so when someone is taking a test they can't get out of it or use something else to try and cheat. So the issue I'm having right now is I can't get the Full screen and AlwaysOnTop to work with each other. Is there a way to do this or will these two things not work together and if not is there a hack to get them to work or maybe another direction I need to take. I'm currently running my app with the Chrome Beta since alwaysOnTop doesn't work with the stable version yet. Here is the code in my
main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('browser.html', {
state: "fullscreen",
"resizable": false,
'alwaysOnTop': true
},
function(win) {
win.contentWindow.document.addEventListener('keydown', function(e) {
if(e.keyCode == 27){
e.preventDefault();
}
});
win.contentWindow.document.addEventListener('keyup', function(e) {
if(e.keyCode == 27){
e.preventDefault();
}
});
});
});
Any help or some type of direction would be great. I've only been able to get the alwaysontop to work when setting bounds instead but this will display the minimize maximize and exit which kind of defeats the purpose of what I'm trying to do. Thanks in advanced.
you might want to have a look at the "overrideEscFullscreen" permission.
See https://code.google.com/p/chromium/codesearch#chromium/src/chrome/test/data/extensions/platform_apps/prevent_leave_fullscreen_old/manifest.json&q=overrideEscFullscreen&sq=package:chromium&type=cs
manifest.json
{
"name": "Test app for leaving fullscreen rules",
"version": "1",
"app": {
"background": {
"scripts": ["main.js"]
}
},
"permissions": [
"fullscreen", "overrideEscFullscreen"
]
}
main.js
chrome.app.runtime.onLaunched.addListener(function() {
chrome.app.window.create('main.html', {}, function(win) {
// The following key events handler will prevent the default behavior for
// the ESC key, thus will prevent the ESC key to leave fullscreen.
win.contentWindow.document.addEventListener('keydown', function(e) {
e.preventDefault();
});
win.contentWindow.document.addEventListener('keyup', function(e) {
e.preventDefault();
});
});
});

Chrome Extension: how to capture selected text and send to a web service

For the Google Chrome extension, I need to capture selected text in a web page and send to a web service. I'm stuck!
First I tried a bookmarklet, but Chrome on Mac seems to have some bookmarklet bugs so I decided to write an extension.
I use this code in my ext:
function getSelText(){
var txt = 'nothing';
if (window.getSelection){
txt = "1" + window.getSelection();
} else if (document.getSelection) {
txt = "2" + document.getSelection();
} else if (document.selection) {
txt = "3" + document.selection.createRange().text;
} else txt = "wtf";
return txt;
}
var selection = getSelText();
alert("selection = " + selection);
When I click on my extension icon, I get a "1". So I think the act of selecting outside the browser window is causing the text to not be seen by the browser as "selected" any more.
Just a theory....
thoughts?
You can do this by using Extensions Messaging. Basically, your "background page" will send the request to your service. For example, lets say you have a "popup" and once you click on it, it will do a "Google search" which is your service.
content_script.js
In your content script, we need to listen for a request coming from your extension, so that we send it the selected text:
chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
if (request.method == "getSelection")
sendResponse({data: window.getSelection().toString()});
else
sendResponse({}); // snub them.
});
background.html
Now in background page you can handle the popup onclick event so that we know we clicked on the popup. Once we clicked on it, the callback fires, and then we can send a request to the content script using "Messaging" to fetch the selected text.
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.sendRequest(tab.id, {method: "getSelection"}, function(response){
sendServiceRequest(response.data);
});
});
function sendServiceRequest(selectedText) {
var serviceCall = 'http://www.google.com/search?q=' + selectedText;
chrome.tabs.create({url: serviceCall});
}
As you have seen, I registered a listener in a content script to allow my extension to send and receive messages from it. Then once I received a message, I handle it by searching for Google.
Hopefully, you can use what I explained above and apply it to your scenario. I just have to warn you that the code written above is not tested, so their might be spelling, or syntax errors. But those can easily be found by looking at your Inspector :)
content script
document.addEventListener('mouseup',function(event)
{
var sel = window.getSelection().toString();
if(sel.length)
chrome.extension.sendRequest({'message':'setText','data': sel},function(response){})
})
Background Page
<script>
var seltext = null;
chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
{
switch(request.message)
{
case 'setText':
window.seltext = request.data
break;
default:
sendResponse({data: 'Invalid arguments'});
break;
}
});
function savetext(info,tab)
{
var jax = new XMLHttpRequest();
jax.open("POST","http://localhost/text/");
jax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
jax.send("text="+seltext);
jax.onreadystatechange = function() { if(jax.readyState==4) { alert(jax.responseText); }}
}
var contexts = ["selection"];
for (var i = 0; i < contexts.length; i++)
{
var context = contexts[i];
chrome.contextMenus.create({"title": "Send to Server", "contexts":[context], "onclick": savetext});
}
</script>
manifest.json
{
"name": "Word Reminder",
"version": "1.0",
"description": "Word Reminder.",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background_page": "background.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/myscript.js"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
]
}
and here is the link where i have all in one extension to download.
after reading this i tried of my own and have published.
and here is the complete source
http://vikku.info/programming/chrome-extension/get-selected-text-send-to-web-server-in-chrome-extension-communicate-between-content-script-and-background-page.htm
Enjoy
Using a content_scripts is not a great solution as it injection to all documents including iframe-ads etc. I get an empty text selection from other pages than the one I expect half the times on messy web sites.
A better solution is to inject code into the selected tab only, as this is where your selected text lives anyhow. Example of jquery doc ready section:
$(document).ready(function() {
// set up an event listener that triggers when chrome.extension.sendRequest is fired.
chrome.extension.onRequest.addListener(
function(request, sender, sendResponse) {
// text selection is stored in request.selection
$('#text').val( request.selection );
});
// inject javascript into DOM of selected window and tab.
// injected code send a message (with selected text) back to the plugin using chrome.extension.sendRequest
chrome.tabs.executeScript(null, {code: "chrome.extension.sendRequest({selection: window.getSelection().toString() });"});
});
It is not clear from your code where it is. What I mean, is that if this code is either in popup html or background html then the results you are seeing are correct, nothing in those windows will be selected.
You will need to place this code in a content script so that it has access to the DOM of the page, and then when you click your browser action, you will need to send a message to the content script to fetch the current document selection.
You don't need a Google API for something as simple as this...
I'll use the Bing online service as an example. Note that the URL is set up to accept a parameter:
var WebService='http://www.bing.com/translator/?text=';
frameID.contentWindow.document.body.addEventListener('contextmenu',function(e){
T=frameID.contentWindow.getSelection().toString();
if(T!==''){e.preventDefault(); Open_New_Tab(WebService+encodeURIComponent(T)); return false;}
},false);
NB: The function "Open_New_Tab()" used above is an imaginary one that accepts the webservice URL with the encoded selected text as a parameter.
That's the idea basically.