Keydown Which Not Working Chrome Extension - javascript

I've been struggling with my idea of google extension, and you as always are the last hope of mine ! :))
Well, I want to click the button on my chrome extension that will cause keydown simulation on the page extension is running.
I think chrome has some safety issues on my idea, that blocks the keyboard simulation (makes event isTrusted:false) and deletes which property.
The function I wrote works fine on jsfiddle , but it appears that chrome extension does it in a different manner.
Here is the content script file :
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if(request.action == "scrollToTop"){
}
else if(request.action == "scrollToBottom"){
}
else if(request.action == "enter"){
triggerKeyboardEvent(document,13);
}
function triggerKeyboardEvent(el, keyCode){
var event = new Event("keydown", {"bubbles":true, "cancelable":true});
event.which = keyCode;
el.dispatchEvent(event);
}
});
chrome.runtime.sendMessage({action : "show"});
The log on jsFiddle writes:
Event {isTrusted: false, which: 13}
The log on website:
document.addEventListener('keydown',function (e) {
console.log(e)
}
writes just:
Event {isTrusted: false}

Thanks to #BG101 and #Rob W I found out that solution is script injection!
the only thing was that according to MDN KeyboardEvent.initKeyboardEvent() is depricated, so I replaced the code with:
var event = new Event(event, {"bubbles":true, "cancelable":true});
Also, as I wanted the trigger to run on document, I removed the element selector things. Here is what I got:
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if(request.action == "scrollToTop"){
triggerKeyboardEventOnDocument("keydown",38);
}
else if(request.action == "scrollToBottom"){
triggerKeyboardEventOnDocument("keydown",40);
}
else if(request.action == "enter"){
triggerKeyboardEventOnDocument("keydown",13);
}
function triggerKeyboardEventOnDocument(event, keyCode){
var script = document.createElement('script');
script.textContent = '(' + function(event, charCode) {
//Create Event
var event = new Event(event, {"bubbles":true, "cancelable":true});
// Define custom values
// This part requires the script to be run in the page's context
var getterCode = {get: function() {return charCode}};
var getterChar = {get: function() {return String.fromCharCode(charCode)}};
Object.defineProperties(event, {
charCode: getterCode,
which: getterCode,
keyCode: getterCode, // Not fully correct
key: getterChar, // Not fully correct
char: getterChar
});
document.dispatchEvent(event);
} + ')(' + '\"' + event + '\", '+ keyCode + ')';
(document.head||document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
}
});
chrome.runtime.sendMessage({action : "show"});

Related

Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response

I am finding the 'Uncaught (in promise) Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received' error in the background.js. My chrome extension works but the dev tools displays a lot of these errors on background.js
I recently migrated my chrome extension from Manifest V2 to Manifest V3 and since then I am finding the error.
This is the place from where the error seems to be arising :
const browser = chrome || browser;
// Listen for messages from content script
browser.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
var frameId = sender.frameId || 0;
if (request.type == "connect") {
connect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "disconnect") {
disconnect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "send") {
sendNativeMessage(request.data, sender.tab);
document.addEventListener('nativeMessage',
function handleNativeMessage(e) {
sendResponse(e.detail);
document.removeEventListener(e.type, handleNativeMessage);
}, false);
sendResponse({});
return true;
}
});
The sendresponse is processed in ContentScript :
const browser = chrome || browser;
self.addEventListener("message", function(event){
var request = event.data;
if(request != null && request.type == "SendMessage")
{
ProcessNativeMessage(request.data);
}
});
function ProcessNativeMessage(nativeMessageData) {
var request = new Object();
request.data = nativeMessageData;
browser.runtime.sendMessage(request,handleExtensionResponse);
}
function handleExtensionResponse(value)
{
//alert(value);
};
I played around by returning true, returning false, returning undefined, sending empty response and even disabling the other extensions but none of these changes worked.
I even tried changing the code with async/await as suggested here but it makes no difference.
Does anything change if you rewrite the code like this?
Anyway I can't reproduce an extension with native messaging, sorry.
P.S. Before any scientist decides to downvote me badly, I want to ensure that if the solution doesn't work I'm willing to cancel the answer.
const browser = chrome || browser;
var globSendResp, globReqTypeSend; //(*)
// Listen for messages from content script
browser.runtime.onMessage.addListener(function(request, sender, sendResponse) {
var frameId = sender.frameId || 0;
globReqTypeSend = false; //(*)
if (request.type == "connect") {
connect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "disconnect") {
disconnect(sender.tab, frameId);
sendResponse({ success: true });
} else if (request.type == "send") {
globReqTypeSend = true; //(*)
globSendResp = sendResponse; //(*)
sendNativeMessage(request.data, sender.tab);
//sendResponse({}); //(*)
return true;
}
});
document.addEventListener('nativeMessage', function handleNativeMessage(e) {
if (globReqTypeSend)
globSendResp(e.detail)
}, false);

Content script unloads when page is not visited for a long time

I'm working on a browser extension that needs to be constantly running, even after an automatic refresh in the background. The problem is, that the page randomly automatically unloads and the script just shuts off. I need to find a way to keep the content script on at all times. Here's part of the code:
// content.js:
function run(fn) {
if(typeof(Worker) !== "undefined") {
if(typeof(w) == "undefined") {
w = new Worker(URL.createObjectURL(new Blob(['('+fn+')()'])));
}
w.onmessage = function(event) {
if (isNaN(grabbedmin) && ID) {
bump() // Note: the bump function refreshes in the page.
w.terminate();
}
if ($("[href='/server/bump/" + ID + "']").text().includes("Bump")) {
bump()
w.terminate();
}
document.getElementById("bumpcount").innerHTML = "Autobumper Enabled: " + getCurrentTimestamp();
if (numberwow == grabbedmin) {
bump()
w.terminate();
}
};
}
}
The code above basically gets run every minute by this worker:
// content.js:
const worker = run(function() {
var i = 0;
function timedCount() {
i = i + 1;
postMessage(i);
setTimeout(function(){timedCount()},1000);
}
timedCount();
});
Is there a way for background.js to detect that content.js is not running (or that the page is unloaded) when it should be and then reload it?
Note: You can find the script here: https://github.com/Theblockbuster1/disboard-auto-bump
After looking through the docs and looking at examples, I put this together:
chrome.tabs.query({
url: ["*://disboard.org/*dashboard/servers", "*://disboard.org/*dashboard/servers/"] // finds matching tabs
}, function(tabs) {
tabs.forEach(tab => {
chrome.tabs.update(tab.id,{autoDiscardable:false});
});
});
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
// checks if the browser automatically re-enabled autoDiscarding and disables it
if (changeInfo.autoDiscardable == true) chrome.tabs.update(tabId,{autoDiscardable:false});
});

Highlight current DOM element in Chrome Extension using DebuggerApi

I'm currently building an extension for chrome (I'm a beginner) and looking for some help to some one issue. The flow of the extension is the following:
User activate the extension
User click an icon in the extension panel to start the capture
When the mouse cursor is over a DOM element it highlight it
When the user click it gets the "selector" (unique identifier/path to the element)
After step 2 I attach a new Debugger instance to the tab. it seems like you can do this action either in background.js or content-script.js. Both work so my question is which one makes more sense. I'd say content-script because it doesn't interact directly with the browser but only with my extension. Am I right?
Second question is when using the DebuggerAPI I need to send command using the DevTools Protocol Viewer. I guess the command I must send to interact with my DOM element sit under this category (https://chromedevtools.github.io/devtools-protocol/tot/DOM). Most of the command requires a NodeId parameter. My question is how would I get this NodeId when the mouse cursor is over it. I have the following event in my content-script
chrome.runtime.onMessage.addListener(function(msg, sender){
if(msg == "togglePanel"){
togglePanel();
} else if (msg == "startCaptureElement") {
console.log("- Content-Script.js: Add Mouse Listener");
document.addEventListener('mouseover', captureEvent);
} else if (msg == "stopCaptureElement") {
console.log("- Content-Script.js: Remove Mouse Listener");
document.removeEventListener('mouseover', captureEvent);
}
});
function captureEvent(el) {
//console.log("- Content-Script.js: It's moving");
console.log(el);
chrome.runtime.sendMessage("highlightElement");
}
In my background.js script
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender);
if (request == "startCaptureElement") {
console.log("- Background.js: Attach the debugger");
chrome.debugger.attach({tabId: sender.tab.id}, "1.0");
chrome.tabs.sendMessage(sender.tab.id, "startCaptureElement");
} else if (request == "stopCaptureElement") {
console.log("- Background.js: Detach the debugger");
chrome.debugger.detach({tabId: sender.tab.id});
chrome.tabs.sendMessage(sender.tab.id, "stopCaptureElement");
} else if (request == "highlightElement") {
console.log("- Background.js: Highlight Element");
chrome.debugger.sendCommand({tabId: sender.tab.id}, "DOM.enable", {});
chrome.debugger.sendCommand({tabId: sender.tab.id}, "Overlay.inspectNodeRequested", {}, function(result) {
console.log(result);
});
}
}
);
I found the similar question here How to highlight elements in a Chrome Extension similar to how DevTools does it? but the code provided confused me a little bit.
Thanks for your help
"Overlay.inspectNodeRequested" is an event that should be listened to.
you can call "Overlay.setInspectMode" to select a node.
background.js:
var version = "1.0";
//show popup page while click icon
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.debugger.attach({tabId:tab.id}, version,
onAttach.bind(null, tab.id));
});
function onAttach(tabId) {
if (chrome.runtime.lastError) {
alert(chrome.runtime.lastError.message);
return;
}
chrome.windows.create(
{url: "headers.html?" + tabId, type: "popup", width: 800, height: 600});
}
headers.html:
<html>
<head>
<meta charset="utf-8">
<script src="headers.js"></script>
</head>
<body>
<div id="container">
<button id="btn_inspect">select node</button>
</div>
</body>
</html>
headers.js:
var tabId = parseInt(window.location.search.substring(1));
var hightCfg = {
'showInfo': true, 'showStyles':true, 'contentColor':{r: 155, g: 11, b: 239, a: 0.7}
}
//listen events when page is loaded
window.addEventListener("load", function() {
chrome.debugger.sendCommand({tabId:tabId}, "DOM.enable");
chrome.debugger.sendCommand({tabId:tabId}, "Overlay.enable");
chrome.debugger.onEvent.addListener(onEvent);
document.getElementById('btn_inspect').addEventListener('click', function(){
chrome.debugger.sendCommand({tabId:tabId}, "Overlay.setInspectMode",
{'mode':'searchForNode', 'highlightConfig':hightCfg});
});
});
window.addEventListener("unload", function() {
chrome.debugger.detach({tabId:tabId});
});
var requests = {};
function onEvent(debuggeeId, message, params) {
console.log('onEvent ...'+message);
if (tabId != debuggeeId.tabId)
return;
if (message == "Network.inspectNodeRequested") {
//do something..
}
}

Chrome: message content-script on runtime.onInstalled

I'm the maker of an addon called BeautifyTumblr which changes the apperance of Tumblr.
I wish for my Chrome extension to automatically detect when it has been updated and display changelog to the user. I use an event page with the chrome.runtime.onInstalled.addListener hook to detect when an update has occured, retrieve the changelog from a text file in the extension.. this all works fine, then when I want to forward it to my content script via chrome.tabs.sendmessage it just wont work, nothing ever happens, no errors no nothing. I'm stumped.
Any help would be much appreciated!
Event Page:
chrome.runtime.onInstalled.addListener(function (details) {
"use strict";
if (details.reason === "install") {
} else if (details.reason === "update") {
var thisVersion = chrome.runtime.getManifest().version, xmlDom, xmlhttp;
xmlDom = null;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", chrome.extension.getURL("changelog.txt"), false);
xmlhttp.send(null);
xmlDom = xmlhttp.responseText;
chrome.tabs.query({'url' : 'http://www.tumblr.com/*'}, function (tabs) {
if (tabs.length > 0) {
var mTab = tabs[0].id;
chrome.tabs.update(mTab, {active: true});
setTimeout(chrome.tabs.sendMessage(mTab, {beautifyTumblrUpdate: xmlDom}), 500);
} else {
chrome.tabs.create({'url' : 'http://www.tumblr.com/dashboard'}, function (tab) {
setTimeout(chrome.tabs.sendMessage(tab.id, {beautifyTumblrUpdate: xmlDom}), 500);
});
}
});
}
});
Relevant code in Content Script:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
"use strict";
window.alert('test');
if (request.beautifyTumblrUpdate) {
window.alert(request.beautifyTumblrUpdate);
} else if (request.beautifyTumblrInstall) {
window.alert(request.beautifyTumblrInstall);
}
}
);
I am also seeing the same thing. I am not a 100% sure but I think this happens because chrome shuts off connection between background page and "old" content scripts the moment the extension is updated. There's more info here in this bug : https://code.google.com/p/chromium/issues/detail?id=168263
simple, use the following code in background,
chrome.runtime.onInstalled.addListener(function(details){
if(details.reason == "install"){
chrome.tabs.create({ url: chrome.extension.getURL('welcome.html')});
}
});

How to trigger a custom javascript event in IE8?

I'm trying to fire a custom event on IE8 and fiddling a solution together from here and here. But I cannot get it to work...
I'm using jquery mobile with requireJS and google analytics. So I'm tracking the JQM pageshow event. However since requireJS loads scripts async, my binding to pageshow needs to be made in a javascript "wrapper", otherwise it will produce an error, because neither jquery nor jquery mobile will have been loaded by the time the snippet is parsed.
So I'm doing including this at the end of every page:
if (document.addEventListener) {
document.addEventListener("jqmReady",function(){trigAnalytics("jqmReady");alert("FF detected")},false);
} else if ( document.attachEvent ) {
document.attachEvent("jqmReady", function(){trigAnalytics("jqmReady");alert("IE detected")});
}
And when detected, I'm firing my analytics snippet with the pageshow binding:
var trigAnalytics = function( trigger ){
$(document).on('pageshow','div:jqmData(role="page").basePage', function (event, ui) {
var url = location.href;
try {
hash = location.hash;
if (hash && hash.length > 1) {
_gaq.push(['_trackPageview', hash.substr(1)]);
_gaq.push(['_setCustomVar', 1, 'id_external', ########, 1 ]);
} else {
_gaq.push(['_trackPageview', url]);
_gaq.push(['_setCustomVar', 1, 'id_external', ########, , 1 ]);
_gaq.push(['b._trackPageview', url]);
}
} catch(err) { }
});
if (typeof _gaq !== "undefined" && _gaq !== null) {
$(document).ajaxSend(function(event, xhr, settings){
_gaq.push(['_trackPageview', settings.url]);
_gaq.push(['b._trackPageview', settings.url]);
});
}
};
So to kick of the event chain, I need to trigger jqmReady when JQM is ready. JQM uses their mobileinit event to indicate just that. So inside my application controller init, I'm binding to it like so:
$(document).bind("mobileinit", function () {
// non-IE OK
if (document.createEvent) {
evt = document.createEvent("Event");
evt.initEvent("jqmReady", true, true);
document.dispatchEvent(evt);
} else if (document.createEventObject) {
// MSIE (NOT WORKING)
document.documentElement.evt = 0; // an expando property
document.documentElement.attachEvent("jqmReady", function () {
document.documentElement.evt = document.documentElement.evt + 1;
});
}
});
I have tried just triggering $(window).trigger('jqmReady'), because when mobileinit triggers, jquery is available. However it seems events created in addEventListener can not be triggered like this, so I need a javascript-only solution to trigger a custom event in IE.
Question:
Can someone give me a pointer on how to trigger a javascript custom event for IE8 correctly?
Ok, I finally understand... here is how it works:
1) setting the listener for jqmReady on the page being loaded
// non-IE: just create a listener for the custom event "jqmReady"
if (document.addEventListener) {
document.addEventListener("jqmReady",function(){trigAnalytics("jqmReady");alert("FF detected")},false);
// IE8
} else if ( document.attachEvent ) {
// create a custom property name jqmReady and set it to 0
document.documentElement.jqmReady = 0;
// since IE8 does not allow to listen to custom events,
// just listen to onpropertychange
document.documentElement.attachEvent("onpropertychange", function(event) {
// if the property changed is the custom jqmReady property
if (event.propertyName == "jqmReady") {
trigAnalytics("jqmReady");
alert("gotcha")
// remove listener, since it's only used once
document.documentElement.detachEvent("onpropertychange", arguments.callee);
}
});
}
So on IE8 I'm not listening for custom jqmReady. Instead I listen for onpropertychange for my custom property jqmReady
2) Then on mobileinit I'm triggering like this:
// non-IE
if (document.createEvent) {
evt = document.createEvent("Event");
evt.initEvent("jqmReady", true, true);
document.dispatchEvent(evt);
} else if (document.createEventObject) { // MSIE
// just change the property
// this will trigger onpropertychange
document.documentElement.jqmReady++;
};
Nice idea (credit to http://dean.edwards.name/weblog/2009/03/callbacks-vs-events/), maybe someone else can find a use for it.
For anyone else interested, I've wrapped up this code into a static javascript object
function Event () {
}
Event.listen = function (eventName, callback) {
if(document.addEventListener) {
document.addEventListener(eventName, callback, false);
} else {
document.documentElement.attachEvent('onpropertychange', function (e) {
if(e.propertyName == eventName) {
callback();
}
});
}
}
Event.trigger = function (eventName) {
if(document.createEvent) {
var event = document.createEvent('Event');
event.initEvent(eventName, true, true);
document.dispatchEvent(event);
} else {
document.documentElement[eventName]++;
}
}
usage:
Event.listen('myevent', function () {
alert('myevent triggered!');
});
Event.trigger('myevent');
Demo:
http://jsfiddle.net/c5CuF/

Categories