Chrome Extension: accessing mic by Speech Recognition call - javascript

Before you read this, it may be related to
How can a Chrome extension get a user's permission to use user's computer's microphone?
I've added an answer below, including code and my manifest, if that helps.
I am writing a minimal Chrome Extension (using Chrome 75.0.3770.90 on MacOS 10.14.5) to implement a 'listen' button for my accessibility project. I've written an HTML version with the JavaScript which works the microphone.
However, when I lift that code into the Extension background.js file, the text-to-speech works, but not the speech-to-text. The code runs, but the flashing mic never appears in the tab.
The code which works is:
<!DOCTYPE html>
<html>
<body>
<h2>All-in-one JavaScript Example</h2>
<button onclick="myCode();">Listen</button>
<script>
window.SpeechRecognition = window.webkitSpeechRecognition
|| window.SpeechRecognition;
function myCode() {
recognition = new SpeechRecognition();
recognition.start();
recognition.onresult = function(event) {
if (event.results[0].isFinal) {
response = event.results[0][0].transcript;
synth = window.speechSynthesis;
synth.speak( new SpeechSynthesisUtterance(
"i don't understand, "+response
));
} }
alert( "all-in-one: we're done!" );
}
</script>
</body>
</html>
Minimal reproducible example:
{
"name": "myName",
"description": "Press to talk",
"version": "0.97",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": false
},
"permissions": ["contentSettings","desktopCapture","*://*/*","tabCapture","tabs","tts","ttsEngine"],
"browser_action": {
"default_icon": "images/myIcon512.png",
"default_title": "Press Ctrl(Win)/Command(Mac)+Shift+ Down to speak"
},
"commands": {
"myCommand": {
"suggested_key": {
"default": "Ctrl+Shift+Down",
"mac": "Command+Shift+Down"
},
"description": "Start speaking"
}
},
"icons": {
"512": "images/myIcon512.png"
}
}
My background JavaScript is:
window.SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition;
function myCode() {
var recognition = new SpeechRecognition();
recognition.onresult = function(event) {
if (event.results[0].isFinal) {
var synth = window.speechSynthesis;
synth.speak( new SpeechSynthesisUtterance(
"sorry, I don't understand."
)
);
}
}
recognition.start();
alert( "extension: we're done!" );
}
chrome.commands.onCommand.addListener(function(command) {
if (command === 'myCommand')
myCode();
});
I've also noticed that the code only runs once - I can keep on clicking in the listen button, but the Extension command only runs once (putting in an alert at the beginning of the function only gets displayed the first time around)
The default on my browser is that it should ask (once) which it does on the HTML version.
Thanks, just for reading this far! I've put an answer, with code, below.

The problem I had is that the mic seems to be a background task, whereas I'm trying to interact with tab contents. I don't think this is usual, and have ended up with a generic 'matches' value (*://*/*) in my manifest (in full):
{ "name": "Enguage(TM) - Let's all talk to the Web",
"short_name" : "Enguage",
"description": "A vocal Web interface",
"version": "0.98",
"manifest_version": 2,
"content_security_policy": "script-src 'self'; object-src 'self'",
"background": {
"scripts": ["kbTx.js"],
"persistent": false
},
"content_scripts": [
{ "matches" : ["*://*/*"],
"js": ["tabRx.js", "interp.js"]
} ],
"permissions": [
"activeTab",
"contentSettings",
"desktopCapture",
"tabCapture",
"tabs",
"tts"
],
"browser_action": {
"default_icon": "images/lbolt512.png",
"default_title": "Press Ctrl(Win)/Command(Mac)+Shift+ Space and speak"
},
"commands": {
"enguage": {
"suggested_key": {
"default": "Ctrl+Shift+Space",
"mac": "Command+Shift+Space"
},
"description": "single utterance"
} },
"icons": {
"16": "images/lbolt16.png",
"48": "images/lbolt48.png",
"128": "images/lbolt128.png",
"512": "images/lbolt512.png"
} }
I think Google might not like this! Anyway, I have put in a keyboard listener in my background code (kbTx.js):
chrome.commands.onCommand.addListener(function(command) {
if (command === 'enguage') {
// find the active tab...
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
//send it a message...
chrome.tabs.sendMessage(
tabs[0].id, // index not always 0?
null, // message sent - none required?
null // response callback - none expected!
//function(response) {console.log("done" /*response.farewell*/);}
);
});
}
});
And I've put in a context script which listens for this message (tabRx.js):
window.SpeechRecognition = window.webkitSpeechRecognition ||
window.SpeechRecognition;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var recognition = new SpeechRecognition();
recognition.start();
recognition.continuous = false;
recognition.onresult = function(event) {
if (event.results[0].isFinal) {
window.speechSynthesis.speak(
new SpeechSynthesisUtterance(
interp( event.results[0][0].transcript )
) );
} } }
);
The message listener essentially contains the code in the allInOne.html example above.
There may be other ways of doing this, but this works and seems reasonably lightweight.
Hope this helps.
Please feel free to add comments to this, if you think I can improve my code!

Related

Chrome extension not detecting input field

I'm creating my first Chrome extension and the code just seems to not be working. Basically for now, I want the extension to be able to identify a click on an input field and give an alert. I think the extension is not recognizing the click. I've tried multiple things, but here's the most recent code:
manifest.json
{
"name": "Pi",
"version": "1.0",
"description": "Pi works!",
"permissions": ["activeTab", "declarativeContent", "storage"],
"browser_action": {
"default_icon": {
"32": "pie.png"
}
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["contentScript.js"]
}],
"manifest_version": 2
}
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
alert('HELLO WORLD!!');
});
contentScript.js
document.addEventListener('DOMContentLoaded', function(){
document.getElementsByTagName('input').addEventListener("click", function (){
alert("Hi");
});
});
Remove document.addEventListener('DOMContentLoaded', function(){ You actually don't need it. The chrome Browser will decide when to run content script based on run_at settings in the manifest.json default is document_idle. See more here.
GodsArchitect's example is correct for detecting the click event of INPUT tags. However, you may need to consider onFocus event on INPUT tags in case if users use keyboard TAB key to focus into the input field.
const inputTags = Array.from(document.querySelectorAll('input'))
inputTags.forEach(function (input) {
input.addEventListener('focus', function (event) {
alert("Hi...")
}, false)
})
The following does the job.
document.addEventListener('click', function(e) {
e = e || window.event;
var target = e.target || e.srcElement;
if(target.tagName == 'INPUT'){
alert('You clicked an Input!');
}
}, false);
Source: https://stackoverflow.com/a/9012576/10383955

Running chrome extension process while popup is closed

Basically I am trying to create an Auto Visitor Extension for Chrome to open a website's URL after some specific time. When I open the popup everything works fine but when the popup is close nothing works. I am trying to find out a method to run that Auto Visitor Extension even when the popup is close I have read multiple questions regarding this phenomena on Stack Overflow but none of them clarifies what I am looking for.
Here is my manifest file:
manifest.json
{
"manifest_version": 2,
"name": "Auto Visitor",
"description": "This extension will visit multiple pages you saved in extension",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"scripts": [
"background.js"
],
"persistent": false
},
"permissions": [
"activeTab",
"storage",
"tabs",
"http://*/",
"https://*/"
]
}
The background file that i want to run even when popup is close :
background.js
// this will work only when you denie the background script in manifest
chrome.runtime.onInstalled.addListener(function(details) {
var initTime = 5;
chrome.storage.local.set({"avtime": initTime}, function() {});
});
reloadMainTab();
function reloadMainTab() {
chrome.storage.local.get('avurl', function (result) {
var urlsToLoad = result.avurl;
console.log(urlsToLoad);
if(urlsToLoad==undefined){
// do nothing
}else{
var urlsArr = urlsToLoad.split(",");
chrome.storage.local.get('avtime', function (result) {
var thisTime = result.avtime;
/*
setting it to -1 because it gets incremented by 1
when it goes into getSelected method
*/
var index=-1;
setInterval(function(){
if(index < urlsArr.length-1){
chrome.tabs.getSelected(function (tab) {
// console.log('index in get selected'+index)
chrome.tabs.update(tab.id,{url: urlsArr[index]});
});
index++;
}else{
index=-1;
}
}, thisTime+"000");
});
}
});
}
any help would be really appreciated

Running a function in chrome extension on double click/select of a word

I am trying to fire a notification whenever I double click on a word/select it. Found several examples online but I still cannot get my example working:
Manifest:
{
"name": "hh",
"description": "hh!",
"manifest_version": 2,
"version": "0.0.0.1",
"content_scripts": [
{
"matches": [ "http://*/*", "https://*/*" ],
"js": [ "background.js" ],
"all_frames": true,
"run_at": "document_end"
}
],
"permissions": ["storage", "notifications"],
"icons": { "128": "neoprice.png" }
}
background.js
var listener = function(evt) {
var selection = window.getSelection();
if (selection.rangeCount > 0) {
displayPrice();
var range = selection.getRangeAt(0);
var text = range.cloneContents().textContent;
console.log(text);
}
};
document.addEventListener('dblclick', listener);
function displayPrice(){
chrome.notifications.create(getNotificationId(), {
title: "message.data.name",
iconUrl: 'hh.png',
type: 'basic',
message: "message.data.prompt"
}, function() {});
}
// Returns a new notification ID used in the notification.
function getNotificationId() {
var id = Math.floor(Math.random() * 9007199254740992) + 1;
return id.toString();
}
I was earlier adding the following but I saw people weren't using it, so I removed it
"app": {
"background": {
"scripts": ["background.js", "assets/jquery.min.js"]
}
},
What I am trying to achieve: Whenever they go to ANY page on selecting a word, it fires the function. Later, I wish to use this for a specific page. :)
Tried: How to keep the eventlistener real time for chrome extensions?
Chrome extension double click on a word
https://github.com/max99x/inline-search-chrome-ext
Both don't really work as I want them too. :(
Solution
It seems you are confused with background page and content script. Your background.js is a content script in fact, though its name is "background". While chrome.notifications api can be only called in background page, trying commenting displayPrice function will make your code work.
Next step
Take a look at above tutorials, wdblclick event triggers, use Message Passing to communicate with background page and call chrome.notications api in background page.
What's more
The following code is used in chrome apps rather than chrome extension.
"app": {
"background": {
"scripts": ["background.js", "assets/jquery.min.js"]
}
},

chrome.windows.onFocusChanged.addListener fired when closing Chrome, crashing plugin

I am writing a chrome extension, it uses chrome.windows.onFocusChanged.addListener to execute a content script. This works fine as long as I switch between different chrome windows however, the Listener is also fired when Chrome is closed. In that case the code inside the function leads to a crash of the extension.
I tried chacking for undefined, but that does not seem to be the issue.
The code lookls liks this
chrome.windows.onFocusChanged.addListener(function()
{
var ctab = chrome.tabs.getCurrent;
if(typeof ctab === "undefined")
{
}
else
{
chrome.tabs.executeScript(ctab.id,{file:"inject.js"}); //THIS LINE CRASHES THE APP WHEN I CLOSE THE BROWSER
}
});
I have a similar problem with a scheduled allert, which also executes the script (which leads to a crash when I close the browser between scheduling and execution).
function onAlarm(alarm)
{
if (alarm && alarm.name == 'check')
{
var ctab = chrome.tabs.getCurrent;
chrome.tabs.executeScript(ctab.id,{file:"inject.js"}); //CRASH
//more code...
Does anybody know how to avoid this crash?
Edit: Working Example
manifest.json
{
"name": "Rest Extension",
"description": "Set an Artnet Controler to the background collor",
"manifest_version": 2,
"version": "1",
"permissions": ["tabs", "http://*/*", "https://*/*", "background", "alarms"],
"background": {"scripts": ["background.js"]},
"content_scripts": [{"matches": ["http://*/*", "https://*/*"],"js": ["inject.js"]}],
"browser_action":
{
"default_icon": "16x16.png",
"default_popup": "popup.html"
}
}
background.js
chrome.windows.onFocusChanged.addListener(function()
{
var ctab = chrome.tabs.getCurrent;
if(typeof ctab === "undefined")
{
}
else
{
chrome.tabs.executeScript(ctab.id,{file:"inject.js"}); //THIS LINE CRASHES THE APP WHEN I CLOSE THE BROWSER
}
})
all the other files can be empty (including the inject.js)
chrome.tabs.getCurrent is a method: it's not an instance of a Tab object.
You should try this:
chrome.windows.onFocusChanged.addListener(function()
{
chrome.tabs.getCurrent(function(ctab)
{
chrome.tabs.executeScript(ctab.id, { file: "inject.js" });
});
});
Reference: http://developer.chrome.com/extensions/tabs.html#method-getCurrent

Trying to get my chrome extension to click a element on page

I am trying to create a chrome extension that can download mp3s from hiphopdx. I have found that once you click the play button on the website it might be possible to extract the download link of the mp3. However I am stuck on getting my extension to click on the play button.
Here is an example of the page on which I am using the extension on:
http://www.hiphopdx.com/index/singles/id.16603/title.fred-the-godson-f-the-kid-daytona-back-to-school-prod-kaimbr
my manifest json
"name": "My Test",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["popup.js"]
},
"browser_action": {
"default_icon": "icon.png"
},
"permissions": ["tabs", "http://*/*", "https://*/*"]
my popup.html
<!doctype html>
<html>
<head>
<script src="popup.js"></script>
</head>
<body>
</body>
</html>
popup.js
function ShowOperationMessage(obj, evt) {
var fireOnThis = obj;
if (document.createEvent) {
var evObj = document.createEvent('MouseEvents');
evObj.initEvent(evt, true, false);
fireOnThis.dispatchEvent(evObj);
} else if (document.createEventObject) {
fireOnThis.fireEvent('on' + evt);
}
}
ShowOperationMessage(document.getElementsByClassName("playBtns medium awesome red adjust launchplayer left"),"click");
Modifying your code to content scripts code, i was able to click the button
References
Content Scripts
Background Pages
Browser Action
Added content script section to eliminate background stuff
manifest.json
{
"name": "Mouse Clicks",
"version": "0.0.1",
"manifest_version": 2,
"description": "This demonstrates how mouse clicks are tracked",
"content_scripts": [
{
"matches": ["http://www.hiphopdx.com/index/singles/id.16603/title.fred-the-godson-f-the-kid-daytona-back-to-school-prod-kaimbr"],
"js": ["myscript.js"]
}
]
}
myscript.js
document.getElementsByClassName("playBtns medium awesome red adjust launchplayer left") returns an array so used obj[0] index
function ShowOperationMessage(obj, evt) {
var fireOnThis = obj[0];
if (document.createEvent) {
var evObj = document.createEvent('MouseEvents');
evObj.initEvent(evt, true, false);
fireOnThis.dispatchEvent(evObj);
} else if (document.createEventObject) {
fireOnThis.fireEvent('on' + evt);
}
}
ShowOperationMessage(document.getElementsByClassName("playBtns medium awesome red adjust launchplayer left"), "click");
Let me know if you need more information.

Categories

Resources