Chrome Extension devtools_page Communication problem - google-chrome-extension

I am getting confused with Chrome Extension devtools_page.
I want to send a command to the current websites Namespace and retrieve information from that page.
devtools_page JS file
//hey webpage, I know you have the Namespace ABC with the method collectData
webpage
let data = ABC.collectData;
return data;
devtools
// I got the data
Where do I add the communication in the extension to be able to get these data.
In the manifest I have "content_scripts" with "document_start"
And I have "web_accessible-resource" and the JS file from the "devtools_page".
And how do I instantiate the communication?
I tried so many different things and did not manage to get the communication up and running.
My best try was to use chrome.tabs.executeScript, but I cannot access the Namespace.
This is what I have so far:
a) executeScript
chrome.tabs.executeScript({
code: 'console.log("foo"); return true;
}, function(results) {
console.log(results[0]);
});
Result: Unchecked runtime.lastError: Cannot access contents of url "devtools://devtools/bundled/devtools_app.html?remoteBase=https://chrome-devtools-frontend.appspot.com/serve_file/#326d148b9655369b86498d9ecca39f63dd2bdd2d/&can_dock=true&dockSide=undocked". Extension manifest must request permission to access this host.
b) devtools.inspectedWindow.eval
chrome.devtools.inspectedWindow.eval(
"ABC.AllItems.all",
function(result, isException) {
if (isException)
//==> I am getting here
}
);
Result: Object reference chain is too long
Any help is appreciated.

Related

Manifest v3 fetch data from external API

I'm developing a Browser (Chrome, Firefox) extension with react with Manifest v3.
In my app, I have a search bar where I show suggested words based on the value typed by the user (like search engine do).
In manifest v2, I used to load this script like so:
"content_security_policy": "script-src 'self' https://suggest.finditnowonline.com; object-src 'self'"
In v3 this is not supprted anymore but I cannot find the way how I could still make my code work.
he most relevant resource I found online are this answer: Content Security Policy in Manifest V3 for Facebook Page Plugin
and this documentation: https://www.chromium.org/Home/chromium-security/extension-content-script-fetches
But I cannot understand how I can implement my script from the background.js page since It needs to fetch the API dynamically, every time the user type something in the input field.
This is the react code: where I fetch the api
useEffect(() => {
const fetchSuggestedWords = async () => {
try {
const res = await fetchJsonp(`${process.env.SUGGESTED_WORDS_URL}${searchValue}`)
const suggestedWordsArray = await res.json()
setSuggestedWords(suggestedWordsArray[1].slice(0, 10))
return
} catch {
console.log('error fetching suggested results')
}
}
if (searchSuggestedWords) {
fetchSuggestedWords()
}
}, [searchValue])
Where searchValue is a state update whenever the onChange event is trigger on the input field.
Any tips on how to approach this new format?
Would people recommend not switching to Manifest v3 just yet?
From what I gathered, you're trying to talk to an api and not load an external script.
If you're trying to load external, that will not work.
Fetching data on the other hand has multitudes of ways it can be done, not all are proper though.
Also I've noticed that you're misunderstanding the core keys of the async messaging system and service worker cycle.
Add a service worker
*Background.js or svc-worker.js
and give it a on message listener, try to at least handle messaging between your extension, if you're not sure, you can always get an example on github.
After that, it's a matter of setting the CSP and optimizing where you'll be fetching the data.
After a little while, I'm sure you'll get the hang of it.
the code in content should be like
content.js
inputElement.oninput = (e) => {let input = e.target.value;
chrome.runtime.sendMessage({Origin: 'Content', Message: input})};
Handle the message in svc worker
svc-worker.js formatted
chrome.runtime.onMessage(request => {
const {Origin, Message} = request
// parse message
// fetch chain here
})
Please note, this is not the only way to handle this.
There's a billion different ways, if you want to add some of this data in a use Effect hook as a dependency, that's going to be tricky, as the data given obtained is always counted as different when obtained via message. < At least in my case scenario.

Injected script unable to access chrome.storage API

I am writing a chrome extension, using a content script to inject some javascript code. As follows:
let actualCode = 'My Injected JS Code';
let script = document.createElement('script');
script.textContent = actualCode;
(document.head||document.documentElement).appendChild(script);
For the injected script, I would like to use some value stored in chrome.storage.sync. However, I found that the API is unaccessible to the injected script (storage.sync is undefined). The API is only accessible within the content script, but not for the injected script. Any ideas how I could access chrome.storage API for the injected script too?
PS: I've registered the storage API in manifest.json
PS: When I open the developer's console on chrome and type "chrome.storage", it returns undefined too. I think this might be a permission problem?
The inject way you used for the script, made it work in the web page environment, which doesn't have access to most of Chrome Extensions API.
However, there is an option to use Messages API which allows sending requests from a webpage to the extension by ID.
In this case, you also need to implement a listener in your background page, to answer such requests.
P.S. chrome.storage API also shouldn't be available from page console. You may want to debug it from your background page console or select a content script environment (if the extension has such):
you can use window.postMessage({type : "MESSAGE_NAME"}) in injected script to send "message" event. then use windows.addEventListner("message", callback) in content_script to listen on "message" event.
You can specify the type of message to which you want to listen in the callback function. for instance
in content_script.js
function func_callback(){
if (event.data.type && event.data.type === "MESSAGE_NAME") {
# Your code
}`enter code here`
}
windows.addEventListner("message", func_callback)
in injected_script.js
<button onClick={()=>window.postMessage({type : "MESSAGE_NAME"})}></button>

How to fetch the value of a key from localstorage which is stored in your browser in a Chrome Extension

I am new to developing Chrome Extensions.
I am trying to get the values for one of the keys which is stored in my browser's localstorage (inspect -> Application -> Localstorage). What I want to do is in my Chrome Extension, when you click a button, I just need it to fetch the value from localstorage for the page which is open in my browser's current tab.
This is the JS function which I am using -
chrome.storage.local.get(['granted.selected'], function(result) {
console.log('Value currently is ' + result.granted.selected);
});
granted.selected - is the name of the key whose value I want to fetch.
When this executes, I get "Value currently is undefined" whereas I want it to fetch the values stored in the above key (granted.selected).
What do I need to add to have this fetch the value for that key from my current open tab's localstorage?
In short - Just want to access a webpage's localStorage from a Chrome extension.
Any help will be greatly appreciated. Thanks!!
chrome.storage.local is not localStorage, it's a completely different storage API.
localStorage is a DOM storage.
To access DOM of a web page you need a content script [1].
Here's an example of programmatic injection in the popup script or in the background script:
chrome.tabs.executeScript({
code: 'localStorage["keyName"]'
}, ([result] = []) => {
if (!chrome.runtime.lastError) {
console.log(result);
// use the result here inside the callback
}
});
Notes:
You'll need permissions in manifest.json as explained in [1].
keyName is the name of the key as seen in devtools "Application" panel of the web page
each part of an extension has its own devtools console.

chrome extension - alternative to externally_connectable?

It seems like the externally_connectable feature that allows a website to communicate with an extension is still in the dev channel and not yet stable. Are there any other ways to allow a specific website to communicate with my extension, while I wait for this feature to become stable? How have chrome extension developers traditionally done it?
Thanks Rob W for pointing me in the direction of HTML5 messaging. For the benefit of other chrome extension developers, I'm writing about the general problem I was trying to solve and the solution that worked in the end.
I am making a chrome extension that can control music playback on a tab via a popup player. When a user clicks on play/pause/etc on the popup player, the extension should be able to convey that message to the webpage and get back a response stating whether the action was accomplished.
My first approach was to inject a content script into the music player page. The problem is, though, that content scripts operate in a "sandbox" and cannot access native javascript on the page. Therefore, the content script was pretty useless (on its own), because while it could receive commands from the extension, it could not effect any change on the webpage itself.
One thing that worked in my favor was that the website where the music was playing belongs to me, so I could put whatever javascript I wanted there and have it be served from the server. That's exactly what I used to my advantage: I created another javascript file that would reside on the website and communicate with the content script mentioned above, via the window object of the page (i.e. HTML5 messaging). This only works because the content script and the javascript file both exist in the same webpage and can share the window object of the page. Thanks Rob W for pointing me to this capability. Here is an example of how the javascript file on the page can initiate a connection with the content script via the window object:
content_script.js (injected by extension into xyz.com):
window.addEventListener("message", function(event) {
if(event.data.secret_key &&
(event.data.secret_key === "my_secret_key") &&
event.data.source === "page"){
if(event.data.type){
switch(event.data.type) {
case 'init':
console.log("received connection request from page");
window.postMessage({source: "content_script", type: 'init',
secret_key: "my_secret_key"}, "*");
break;
}
}
}
}, false);
onpage.js (resides on server and served along with xyz.com):
window.postMessage({source: "page", type: 'init',
secret_key: "my_secret_key"}, "*");
window.addEventListener("message", function(event) {
if(event.data.secret_key &&
(event.data.secret_key === "my_secret_key") &&
event.data.source === "content_script"){
if(event.data.type){
switch(event.data.type) {
case 'init':
console.log("connection established");
break;
}
}
}
}, false);
I check the secret key just to make sure that the message originates from where I expect it to.
That's it! If anything is unclear, or if you have any questions, feel free to follow up!
You could have an extension inject a content script alongside a web page, and use that to pass messages back and forth between the website and the background page of the extension.
It's tedious, though, and externally connectable is a lot nicer.

Unable to use getBackgroundPage() api from a devtools extension

I am trying to write an extension that adds functionality to the Chrome devtools.
According to the devtools documentation, it says that the pages in devtools support very limited apis. Any API that is not supported can be access by accessing it through the background page, just as what contentscripts does.
Here is the relevant documentation snippet:
The tabId property provides the tab identifier that you can use with the chrome.tabs.* API calls. However, please note that chrome.tabs.* API is not exposed to the Developer Tools extension pages due to security considerations — you will need to pass the tab ID to the background page and invoke the chrome.tabs.* API functions from there.
Here is the source url: http://developer.chrome.com/extensions/devtools.inspectedWindow.html
However, when I try to do that, I get the following error in the console:
uncaught Error: "getBackgroundPage" can only be used in extension processes. See the content scripts documentation for more details.
Here is my code in my devtools.js script:
chrome.extension.getBackgroundPage().getLocation();
What am I doing wrong?
EDIT
I should describe my scenario first, and show how I am implementing it.
What I want to do is to display extra data in a devtools panel related to a webpage. In order to get that data, I will need to send a HTTP request in the same session as the page being debugged, because it requires authentication.
Use Case:
User browses to a particular URL. He is authenticated to the site. He then invokes devtools. The devtools panel opens up and a new panel shows up that has extra data related to the page.
Implementation:
1) DevTools script finds out the url of the page being inspected. If the url matches the site base hostname, then it opens a panel. In the callback of the panel creation, it sends a message to a background page, asking it to download a JSON payload from a debug endpoint on the same site, and then sends it to the devtools extension, wh ich then displays it.
Problems:
1) The background page gets the request, and downloads the URL. However the download is not using the same session as the user, so the download request fails.
2) From devtools window, I got the tabId of the inspected window. I send this tabId to the background page so that it can parse some stuff out of the url. However, chrome.tabs.get(tabId) does not return the tab.
To summarize, I need to
1) Get the background page to download data in the same session as the user's tab that is being debugged.
2) I need to have the background page be able to get access to the user's tab.
The APIs available to extension pages within the Developer Tools window include all devtools modules listed above and chrome.extension API. Other extension APIs are not available to the Developer Tools pages, but you may invoke them by sending a request to the background page of your extension, similarly to how it's done in the content scripts.
I guess the documentation is little ambiguous, By chrome.extension API they mean the Supported API's for content scripts.
So, you can use long lived communication for communication between inspected page and background page
Demonstration:
The following code illustrate scenario where a devtools page need some information from background page, it uses messages for communication.
manifest.json
Ensured permissions are all available in manifest file
{
"name":"Inspected Windows Demo",
"description":"This demonstrates Inspected window API",
"devtools_page":"devtools.html",
"manifest_version":2,
"version":"2",
"permissions":["experimental"],
"background":{
"scripts" : ["background.js"]
}
}
devtools.html
A trivial HTML File
<html>
<head>
<script src="devtools.js"></script>
</head>
<body>
</body>
</html>
devtools.js
Used Long lived Communication API's
var port = chrome.extension.connect({
name: "Sample Communication"
});
port.postMessage("Request Tab Data");
port.onMessage.addListener(function (msg) {
console.log("Tab Data recieved is " + msg);
});
background.js
Responded to communication request and passed trivial information using tab API()'s
chrome.extension.onConnect.addListener(function (port) {
port.onMessage.addListener(function (message) {
chrome.tabs.query({
"status": "complete",
"currentWindow": true,
"active": true
}, function (tabs) {
port.postMessage(tabs[0].id);
});
console.log("Message recived is "+message);
});
});
Sample Output received for trivial devtools.js here
Let me know if you need more information
EDIT 1)
For your question 1)
Can you make you call(s) from browser extension HTML Page\Content Script so same session is shared, i have tried both the ways in a sample and it is working form me, instead of code in background page- make the code in content script or browser action HTML Page.
Let me know if you are still facing problems.
For your question 2)
The following code always fetches current window user is browsing
manifest.json
Ensure you have tabs permission in your manifest.
{
"name":"Inspected Windows Demo",
"description":"This demonstrates Inspected window API",
"manifest_version":2,
"version":"2",
"permissions":["tabs"],
"background":{
"scripts" : ["background.js"]
}
}
background.js
chrome.tabs.query({
"status": "complete", // Window load is completed
"currentWindow": true, // It is in current window
"active": true //Window user is browsing
}, function (tabs) {
for (tab in tabs) { // It returns array so used a loop to iterate over items
console.log(tabs[tab].id); // Catch tab id
}
});
Let me know if you are still unable to get tab id of current window.

Resources