How to send information from window to the devtool in chrome extension - google-chrome

In my app I have a namespaced application and there's information or metadata myApp carries on it that might be useful to devpane.
window.myApp = new App();
How can I relay or send the following information to the devtool.js?
window.myApp.metadata; // information
And can I send a request from the devtool with a function that customizes the serialization of that metadata?
I've seen similar posts with the solution below, which returns null when I tried it.
chrome.devtools.inspectedWindow.eval("window.myApp", {
useContentScriptContext: true
})
NOTE: If a sample template can be provided that would be wonderful.

This is how I've solved this. It feels more complicated than necessary, but it does work.
In the context of the inspected window
Based on this question.
This is where you've got access to window.myApp.metadata and can put it into the data object.
var event = new CustomEvent("RebroadcastExtensionMessage", {data: ""});
window.dispatchEvent(event);
In the content script
This just forwards the data to the background page.
window.addEventListener("RebroadcastExtensionMessage", function(evt) {
chrome.runtime.sendMessage(evt)
}, false);
In the background page
Based on the Chrome docs.
chrome.runtime.onConnect.addListener(function(devToolsConnection) {
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
devToolsConnection.postMessage(request)
});
})
In devtools.js
var backgroundPageConnection = chrome.runtime.connect({
name: "devtools-page"
});
backgroundPageConnection.onMessage.addListener(function (message) {
// Data has arrived in devtools page!!
});

Related

Add JQuery reference to custom function

I want to test calling an API in a custom function for Google Sheets. code.gs is as follows:
function TApi(input) {
var url = "https://api.nytimes.com/svc/search/v2/articlesearch.json";
url += '?' + $.param({
'api-key': "cdaa59fea5f04f6f9fd8fa551e47fdc4",
'q': "MIT"
});
$.ajax({
url: url,
method: 'GET',
}).done(function(result) {
return result;
console.log(result);
}).fail(function(err) {
throw err;
});
}
But when I call =TAPI() in a sheet cell, it returns an error ReferenceError: "$" is not defined. (line 22). I guess we need to add a link to JQuery. Does anyone know how to do this?
You can only use JQuery on client side scripts which use the HTML service. It is not available server side. There is a blurb about using it in the HTML Services Best Practices.
It's not possible. You must build either a web app or custom UI (sidebar or dialog) using HtmlService and do the processing on the client. Because your code runs on Google servers, there are no 'window' or 'document' objects. DOM and BOM are only accessible on the client.
In fact, feel free to do the following little experiment. Open your browser console (I'm using Chrome developer tools) and type in
console.log(this); //this logs global object
Here's the output
This is the 'window' object used by jQuery for navigating the DOM tree. jQuery is simply a JS library that builds on top of existing DOM manipulation methods and CSS selectors.
Next, open any GAS file, run the following function and check the Logs (Ctrl + Enter):
function test() {
Logger.log(this);
}
And here's the output.
As you can see, the global object in this context consists of Google-defined pseudo classes (GAS services).
You can use urlFetch app. Try the below snippet
function fetchURL() {
try {
var url = "https://api.nytimes.com/svc/search/v2/articlesearch.json";
url += '?api-key=cdaa59fea5f04f6f9fd8fa551e47fdc4&q=MIT';
var params = {
'method': 'get',
'contentType': 'application/json',
'muteHttpExceptions': true
}
var response = UrlFetchApp.fetch(url, params);
Logger.log(response)
} catch (e) {
Logger.log(e)
}
}

Chrome extension create new tab and send message from popup.js to content script of new tab

I am developing a chrome extension where my popup.js receives a message from a content script on the current page and creates an array. Then on a button press, popup.js creates a new tab (which has a content script running) and sends that content script a message containing the array.
My popup.js:
//this message is sent from a different content script (for current page), not shown here
chrome.runtime.onMessage.addListener(function(request, sender) {
if (request.action === "getSource") {
var arr = JSON.parse(request.source);
//create new tab
chrome.tabs.create({url: "newtab.html"}, function(tab){
//send message to new tab
chrome.tabs.sendMessage(tab.id{
action: "getDataArray",
source: JSON.stringify(arr)
});
}
});
newtab-contentscript.js:
$(document).ready( function() {
chrome.runtime.onMessage.addListener(function(request, sender) {
if (request.action === "getDataArray") {
$("#result").html(JSON.parse(request.source));
}
});
newtab.html:
<script src="newtab-contentscript.js"></script>
Problem: The newtab-contentscript.js never seems to receive the message.
Are the any mistakes with how I am creating a tab or sending the message. Do you have any suggestions to how to fix this issue?
As we discussed in the comments, I guess maybe $(document).ready is too late to receive messages from chrome.tabs.sendMessage, you can test it by comparing timestamps of console.log inside the callback and on the first line of the new tab's content scripts, as #wOxxOm mentioned.
I just suggest moving message logic to background (event) page and starting the message passing from newtab-contentscript.js, in which you could control when to start sending the message.
A sample code
background.js
let source = null;
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
// sent from another content script, intended for saving source
if(request.action === 'putSource') {
source = request.source;
chrome.tabs.create({ url: 'newtab.html' });
}
// sent from newtab-contentscript, to get the source
if(request.action === 'getSource') {
sendResponse({ source: source });
}
});
newtab-contentscript.js
chrome.runtime.sendMessage({action: 'getSource'}, function(response) {
$('#result').html(response.source);
});

Repeatedly Grab DOM in Chrome Extension

I'm trying to teach myself how to write Chrome extensions and ran into a snag when I realized that my jQuery was breaking because it was getting information from the extension page itself and not the tab's current page like I had expected.
Quick summary, my sample extension will refresh the page every x seconds, look at the contents/DOM, and then do some stuff with it. The first and last parts are fine, but getting the DOM from the page that I'm on has proven very difficult, and the documentation hasn't been terribly helpful for me.
You can see the code that I have so far at these links:
Current manifest
Current js script
Current popup.html
If I want to have the ability to grab the DOM on each cycle of my setInterval call, what more needs to be done? I know that, for example, I'll need to have a content script. But do I also need to specify a background page in my manifest? Where do I need to call the content script within my extension? What's the easiest/best way to have it communicate with my current js file on each reload? Will my content script also be expecting me to use jQuery?
I know that these questions are basic and will seem trivial to me in retrospect, but they've really been a headache trying to explore completely on my own. Thanks in advance.
In order to access the web-pages DOM you'll need to programmatically inject some code into it (using chrome.tabs.executeScript()).
That said, although it is possible to grab the DOM as a string, pass it back to your popup, load it into a new element and look for what ever you want, this is a really bad approach (for various reasons).
The best option (in terms of efficiency and accuracy) is to do the processing in web-page itself and then pass just the results back to the popup. Note that in order to be able to inject code into a web-page, you have to include the corresponding host match pattern in your permissions property in manifest.
What I describe above can be achieved like this:
editorMarket.js
var refresherID = 0;
var currentID = 0;
$(document).ready(function(){
$('.start-button').click(function(){
oldGroupedHTML = null;
oldIndividualHTML = null;
chrome.tabs.query({ active: true }, function(tabs) {
if (tabs.length === 0) {
return;
}
currentID = tabs[0].id;
refresherID = setInterval(function() {
chrome.tabs.reload(currentID, { bypassCache: true }, function() {
chrome.tabs.executeScript(currentID, {
file: 'content.js',
runAt: 'document_idle',
allFrames: false
}, function(results) {
if (chrome.runtime.lastError) {
alert('ERROR:\n' + chrome.runtime.lastError.message);
return;
} else if (results.length === 0) {
alert('ERROR: No results !');
return;
}
var nIndyJobs = results[0].nIndyJobs;
var nGroupJobs = results[0].nGroupJobs;
$('.lt').text('Indy: ' + nIndyJobs + '; '
+ 'Grouped: ' + nGroupJobs);
});
});
}, 5000);
});
});
$('.stop-button').click(function(){
clearInterval(refresherID);
});
});
content.js:
(function() {
function getNumberOfIndividualJobs() {...}
function getNumberOfGroupedJobs() {...}
function comparator(grouped, individual) {
var IndyJobs = getNumberOfIndividualJobs();
var GroupJobs = getNumberOfGroupedJobs();
nIndyJobs = IndyJobs[1];
nGroupJobs = GroupJobs[1];
console.log(GroupJobs);
return {
nIndyJobs: nIndyJobs,
nGroupJobs: nGroupJobs
};
}
var currentGroupedHTML = $(".grouped_jobs").html();
var currentIndividualHTML = $(".individual_jobs").html();
var result = comparator(currentGroupedHTML, currentIndividualHTML);
return result;
})();

Sending message from popup.js in Chrome extension to background.js

What is the proper way to send a message (and get a response) to background.js from popup.js in a Chrome extension? Every method I try ends up with an error that:
"Port: Could not establish connection. Receiving end does not exist."
I would prefer to use chrome.extension.sendMessage() over chrome.extension.connect() with port.postMessage(), but neither method seems to have worked.
What I am trying to do is wire a button in the popup.html to call into some javascript in popup.js which calls back to background.js in an effort to get info about the currentTab() that was topMost (ie:to get the current URL string to show in the popup.html)
Right now in popup.js (wired to the action of the button) I have:
function getURL()
{
chrome.extension.sendMessage({greeting: "GetURL"},
function(response) { tabURL = response.navURL });
$("#tabURL").text(tabURL);
}
In background.js I have:
chrome.extension.onMessage.addListener( function(request,sender,sendResponse)
{
if( request.greeting == "GetURL" )
{
var tabURL = "Not set yet";
chrome.tabs.getCurrent(function(tab){
tabURL = tab.url;
});
sendResponse( {navURL:tabURL} );
}
}
Any ideas?
Just to clarify, we talking about communication between popup page from browserAction and background script?
Anyway you have quite a few errors in your code.
First your totally ignore the fact that all callbacks in chrome api are asynchronous.
In background page
var tabURL = "Not set yet";
chrome.tabs.getCurrent(function(tab){
tabURL = tab.url;
}); //this will be invoked somewhere in the future
sendResponse( {navURL:tabURL} );
//navUrl will be always Not set yet because callback of getCurrent hasn't been called yet
Same in popup.js
chrome.runtime.sendMessage({greeting: "GetURL"},
function(response) { tabURL = response.navURL });//callback will be invoked somewhere in the future
$("#tabURL").text(tabURL)//tabURL will display something totally different from what you have been expected
Second error is that chrome.tabs.getCurrent doesn't give you the current tab selected by user in main window. The docs says:
Gets the tab that this script call is being made from. May be
undefined if called from a non-tab context (for example: a background
page or popup view).
So you will get undefined for all of your requests, because you call it in background page. What you need to do is to use method chrome.tabs.query to obtain currently active tabs.
So after fixing all problems new code should look something like this:
background.js
chrome.runtime.onMessage.addListener( function(request,sender,sendResponse)
{
if( request.greeting === "GetURL" )
{
var tabURL = "Not set yet";
chrome.tabs.query({active:true},function(tabs){
if(tabs.length === 0) {
sendResponse({});
return;
}
tabURL = tabs[0].url;
sendResponse( {navURL:tabURL} );
});
}
}
popup.js
function getURL() {
chrome.runtime.sendMessage({greeting: "GetURL"},
function (response) {
tabURL = response.navURL;
$("#tabURL").text(tabURL);
});
}

backbone.js fetch json success will not hit

i use fetch from backbone.js to load a json model but success will not hit.
var DialogModel = Backbone.Model.extend({
url : function() {
return '/messages/getDialog';
},
parse : function(res) {
return res.dialog;
}
});
var DialogView = Backbone.View.extend({
el: $("#page"),
initialize: function() {
var onDataHandler = function() {
this.render();
};
this.model = new DialogModel();
this.model.fetch({ success : onDataHandler});
},
render: function(){
var data = {
dialogModel : this.model
};
var form = new Backbone.Form({
model: data
});
$(this.el).html(form.render().el);
}
});
What happens now:
DialogView initialize is called.
this.model.fetch is called but the onDataHandler function will not be hit if success.
/messages/getDialog throws a json file back.
The json file is loading well as i can see in the network browser.
Thanks for your help!
Oleg
The problem you're having is due to a typical JS gotcha and not related to Backbone itself. Try
var that = this;
this.model.fetch({
success : function () {
that.render();
}
});
The way you're currently passing onDataHandler is problematic as it will cause this to refer to the global object instead of the DialogView, when the function is called.
This fiddle demonstrates the problematic version vs one that works.
(You may also want to take a look at JS strict mode which can shield you from this type of errors.)
Even better is to listen for an event:
this.model.on("sync", this.render).fetch();
I ran across this question while looking for something else, but the currently accepted answer drives me nuts. There's no good reason to be sprinkling this and that all over your code. Backbone (underscore) includes a context parameter that you can bind to.
that = this makes no sense. If you must implement obsolete 2007-era Crockford patterns, then say var self = this. Saying that = this is like saying left = right. Everyone Stop.