Assertion failed in chrome extension and duplication - javascript

In background.js:
function checkForValidUrl(tabId, changeInfo, tab) {
if(changeInfo.status === "loading") {
if (tab.url.indexOf('google.com') > -1 || tab.url.indexOf('amazon.com') > -1) {
chrome.pageAction.show(tabId);
}
}
};
chrome.tabs.onUpdated.addListener(checkForValidUrl);
chrome.pageAction.onClicked.addListener(function(tab){
if (tab.url.indexOf('google.com') > -1){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tab.id, {google_go: "go"});
});
chrome.tabs.create({url: "http://facebook.com", "active":true});
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "facebook_chanel");
port.onMessage.addListener(function(msg) {
if (msg.facebook_go == "go")
port.postMessage(request);
});
});
});
}
In facebook.js:
$(document).ready(function(){
var port = chrome.runtime.connect({name: "facebook_chanel"});
port.postMessage({facebook_go: "go"});
port.onMessage.addListener(function(msg) {
console.log(msg);
});
});
I go too google. Press on pageAction, I see tab with facebook, see one Object in console. In html/background.html console (Chrome) I see the error Assertion failed: in
console.assert(port.name == "facebook_chanel");
I go to google again, press pageAction, and I see at new facebook page one old and 2 new Objects.
How to fix it? Thanks.
UPDATE
in background.js
chrome.tabs.create({url: "http://facebook.com", "active":true}, function(tab){
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
chrome.tabs.sendMessage(tab[0].id, request);
});
});
});
in facebook.js:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(request);
});
Its right way? But I have clear console, whats wrong?

It is still not very clear what you are trying to achieve and why you chose to use porsistent ports (instead of single messaging) and page-actions, but from what I did understand, context-menus could be a better/cleaner approach.
So, below is the source code of a sample extension that registers a context-menu for when some text is selected (i.e. select/highlight some text and right click) and then opens an Facebook tab and sends the selected text for further processing (by the FB tab's content script).
If you still need a page-action instead of a context-menu, change the code like this:
Add listener for chrome.pageAction.onClicked in background-page.
Inject some code into the original tab (e.g. google.com) to retrieve ad return the current selection (using chrome.tabs.executeScript()).
Handle the acquired selected text as demonstrated in the code below.
manifest.json:
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"offline_enabled": false,
"background": {
"persistent": false,
"scripts": [
"background.js"
]
},
"content_scripts": [{
"matches": ["*://*.facebook.com/"],
"js": ["content.js"],
"run_at": "document_end",
"all_frames": false
}],
"permissions": [
"contextMenus"
]
}
content.js:
chrome.runtime.onMessage.addListener(function(msg) {
/* Input validation */
if (!msg.source || !msg.text) {
console.error('Bad message format: ', msg);
return;
}
/* Handle the selected text */
switch (msg.source) {
case 'Amazon':
case 'Google':
alert('Selected on ' + msg.source + ':\n' + msg.text);
break;
default:
alert('Unknown source: ' + msg.source);
break;
}
});
background.js:
var contextMenuID = 'copyToFB';
var googleRegex = /^https?:\/\/(?:[^\.]+\.)?google\..+/;
var amazonRegex = /^https?:\/\/(?:[^\.]+\.)?amazon\..+/;
/* `onUpdated` listener factory (for when FB has loaded) */
var listenerFactory = function(trgTabID, msgSrc, selectedText) {
return function(tabId, info, tab) {
if ((trgTabID === tabId) && (info.status === 'complete')) {
chrome.tabs.onUpdated.removeListener(arguments.callee);
chrome.tabs.sendMessage(trgTabID, {
source: msgSrc,
text: selectedText
});
}
}
};
chrome.contextMenus.create({
type: 'normal',
id: contextMenuID,
title: 'Copy to Facebook',
contexts: ['selection'],
// For some reason documentsUrlPatterns
// does not seem to work (at least on Windows).
// Theoratically, you should be able to limit the
// context-menu on specific sites only
//documentUrlPatterns: [
//'*://*.google.*/*',
//'*://*.amazon.*/*'
//],
enabled: true
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
console.log('Context menu clicked: ', info.menuItemId);
if (info.menuItemId === contextMenuID) {
var selectedText = info.selectionText;
console.log('Selected text: ', selectedText);
if (!selectedText) {
alert('Nothing to copy to Facebook !');
return;
}
var url = info.frameUrl ? info.frameUrl : info.pageUrl;
if (googleRegex.test(url)) {
console.log('Google.com URL: ', url);
/* Handle text selected on `google.com` */
chrome.tabs.create({
url: 'http://facebook.com/',
active: true
}, function(tab) {
chrome.tabs.onUpdated.addListener(
listenerFactory(tab.id, 'Google', selectedText));
});
} else if (amazonRegex.test(url)) {
console.log('Amazon.com URL: ', url);
/* Handle text selected on `amazon.com` */
chrome.tabs.create({
url: 'http://facebook.com/',
active: true
}, function(tab) {
chrome.tabs.onUpdated.addListener(
listenerFactory(tab.id, 'Amazon', selectedText));
});
} else {
console.log('Non-matching URL: ', url);
}
}
});
Final notes:
Because documentUrlPatterns does not seem to work (at least on Windows), the context-menu is shown on every page (when you select some text). You could add extra listeners (e.g. for chrome.tabs.onActivated etc) to remove or disable the context menu when the user is not on one of the permitted URLs.
It might be a good idea to keep track of an ope FB tab and not create a new one every time. (Or you could also look for an already opened (e.g. by the user) FB tab.)

Related

Chrome Extension - Change styles.CSS var not working

Sorry, I messed that up. I did not update the manifest from the last post and I forgot the content.js, that is how the Change button was sending the value and the page was getting the new style attribute value from the one entered in the text field and then to the content.js, existing content.js file added below.
I have a styles.css that changes the web page and is loaded from the popup but want to change a variable in the styles.css from the data entered at the popup.
I've added a content.js file and I can now pass a value from the Popup and it adds a new style attribute to the page but it's not updating the --ptwidth in the styles.css file. I think I need to have it in the styles.css to give the correct location and add the !important option.
I tried to ask this question before an it was closed\linked to one about webpage DOMs and don't have the reputation to post a comment and not sure I should ask my questions there if I could:
How to access the webpage DOM rather than the extension page DOM?
The styles.css injection works using the Wider button and the --ptwidth var is passed the value given (310) in the styles.CSS, at the very least I'd like to be able to enter a new value in the textbox and then use the existing Wider button to load the updated styles.css but it would be nice to have it auto update and maybe even use the slider.
The change button moves the new value entered in the text field to the content.js file and it then adds the new style attribute but it's not working. The insertCSS on the Wider button works but the info it adds is different.
Content.js adds the Style attribute section and the insertCSS adds the :root section that works.
Added by Content.js No Work:
Added by insertCSS Works and adds these two:
Columns before:
Columns after:
Rule before using the working Wider button with insertCSS:
Rules after the insertCSS injections of styles.css:
Popup:
manifest:
{
"manifest_version": 3,
"name": "Hellper",
"description": "Extension",
"version": "0.1",
"icons": { "16": "logo_16_T.png",
"48": "logo_48_T.png",
"128": "logo_128_T.png"
},
"action": {
"default_icon": "logo_16_T.png",
"default_popup":"popup.html"
},
"permissions": ["scripting", "tabs", "activeTab", "content.js"],
"host_permissions": ["<all_urls>"],
"content_scripts": [{
"js": ["jquery-2.2.0.min.js", "popup.js"],
"matches": ["https://www.google.com/*",
"https://en.wikipedia.org/*",
"https://stackoverflow.com/*"]
}]
}
popup.html:
<!doctype html>
<html>
<head>
<title>Popup</title>
</head>
<body>
<input id="button1" type=button value=clickme>
<button class="format">Wider</button>
<button class="reset">Reset</button>
<script src="jquery-2.2.0.min.js"></script>
<script src="popup.js"></script>
<!--
<h2>Background Color</h2>
<input type="color" id="color-changer" />
<h2>Rotate Page</h2>
<input type="range" min="0" max="360" step="1" value="0" id="rotate" />
-->
<h1>New Width</h1>
<p>
<input type="text" id="newWidth" value="120"/>
<input type="submit" id="btnChange" value="Change"/>
</p>
<div class="form-group">
<lable for="slider">Project/Task Width</lable>
<input type="range" min="0" max="999" step="1" value="160" id="slider" />
</div>
</body>
</html>
styles.css:
:root {
--ptwidth: 310px
}
.quixote .qx-grid .editor_grid tbody tr td input, .quixote .qx-grid .editor_grid tbody tr td .input-group {
/*max-width: 450px !important;
min-width: 450px !important;*/
max-width: var(--ptwidth) !important;
min-width: var(--ptwidth) !important;
popup.js:
$(document).ready(function() {
$('.reset').click(function() {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.removeCSS({
target: { tabId: activeTab.id },
files: ["styles.css"]
});
});
})
$('.format').click(function() {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
files: ["styles.css"]
});
/*chrome.tabs.sendMessage(activeTab.id, {"buttonclicked": "wider"});*/
});
})
})
$(function(){
var width = $('#newWidth').val();
$('#newWidth').on("change paste keyup", function(){
width = $(this).val();
});
$('#btnChange').click(function(){
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
chrome.tabs.sendMessage(tabs[0].id, {todo: "changeWidth", sliderWidth: width})
});
});
});
content.js
let root = document.documentElement;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.todo == "changeWidth"){
var updateWidth = request.sliderWidth;
root.style.setProperty('--ptwidth', updateWidth + "px");
// start();
}
});
It seems like there is still some work to do on this extension. I will try to provide only a solution to what was asked an leave the rest for you to work on.
Issues I will try to address
You shouldn't be reusing popup.js as a content script. You should create a separate file instead of having one script be both injected into the webpage and also being used in the popup window.
You are also sending messages from the popup window without anything listening for the messages. This also would be solved by creating a separate content script that listens for these messages.
Partial solution
I edited the manifest.json and popup.js as well as created a new file content.js.
New manifest.json
changed "popup.js" to "content.js"
{
"manifest_version": 3,
"name": "Hellper",
"description": "Extension",
"version": "0.1",
"icons": { "16": "logo_16_T.png",
"48": "logo_48_T.png",
"128": "logo_128_T.png"
},
"action": {
"default_icon": "logo_16_T.png",
"default_popup":"popup.html"
},
"permissions": ["scripting", "tabs", "activeTab"],
"host_permissions": ["<all_urls>"],
"content_scripts": [{
"js": ["jquery-2.2.0.min.js", "content.js"],
"matches": ["https://www.google.com/*",
"https://en.wikipedia.org/*",
"https://stackoverflow.com/*"]
}]
}
New popup.js
only edited the last function block
added a listener for the slider changes
coupled the values in the text box and slider, so when one changes, the other changes as well
changed the message parameter from "sliderWidth" to "newWidth" to make it more general
moved retrieval of the width value into the listeners so the new changed value can be sent along
I also suggest removing the change button entirely because the other listeners make it unnecessary
// Unchanged
$(document).ready(function () {
$(".reset").click(function () {
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
var activeTab = tabs[0];
chrome.scripting.removeCSS({
target: { tabId: activeTab.id },
files: ["styles.css"],
});
});
});
$(".format").click(function () {
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
files: ["styles.css"],
});
/*chrome.tabs.sendMessage(activeTab.id, {"buttonclicked": "wider"});*/
});
});
});
// Changed
$(function () {
// text input listener
$("#newWidth").on("change paste keyup", function () {
const width = $(this).val();
// update slider
$("#slider").val(width);
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { todo: "changeWidth", newWidth: width });
});
});
// listener for change button press
// button might not be needed anymore because of the text input listener above
$("#btnChange").click(function () {
const width = $("#newWidth").val();
// update slider
$("#slider").val(width);
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { todo: "changeWidth", newWidth: width });
});
});
// listener for slider changes
$("#slider").on("input", function () {
const width = $("#slider").val();
// update text box
$("#newWidth").val(width);
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { todo: "changeWidth", newWidth: width });
});
});
});
New content.js
created this script to listen for messages sent from the popup and make the changes to the CSS based on the message
$(document).ready(function () {
// listen for messages sent to the tab the content script is running in
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// check to see if the message received is something that needs to be acted on
if (request.todo === "changeWidth") {
// pull the width data from the message
const newWidth = request.newWidth;
// set the style attribute of :root to override the styles.css value for --ptwidth
document.documentElement.style.setProperty("--ptwidth", newWidth + "px");
}
// send a response to avoid errors in popup.js
sendResponse("Width updated");
});
});
Hopefully that get you on the right track for finishing your extension!
Don't forget to check out the developer documentation for Chrome extensions. There are several useful posts for teaching how to accomplish the various parts of an extension.
[1] Message Passing: https://developer.chrome.com/docs/extensions/mv3/messaging/
[2] Content Scripts: https://developer.chrome.com/docs/extensions/mv3/content_scripts/
I got this working by using the chrome.scripting.insertCSS to replace the --ptwidth value after the styles.css has been injected. So far everything works with just the popup.js and no need for the content.js file.
I use this to load the styles.css with it's default value.
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
files: ["styles.css"]
});
});
Then this when I get a new value from the textbox or slider.
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
//alert(readsavewidth);
$("#slider").val(readsavewidth);
$("#newWidth").val(readsavewidth);
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${readsavewidth + "px"} }`
});
});
});
I also used the chrome.storage.local to set and get the last value used.
$(function () {
// text input listener
$("#newWidth").on("change paste keyup", function () {
width = $(this).val();
//Save it to the localStorage variable which will always remember what you store in it
chrome.storage.local.set({'savewidth': width});
// update slider
$("#slider").val(width);
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${width + "px"} }`
});
});
});
// listener for slider changes
$("#slider").on("input", function () {
width = $("#slider").val();
// update text box
$("#newWidth").val(width);
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${width + "px"} }`
});
});
//Pull text from user inputbox
var data = width;
//Save it to the localStorage variable which will always remember what you store in it
chrome.storage.local.set({'savewidth': data});
});
});
Then I get the value when the popup opens and it then overights the --ptwidth value using the chrome.scripting.insertCSS funtion.
chrome.storage.local.get('savewidth', function (result) {
readsavewidth = result.savewidth;
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
//alert(readsavewidth);
$("#slider").val(readsavewidth);
$("#newWidth").val(readsavewidth);
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${readsavewidth + "px"} }`
});
});
});

Same messages being passed more that once while switching tabs fast : Chrome extension issue

I am currently new to building the chrome extension. However I have started to build a chrome extension that scrapes a webpage and stores the data in the google spreadsheet.
Here is my manifest.json:
"manifest_version": 2,
"version": "0.0.1",
"name": "A listener check",
"description": "Understanding the working of listeners in the application",
"content_scripts": [
{
"matches": [
"https://some.website.com/projects/*",
"http://some.website.com/projects/*"
],
"js": [
"content.js"
],
"all_frames": true,
"run_at": "document_end"
},
{
"matches": [
"https://embedded_website.net/*",
"https://embedded_website_2.net/*"
],
"js": [
"demeter.js"
],
"all_frames": true,
"run_at": "document_end"
}
],
"background":{
"scripts": [
"background.js"
]
},
"browser_action": {
"default_popup": "popup/popup.html"
},
"permissions": [
"tabs",
"background",
"management",
"storage"
]
}
Here is my background.js file
const loadedTabs = [];
const switchedTabs = [];
function updatedCallbackFunction(tabId, changeInfo, tab) {
const message = {
action: "load_page",
changeInfo: changeInfo,
tab: tab,
allowLoad: 1,
state: "loaded"
}
if (!loadedTabs.includes(tab.id))
loadedTabs.push(tab.id);
if (changeInfo.status === 'complete') {
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tab.id, message);
});
}
}
function tabActivatedCallbackFunction(activeInfo) {
const message = {
action: "load_page",
activeInfo: activeInfo,
state: "switched",
allowLoad: 1
}
console.log(`Switched Tabs : ${switchedTabs}`)
if (!switchedTabs.includes(activeInfo.tabId) ) {
switchedTabs.push(activeInfo.tabId);
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(activeInfo.tabId, message);
});
}
}
async function backgroundRuntimeCallbackFunction(message, sender, sendResponse) {
switch (message.action) {
case "unload":
console.log('Wrong page loaded');
break;
case "listen":
console.log('Investigating current page');
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {action: "load_page"});
})
break;
case "demeter_page_direct":
console.log('Going to demeter page');
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {...message, action: 'demeter_page_load'});
});
break;
case "wait_for_page_load":
console.log('Waiting for demeter page load');
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, {...message, action: 'demeter_page_load'});
});
break;
case "upload_google_sheet":
console.log('Updating to google sheet');
const {action, allowLoad, QApresent, ...googleSheetData} = message;
console.log(googleSheetData);
googleSheetData.hitlink = googleSheetData.hitlink.substring(0, googleSheetData.hitlink.lastIndexOf('&'));
const options = {
method: 'post',
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(googleSheetData)
}
const response = await fetch('my_backend_app.com/url_to_fetch', options);
const responseBody = await response.json();
console.log(responseBody);
const boxUpdateMessage = {
...message,
action: "load_page",
allowLoad: 0
};
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, boxUpdateMessage);
});
break;
}
}
// creating the functions to get the listeners
function tabUpdateFunction() {
if (chrome.tabs.onUpdated.hasListener(updatedCallbackFunction)) {
console.log('The onupdated listener is already there so removing it!');
chrome.tabs.onUpdated.removeListener(updatedCallbackFunction);
}
// adding the onupdated listener again
console.log('No overhead update function');
chrome.tabs.onUpdated.addListener(updatedCallbackFunction);
}
function tabSwitchFunction() {
if (chrome.tabs.onActivated.hasListener(tabActivatedCallbackFunction)) {
console.log('The tab switch function already there so removing it');
chrome.tabs.onActivated.removeListener(tabActivatedCallbackFunction);
}
console.log('No overhead switch function');
chrome.tabs.onActivated.addListener(tabActivatedCallbackFunction);
}
function backgroundRuntimeFunction() {
console.log(`backgroundRuntimeFunction : ${chrome.runtime.onMessage.hasListener(backgroundRuntimeCallbackFunction)}`);
chrome.runtime.onMessage.removeListener(backgroundRuntimeCallbackFunction);
if (chrome.runtime.onMessage.hasListener(backgroundRuntimeCallbackFunction)) {
chrome.runtime.onMessage.removeListener(backgroundRuntimeCallbackFunction);
}
chrome.runtime.onMessage.addListener(backgroundRuntimeCallbackFunction);
}
// calling the functions
tabUpdateFunction();
tabSwitchFunction();
backgroundRuntimeFunction();
Here is content.js
function getDetails(msg) {
// extract info
return {
action: 'demeter_page_direct',
// more info
};
}
function contentRuntimeCallback(message, sender, sendResponse) {
switch (message.action) {
case "load_page":
const delimiterMessage = getDetails(message);
if (delimiterMessage.title) {
const returnBtnForms = document.getElementsByClassName('force-inline');
for (let returnBtnForm of returnBtnForms) {
returnBtnForm.addEventListener('submit', (e) => {
console.log('Return btn was clicked!');
e.preventDefault();
delimiterMessage.return = 1;
delimiterMessage.allowLoad = 0;
chrome.runtime.sendMessage(delimiterMessage);
})
}
chrome.runtime.sendMessage(delimiterMessage);
}
else {
chrome.runtime.sendMessage({...delimiterMessage, action: 'unload'});
}
break;
}
}
function contentRuntimeCheck() {
if (chrome.runtime.onMessage.hasListener(contentRuntimeCallback))
chrome.runtime.onMessage.removeListener(contentRuntimeCallback);
chrome.runtime.onMessage.addListener(contentRuntimeCallback);
}
contentRuntimeCheck();
and demeter.js
function demeterRuntimeCallbackFunction(message, sender, sendResponse) {
switch (message.action) {
case "demeter_page_load":
if (message.hittitle.includes("specifictitle")) {
const svgParentContainer = document.getElementById('SVG_PARENT_CONTAINER');
if (!svgParentContainer) {
console.log('svg container loading!');
chrome.runtime.sendMessage({...message, action: 'wait_for_page_load'});
break;
} else {
// counting the total number of boxes
const totalBoxes = svgParentContainer.querySelectorAll('rect').length;
// when page loads for the first time
if (message.allowLoad === 1) {
if (message.changeInfo)
console.log('Updated info done');
else if (message.currentTabInfo)
console.log('In port 1');
console.log(`still allowing to load`);
message.allowLoad = 0;
const finalSheetUploadMessage = {
...message,
action: 'upload_google_sheet',
boxcount: totalBoxes
}
chrome.runtime.sendMessage(finalSheetUploadMessage);
break;
} else if (message.return === 1) {
console.log('In port 2');
const finalSheetUploadMessage = {
...message,
hitqueue: message.hitqueue - 1,
action: 'upload_google_sheet',
boxcount: totalBoxes
}
chrome.runtime.sendMessage(finalSheetUploadMessage);
break;
} else {
console.log('In port 3');
const saveBtn = document.querySelector("[aria-label='save']");
const submitBtn = document.querySelector('[aria-label="submit"]');
console.log(submitBtn);
if (saveBtn) {
saveBtn.addEventListener('click', function (e) {
console.log('Save button clicked');
const totalBoxes = svgParentContainer.querySelectorAll('rect').length;
const finalSheetUploadMessage = {
...message,
action: 'upload_google_sheet',
boxcount: totalBoxes
};
chrome.runtime.sendMessage(finalSheetUploadMessage);
});
}
if (submitBtn) {
console.log(submitBtn);
const totalBoxes = svgParentContainer.querySelectorAll('rect').length;
submitBtn.addEventListener('click', (e) => {
const sheetUpdateMessage = {
...message,
action: 'upload_google_sheet',
boxcount: totalBoxes,
submit: 1,
return: 0
}
console.log('Submit btn was clicked');
chrome.runtime.sendMessage(sheetUpdateMessage);
});
break;
}
break;
}
}
}
}
}
if (chrome.runtime.onMessage.hasListener(demeterRuntimeCallbackFunction))
chrome.runtime.onMessage.removeListener(demeterRuntimeCallbackFunction);
chrome.runtime.onMessage.addListener(demeterRuntimeCallbackFunction);
Working
The website i am scraping has an embedded website inside it as an iframe, so I created another content script for it. What i did was scraped details on the original webpage, then sent those details to the embedded webpage to extract further information there and then I am clustering those information and uploading to my google sheet
here is my workflow
content.js -> background.js -> demeter.js -> upload_google_sheet
I have kept allowload = 1 for automatically update the google sheet once all the page loads. Since several instances of the webpage can be open so I tried to implement tab switch event. So that the extension starts firing messages again from scratch once tab switch happens.
Problem
I open let say 3 tabs of the website and rapidly switch tabs among them. I get quite a large number of duplicate rows. I did a lot in tracking and removing listeners. But I really can't find the exact solution or approach to solving this. I want that after rapid switching of tabs. I get the information on the current tab only and no information from other tabs.
Since I am very new to stack overflow. Pardon my long question.

Why is Input.dispatchMouseEvent not dispatching an event?

I'm trying to dispatch an IsTrusted event that simulates the user clicking a certain place on the screen. I'm trying this out through a Chrome Extension, although I'm not having much luck. There are no errors in the console, and my giant screen-wide button isn't being clicked. Here is my background.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var activeTab = tabs[0];
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "clickElement" ) {
chrome.debugger.attach({tabId:tab.id}, "1.2", function(debugg) {
chrome.debugger.sendCommand(
{tabId:tab.id}, "Debugger.enable", {},
function() {
chrome.debugger.sendCommand({tabId:tab.id}, "Input.dispatchMouseEvent",
{
type:"mousePressed",
x:parseFloat(request.x),
y:parseFloat(request.y)
})
})
})
}
}
);
chrome.tabs.sendMessage(activeTab.id, {"message": "runbot"});
});
});
And my content.js just sends the clickElement message with the coordinates of the button.
Any ideas?
What you need to do is remove this nonsense:
chrome.debugger.sendCommand(
{tabId:tab.id}, "Debugger.enable", {},
And add button: "left" prop to the parameters object:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var activeTab = tabs[0];
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "clickElement" ) {
chrome.debugger.attach({tabId:tab.id}, "1.2", function(debugg) {
chrome.debugger.sendCommand({tabId:tab.id}, "Input.dispatchMouseEvent",
{
type:"mousePressed",
button: "left",
x:parseFloat(request.x),
y:parseFloat(request.y)
})
})
}
}
);
chrome.tabs.sendMessage(activeTab.id, {"message": "runbot"});
});
});
Also make sure you have the "debugger" permission in you manifest.json.
ps. When debugger attaches you should see this bar appear just below the address bar saying smth like '${extn. name} is executing browser debug':

captureVisibleTab in chrome extension returns a double sized image

I'm developing a screenshot taking google chrome extension. But my problem is, that the screenshot don't have the original size, it is much bigger. If the size of my web document has 1272x594 pixel, the screenshot will be 2544x1188 pixel.
The minimal working example takes a screenshot of the current tab, after clicking on "Test Plugin" in context menu and displays the screenshot in the current tab as an overlay.
My minimal working example:
manifest.json
{
"name": "Minimal working Example",
"version": "1.0",
"description": "",
"permissions": ["contextMenus", "tabs", "activeTab"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"manifest_version": 2
}
background.js
chrome.runtime.onInstalled.addListener(function() {
chrome.contextMenus.create({
id: 'minimalExampe',
title: 'Test Plugin',
contexts: ['all']
});
});
chrome.contextMenus.onClicked.addListener(function(itemData) {
if (itemData.menuItemId == "minimalExampe") {
console.log('clicked');
// checks, if content.js is already injected, otherwise content.js is added
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {func: 'alreadyInjected'}, function (response) {
if (response === undefined) {
chrome.tabs.executeScript({
file: 'content.js'
});
console.log('Script added');
} else {
console.log('Script was already injected');
}
//does the screenshot
chrome.tabs.captureVisibleTab(null, {format: 'png'}, function (screen) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {func: 'showPicture', picture: screen});
});
});
});
});
}
});
content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch(request.func) {
case 'showPicture':
showPicture(request.picture);
break;
case 'alreadyInjected':
sendResponse({alreadyInjected: true});
break;
}
});
function showPicture(picture) {
console.log(window.innerWidth);
console.log(window.innerHeight);
var img = new Image();
img.src = picture;
img.style.zIndex = '1000';
img.style.position = 'absolute';
img.style.top = '0';
img.style.left = '0';
img.onload = function () {
console.log(img.naturalWidth);
console.log(img.naturalHeight);
};
document.body.appendChild(img);
}
Does anybody know why? Every help is welcome. I already searched on Stackoverflow, but there is only one same question with no answer because no minimal working example or anything else was provided.
Best,
Klaus

Detect Tab duplication events

I'm developing a Chrome extension;
I need to detect when a tab is duplicated, I'm looking for a tab detection event like onduplicate.addListener()?
Is that possible with the Chrome extension API?
This is the closest implementation:
const newTabsId = new Set();
// Observe all new tabs with opener ID
chrome.tabs.onCreated.addListener(tab => {
if(tab.openerTabId) {
newTabsId.add(tab.id);
}
});
// Waiting for a new tab completeness
chrome.tabs.onUpdated.addListener((tabId, changes, tab) => {
if(newTabsId.has(tabId) && changes.status === 'complete') {
if(!tab.openerTabId) {
return;
}
// Retrieve opener (original) tab
getTabById(tab.openerTabId)
.then(originalTab => {
if(
originalTab.url === tab.url && // original and new tab should have same URL
originalTab.index + 1 === tab.index && // new tab should have next index
tab.active && tab.selected // new tab should be active and selected
// also we may compare scroll from top, but for that we need to use content-script
) {
console.log('Duplicate:', tab);
}
});
// Remove this tab from observable list
newTabsId.delete(tabId);
}
});
// Syntax sugar
function getTabById(id) {
return new Promise((resolve, reject) => {
chrome.tabs.get(id, resolve);
});
}
// Cleanup memory: remove from observables if tab has been closed
chrome.tabs.onRemoved.addListener(tabId => {
newTabsId.delete(tabId);
});
EDIT 1: But yeah, there is no clear solution now to detect real duplicate
Tab duplication preserves sessionStorage of the page so simply store some unique variable in your content script in each page and check if it's present in the beginning of your content script.
manifest:
"content_scripts": [{
"matches": ["<all_urls>"],
"run_at": "document_start",
"js": ["content.js"]
}],
content script:
if (sessionStorage[chrome.runtime.id]) {
chrome.runtime.sendMessage({
action: 'checkDup',
tabId: Number(sessionStorage[chrome.runtime.id]),
}, isDupe => {
console.log(isDupe);
});
} else {
chrome.runtime.sendMessage({
action: 'getTabId'
}, tabId => {
sessionStorage[chrome.runtime.id] = tabId;
});
}
background/event script:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
switch (msg.action) {
case 'getTabId':
sendResponse(sender.tab.id);
return;
case 'checkDup':
chrome.tabs.get(msg.tabId, tab => {
if (tab
&& tab.index == sender.tab.index - 1
&& tab.url == sender.tab.url) {
sendResponse(true);
console.log('Tab duplicated: ', tab, '->', sender.tab);
}
});
return true; // keep the message channel open
}
});

Categories

Resources