How can I focus a document programmatically from a Chrome extension (to paste content) - google-chrome

What I try to do is simple:
I am making a chrome extension.
This extension has a shortcut (e.g. alt + o).
When I press this shortcut it switches to a tab,
and should paste clipboard content in the page.
Here's the background script that reacts to the shortcut and inject code in page to simulate paste event:
// --- background.js
chrome.commands.onCommand.addListener((command) => {
switch (command) {
case 'switch_tab_and_paste_content':
switchToTabAndPasteContent()
break;
}
})
async function switchToTabAndPasteContent() {
const tab = (await chrome.tabs.query({})).filter((tab) => tab.title.toLowerCase().includes('my page'))[0]
// focus the window
await chrome.windows.update(tab.windowId, { focused: true })
// and focus the tab
await chrome.tabs.update(tab.id, { active: true })
/* inject paste emulation script */
await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: function () {
pasteContent()
}
})
}
pasteContent() in the code above is a function contained in a content script. It's used to simulate paste event (just like a user would use ctrl + v on some page.)
// --- paste.js
async function pasteContent() {
try {
const wizElement = document.querySelector('c-wiz')
const clipboardItems = await navigator.clipboard.read()
const firstItem = clipboardItems[0]
const blob = await firstItem.getType('image/png')
const file = new File([blob], 'image.png', { type: 'image/png' })
const dataTransfer = new DataTransfer()
dataTransfer.items.add(file)
const pasteEvent = new ClipboardEvent('paste', {
bubbles: true,
cancelable: true,
clipboardData: dataTransfer,
})
wizElement.dispatchEvent(pasteEvent)
}
catch (e) { console.log(e) }
}
and here's the simplified manifest.json
{
"name": "app-connector",
"version": 3,
"permissions": [ "tabs", "scripting", "activeTab" ],
"background": {
"service_worker": "background.js"
},
"content_scripts": [{
"matches": [ ... ],
"js": "paste.js"
}],
"commands": {
"switch_tab_and_paste_content": {
"description": "Switch tab"
}
}
}
However when I press the shortcut on my keyboard, the tab is focused but I get this exception
DOMException: Document is not focused.
I know the reason of that exception (it's because when I leave the tab the document loses focus, chrome.tabs.update(tab.id, { active: true }) activates the tab but it's not enough to focus back on the document)
What I want to know is if there is a way to bypass that (a chrome flag, a command-line argument, etc...).
Of course the ideal would be a native solution in the extension ecosystem but I didn't find/can't think of any solution. I tried opening/closing a popup fast to give focus back to the page but I still get this exception (it seems like it won't work unless I click in the page but it defeats the purpose of making an automated routine behind).
am I missing a permission?

Related

Can't inject JavaScript into new tab from popup.js

I'M creating an extension that autofill's forms on different websites. There is a portion of the script that records the actions of the user with event listeners. After the recording is done the user click the test button that will launch a new tab and autofill's the form. I know launching a new tab for testing is not necessary but this function will be used later for another feature.
This is what I got so far. I had started over several times. I tried sending a message to content script. The chrome.windows,create function only launched from the popup script. I started chrome development a week ago. I'm totally confused.
Popup.js
`(async () =\> { const tab = await chrome.tabs.create({url: 'https://www.listyourself.net/ListYourself/listing.jsp'});
const tabId = tab.id; if (!tab.url)
await onTabUrlUpdated(tabId);
await chrome.scripting.executeScript({
target: {tabId},
files: \['test.js'\],
});`
})();`
I think your extension has two problems.
onTabUrlUpdated is not defined.
Execution of chrome.tabs.create causes the popup to lose focus, so the JavaScript is also terminated.
Here is a sample that fixes it.
manifest.json
{
"manifest_version": 3,
"name": "hoge",
"version": "1.0",
"permissions": [
"scripting"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "hoge"
}
}
background.js
chrome.action.onClicked.addListener(() => {
main();
});
const test = () => {
console.log("test");
}
const main = async () => {
const tab = await chrome.tabs.create({ url: "https://nory-soft.web.app/" });
const tabId = tab.id;
if (!tab.url) {
chrome.tabs.onUpdated.addListener((updateTabId, changeInfo) => {
if ((updateTabId == tabId) && (changeInfo.status == "complete")) {
chrome.scripting.executeScript({
target: { tabId },
func: test
});
}
});
}
}

Chrome Extension embedding script in active web page, in MV3?

beautiful people on the internet. I am new to chrome extension not new to writing code though. I have implemented webpack to use external packages. One major one in my application is npm package by the name "mark.js".
My application works like this i want some specific words to be highlighted in active webpage using this package. I have written code for this to achieve the functionality but the problem is with loading the script in a web page. I have performed different aspect of loading script but that doesnot work. The new MV3 version have some strict rules.
I want to achieve anything similar of loading script in an active webpage. Please help.
btn.addEventListener("click", async () => {
console.log("BUTTON IS PRESSED!!");
try {
await chrome.tabs.query(
{ active: true, currentWindow: true },
async function (tabs) {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: highlightText,
args: [arr],
});
}
);
} catch (e) {
console.log("ERROR AT CHROME TAB QUERY : ", e);
}
});
async function highlightText(arr) {
console.log(typeof Mark);
try {
var instance2 = new Mark(document.querySelector("body"));
// instance2.mark("is");
var success = [];
// const instance2 = new Mark(document.querySelector("body"));
await Promise.all(
arr.map(async function (obj) {
console.log("OBJECT TEXT : ", obj.text);
instance2.mark(obj.text, {
element: "span",
each: function (ele) {
console.log("STYLING : ");
ele.setAttribute("style", `background-color: ${obj.color};`);
if (obj.title && obj.title != "") {
ele.setAttribute("title", obj.title);
}
ele.innerHTML = obj.text;
success.push({
status: "Success",
text: obj.text,
});
},
});
})
);
console.log("SUCCESS : ", success);
} catch (error) {
console.log(error);
}
}
There's no need to use chrome.runtime.getURL. Since you use executeScript to run your code all you need is to inject mark.js before injecting the function.
Also, don't load popup.js in content_scripts, it's not a content script (these run in web pages), it's a script for your extension page. Actually, you don't need content_scripts at all.
btn.addEventListener('click', async () => {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
const target = { tabId: tab.id };
const exec = v => (await chrome.scripting.executeScript({ target, ...v }))[0].result;
if (!await exec({ func: () => !!window.Mark })) {
await exec({files: ['mark.js.min'] });
await exec({func: highlightText, args: [arr] });
}
});
For V3 I assume you will want to use Content Scripts in your manifest to inject the javascript into every webpage it matches. I recently open-sourced TorpedoRead and had to do both V2 and V3, I recommend checking the repo as it sounds like I did something similar to you (Firefox is V2, Chrome is V3).
The code below need to be added to your manifest.json and this will execute on every page based on the matches property. You can read more about content scripts here: https://developer.chrome.com/docs/extensions/mv3/content_scripts/
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["yourscript.js"]
}
],

Can't capture screenshot on click, if a new tab opens immediately - chrome extension

I am building a chrome extension that captures the screenshot (tab) on clicking, say, hyperlink. The current Tab is not being captured if a new tab opens on clicking the hyperlink or a button.
Error:
Unchecked runtime.lastError: Cannot access contents of URL "". Extension manifest must request permission to access this host.
How can I capture the current tab in this scenario (not the new tab)?
In the example below, dataUrl (screenshot data) will be null if clicked on the first link.
The new tab opens quickly, and by the time function is being executed in the background script, the focus is on the new tab (see current tab id).
To reproduce:
index.html
<a id="first" href="https://www.example.org" target="_blank">Open link in new tab</a> <br>
<a id="second" href="https://www.example.org">Open link in same tab</a>
manifest.json
...
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"css": ["content.css"]
}
],
"host_permissions": ["<all_urls>","*://localhost:8080/*"],
"permissions": [
"activeTab",
"tabs"
],
...
content.js
/////////////// Capture tab on mouse click ///////////////
document.addEventListener("click", (e) => {
chrome.runtime.sendMessage({ msg: "capture"}, function (response) {
});
console.log("inside click eventListener");
}, true);
background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
console.log("Current tab id", tabs[0].id);
console.log("Sender tab id", sender.tab.id);
});
chrome.tabs.captureVisibleTab(
null,
{
format: "png",
quality: 100,
},
function (dataUrl) {
console.log(dataUrl);
}
);
return true;
});
Please let me know if you need additional details. Thanks in advance!

While using chrome.debugger api it closes automatically and says target closed but the tab is still open, Can anyone help please?

Here is the relevant part of the code.
I am trying to capture network calls using chrome.debugger API here. Everything is ok but once the page loads or I click a link then after the first action it closes automatically
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.play) {
if (message.captureNtwrk) {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const tab = tabs[0];
tabId = tab.id;
chrome.debugger.attach({ tabId: tabId }, version, onAttach.bind(null, tabId));
});
}
if (message.captureScreen) {
}
if (message.captureEvent) {
}
}
if (message.stop) {
chrome.debugger.detach({ tabId: tabId });
}
});
// Function to run while debugging
const onAttach = (tabId) => {
if (chrome.runtime.lastError) console.log(chrome.runtime.lastError.message);
chrome.debugger.sendCommand({ tabId: tabId }, 'Network.enable');
chrome.debugger.onEvent.addListener(onEvent);
chrome.debugger.onDetach.addListener((source, reason) => {
console.log('Detached: ' + reason);
});
});
You can try disabling other extensions, especially 3rd party extensions added by desktop applications. One particular extension observed, which causes debugger to detach after page navigation is Adobe Acrobat.

how to change the popup content based on the current url extension page action

am new in creating chrome extensions, I'm developing an extension page action it works in certain urls, I would like to put different text in the popup for each url, i can do it? please help me.
My background.js is thus
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (~tab.url.indexOf('url1.com.br')) {
chrome.pageAction.show(tabId);
}
if (~tab.url.indexOf('url2.com.br')) {
chrome.pageAction.show(tabId);
}
});
OK. First of all, to show page_action icon on specific URLs you can use declarative content.
// When the extension is installed or upgraded ...
chrome.runtime.onInstalled.addListener(function() {
// Replace all rules ...
chrome.declarativeContent.onPageChanged.removeRules(undefined, function() {
// With a new rule ...
chrome.declarativeContent.onPageChanged.addRules([
{
// That fires when a page's on a specific URL
conditions: [
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlContains: 'url1.com.br' },
}),
new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlContains: 'url2.com.br' }
})
],
// And shows the extension's page action.
actions: [ new chrome.declarativeContent.ShowPageAction() ]
}
]);
});
});
Don't forget adding a permission for declarative content in manifest.json. Another thing is different texts for different urls.
popup.js
chrome.tabs.query({'active': true, 'currentWindow': true}, function (tabs) {
var dynamicText = "You are here;"+ tabs[0].url;
document.getElementById("textbox").value = dynamicText ;
});
This sample gets the currentWindow's URL and insert it into the element that has textbox id. I hope samples are enough to solve the problem.