Unable to use getBackgroundPage() api from a devtools extension - google-chrome-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.

Related

Using tabs.executeScript in iframe

I'm trying to implement an extension that can auto fill all fields in an application website. Currently it works great in main frame, but doesn't work if the fields are in a iframe.
I'm allowing users to either use content script to auto inject JS or click a button to inject JS manually.
The problem I have is my JS is not injecting into iframe even I set allFrames to true, but content script work.
"content_scripts" : [
{
"matches" : ["https://*/my_url/*"],
"js" : ["/auto_fill.js"],
"all_frames": true
}
]
This content scripts work fine, JS is being injected. However,
my_button.addEventListener('click', () => {
chrome.tabs.executeScript(null, {
file : "/auto_fill.js",
allFrames : true
})
});
This executeScript doesn't work, nothing happen.
The iframe I'm trying to work with is kinda a dynamic iframe (where I have to click few buttons to load and navigate to the application site I want)
Do I have to find out the iframe id or tab id in order for this to work, please provide some hints.
Please let me know if I'm not making myself clear.
Thank you so much for your time.
As the comments revealed, the extension is using the activeTab permission without host URL patterns (just as the documentation suggests), but this permission only grants access to the tab's main URL. When an iframe points to a different URL origin, it won't be granted by activeTab, which is an intentional restriction enforced since Chrome 45, see https://crbug.com/826433.

How to execute a callback on the devtools page from page's JS?

I am trying to write a Chrome devtools extension for a JS SDK we have developed. This SDK has an API addEventListener (the events are not DOM events) that I would like to use to be able to display all the events that are being published in the devtools panel I made.
Basically I would like to be able to have the following code in my devtools page script :
chrome.devtools.inspectedWindow.eval(
"mySDKonTheContentPage", function(result, isException){
mySDK =result;
mySDK.addEventListener("myEvent", function(){
doSomethingInDevtoolsUI();
});
});
Since content scripts don't have access (do they?) to the page's JS objects, I don't really know where to start.
In the script on your page, you can use window.postMessage to send your data to the content script. From there you can set up communication between the content script and the DevTools panel via a background page.
See: Messaging from Injected Scripts to the DevTools Page and Messaging from Content Scripts to the DevTools Page for examples of this in the documentation.

Message listener only begins firing after browserAction has been clicked

It appears that a message from content to background only begins firing after backgroundAction has been run at least once.
In the below code example, a click on browserAction turns the page red, and a click on the page body turns the page blue (via a message sent by the content script).
If I click the page body first, nothing happens. It only begins working after I've clicked browserAction once.
Why is this happening and how can I make it so the message listener fires without having browserAction run first?
Any help would be greatly appreciated!
content.js
$(function(){
$('body').on('click', function() {
// Send a message to background.js
chrome.runtime.sendMessage(true, function(response){
console.log(response);
});
});
});
background.js
// Make background red when browserAction is cliked
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.executeScript( {
code: 'document.body.style.backgroundColor="red"'
});
});
// Make background blue when any message is received
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse){
chrome.tabs.executeScript( {
code: 'document.body.style.backgroundColor="blue"'
});
return true;
})
As always in such cases use the debugger. The error I'm seeing here in the extension's background page console which can be opened on chrome://extensions page:
Unchecked runtime.lastError while running tabs.executeScript: Cannot access contents of url "...". Extension manifest must request permission to access this host.
When runtime.onMessage is executed after a message from the content script Chrome doesn't know that executeScript was initiated by a user action so the code is blocked.
As for browserAction.onClicked, it is always invoked on user's interaction so "permissions": ["activeTab"] is sufficient for the code executed in the eventjob context of the issued click. And it creates temporary permission to modify the active tab, see the documentation:
The following user gestures enable activeTab:
Executing a browser action
Executing a page action
Executing a context menu item
Executing a keyboard shortcut from the commands API
Accepting a suggestion from the omnibox API
Solution #1 (the best) would be to avoid injecting any code from the background script and do everything in the content script based on messages from the background script.
Solution #2 would be to add the required permission to manifest.json:
"permissions": ["activeTab", "<all_urls>"]

Opening and writing to a new window from a Google Chrome extension sandbox page

(Cross posted here)
Hi,
I have a sandboxed page (specified in my manifest) which is loaded into an iframe in my extension's background page. From within my sandboxed page, I'd like to open a new window and write to it, i.e.:
var win = window.open(); win.document.write('<p&gtHello!</p>');
This works from my extension's background page and from regular web pages, but when invoked from either content scripts or my sandboxed page, the window opens, but I cannot access the win object (it's defined, but empty---console.log outputs "Window {}").
I assume this is due to same-origin policies (with every window being given a uinque-origin within the sandboxed environment). However, since the window opens an about:blank page, I'm confused why this would matter.
Is this a feature? Is there a parameter I can add to my manifest to avoid this? And does anyone know of work-arounds that don't involve using postMessage back to my background page? My ideal solution is to have my sandboxed script open a new window and interact with it directly, not with message passing.
I can provide a full example if necessary, but I'm hoping someone might just know off the top of their head. I'm running Chrome 24.0.1312.57 on Mac and 24.0.1312.68 on Ubuntu if that helps.
Thanks,
Hank
Content scripts, by definition, are part of external regular web pages you load so I'm not really sure how your script could work on a "regular web page" but not the content script. Do you mean the code works when you embed it in your own pages, but not in other pages via the content script?
Regardless, if the script is working properly from your background page, you could always try messaging. (more here: http://developer.chrome.com/extensions/messaging.html)
Script for your sandbox/contentscript:
//send message to background page
chrome.extension.sendMessage({todo: "newWindow"});
In background page:
//create a listener
chrome.extension.onMessage.addListener(
function(request, sender) {
if (request.todo === "newWindow") {
//do your stuff here
var win = window.open(); win.document.write('<p&gtHello!</p>');
}
});
Per the cross-post here, the issue is indeed that the opened window is given a unique origin. This was intentional as the members of the standards working group (SWG) felt that it would be more secure to not make an exception for about:blank pages where they inherit the sandbox's origin.
However, to get around this issue, at least for my purposes, I can use the following method. First, forget sandboxing. This workaround uses an iframe embedded in a background page with the src url set to data:text/html,.... This gives a unique origin to the iframe so it's no longer in extension space. That means eval can be used and chrome apis cannot be accessed. Unlike in a sandbox, windows opened from the iframe share that same origin as the iframe, allowing created windows to be accessed. For example:
In a background html page:
<html>
<head>
...
<script src="background.js"></script>
...
</head>
<body>
...
<iframe id="myframe"></iframe>
...
</body>
</html>
In background.js:
...
document.getElementById('myframe').setAttribute('src', 'data:text/html,'+
encodeURI('<html><head>'+
'<script src='+chrome.extension.getURL('jquery.js')+'></script>'+
'<script src='+chrome.extension.getURL('myscript.js')+'></script>'+
...
'</head><body></body></html>'
));
...
In myscript.js
jQuery(document).ready(function(){
...
// To receive messages from background.js.
window.addEventListener('message', function(e){ ... } );
// To send messages to background.js.
parent.postMessage({...}, '*');
// To open AND ACCESS a window.
var win = window.open();
win.document.write('Hello'); // Fails in sandbox, works here.
// Eval code, if you want. Can't do this from an extension
// page loaded normally unless you allow eval in your manifest.
// Here, it's okay!
eval( 'var x = window.open(); x.document.write("Hi!")' );
// No chrome apis.
chrome.log( chrome.extension ); // -> undefined
chrome.log( chrome.windows ); // -> undefined
// No direct access to background page (e.g., parent).
chrome.log( parent ); // -> Window with no properties.
...
});

Google Chrome Extension- Messages + call to function between popup page and content script

In a Google Chrome extension I want to pass messages between a content script and a popup page. I also want a function in the content script to be fired when a signal to do this is sent by the popup page to the content script.
The section in Google Chrome Extensions developer docs for messages, states that messages can be passed "from your content script to an extension page, or vice versa" (quoted from http://code.google.com/chrome/extensions/messaging.html )
Does this mean that I can send/receive messages between the content script and the popup page? Because the usage I have seen is for communication between background page and content script.
Or do I have to set up a 3 - way message passing route-- viz.
content script <--> background page <--> popup page. And if this is the case, how do I set up messaging between background page <--> popup page?
And how do I fire a function in the content script, after sending signal to content script from the popup page? Does this also require background script to be present?
Content scripts can only send a message to the background page. If you want to send a message from a content script to a popup, you have to:
Send the message from the Content script to the background page.
chrome.runtime.sendMessage( ...message... , function() { /*response*/ });
Receive the message at the background.
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) { ... });
Pass the message to the popup. To get a reference to the global window object of the popup, use chrome.extension.getViews. Note: Unlike most of the other chrome APIs, this method is synchronous:
var popupWindows = chrome.extension.getViews({type:'popup'});
if (popupWindows.length) { // A popup has been found
// details is the object from the onMessage event (see 2)
popupWindows[0].whatever(message, sendResponse);
}
In the popup, define the whatever method.
function whatever(message, sendResponse) {
// Do something, for example, sending the message back
sendResponse(message);
}
When sendResponse (4) is called, details.sendResponse (see 3) is called. This, in turn, calls function() { /*response*/ } from 1.
Note: chrome.runtime.sendMessage/onMessage is only supported in Chrome 26+. Use chrome.extension.sendMessage if you want to support older browsers as well.

Resources