First time using the site to ask a question. I'm new to Chrome Extensions so I'm sure I'm making some stupid mistake. I apologize in advance.
I'm trying to grab the current tab's url and POST it to a URL that will then recieve it and push it into a database. Like my own personal bookmarking service. I've debuged as much as I can, the data is making it all the way to where i'm sending my XHR request.. but the data doesn't come out when I echo it on my server side script. I'm confirming that it's hitting my URL because i'm console logging the output... but again no data is being passed.
BACKGROUND.JS
chrome.runtime.onMessage.addListener(function(request, sender, callback) {
if (request.action == "xhttp") {
var xhttp = new XMLHttpRequest();
var method = request.method ? request.method.toUpperCase() : 'GET';
xhttp.open(method, request.url, true);
xhttp.send(request.data);
xhttp.onload = function() {
callback(xhttp.responseText);
};
return true
}
});
MANIFEST.json
{
"name": "bookmarker",
"version": "0.0.1",
"manifest_version": 2,
"description": "POSTIN BOOKMARKS!",
"homepage_url": "URL",
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},
"permissions": [
"contentSettings",
"cookies",
"tabs",
"geolocation",
"http://*/*"
],
"content_scripts": [
{
"js": ["contentscript.js"],
"matches": ["http://*/*"]
}
],
"browser_action": {
"default_icon": "icons/icon16.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
MAIN.JS
jQuery(function() {
jQuery('.reminded').on('click', function(e){
e.preventDefault();
chrome.tabs.query({currentWindow: true, active: true}, function(tabs){
var url = tabs[0].url,
date = 1391048414,
clientId = 1234
chrome.runtime.sendMessage({
method: "POST",
action: "xhttp",
url: "http://www.API.com/endpoint",
data: {url: url, date: date, clientId: clientId}
}, function(responseText) {
console.log(responseText);
});
});
});
});
Thank you very much in advance for any light anyone can share.
You didn't mention where "main.js" was used, but I guess that it's used in the popup page, right? The popup page can also make cross-domain requests in a Chrome extension when you've declared the permissions in the manifest file. So, just use jQuery.ajax directly to make the cross-domain request:
jQuery.ajax({
type: "POST",
url: "http://www.API.com/endpoint",
data: {url: url, date: date, clientId: clientId},
success: function(data) {
console.log(data);
}
});
For the record, the code in your question is failing because you're trying to submit a JavaScript Object ({url: url, date: date, clientId: clientId} via the .send method of a XMLHttpRequest. This makes no sense, and your server would receive the string [object Object] because of the default serialization of objects.
To get your original code to work, you should use jQuery.param to serialize the form data and xhttp.setRequestHeader("Content-Type", "application/x-www-form-url-encoded");.
Related
manifest.json:
{
"manifest_version": 2,
"name": "chrome-extension-bar",
"description": "Chrome Extension Bar",
"version": "1.0",
"browser_action": {
"default_title": "Chrome Extension Bar",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"service_worker": "background.js"
},
"permissions": [
"<all_urls>",
"tabs"
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com/ https://maxcdn.bootstrapcdn.com ; object-src 'self'"
}
background.js:
console.log("In background.js")
chrome.runtime.onMessage.addListener (
function (request, sender, sendResponse) {
console.log("In listener");
// to send back your response to the current tab
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {fileData: response}, function(response) {
sendResponse({farewell: "goodbye"});
});
});
return true;
}
);
content_script.js:
var current_title_bar = document.getElementById("global-nav");
console.log("Title bar:" + current_title_bar);
current_title_bar.appendChild(extension_bar); // <= GODD, getting here
document.getElementById("idStartButton").addEventListener('click', function () {
console.log('Sending start button message: ' + document.getElementById("idNumSecondsPerPage").value);
chrome.runtime.sendMessage({numSecondsPerPage : document.getElementById("idNumSecondsPerPage").value}, function(response) {
console.log('Got response: ' + response.farewell);
});
});
document.getElementById("idStopButton").addEventListener('click', function () {
console.log('Sending stop button message: ' + document.getElementById("idNumPages").value);
chrome.runtime.sendMessage({numPages : document.getElementById("idNumPages").value}, function(response) {
console.log('Got response: ' + response.farewell);
});
});
The messages seem to be getting sent, but there's I'm not getting anything back from the receiver.
Console log on the page into which the form was injected:
Entering content_script.js
content_script.js:156 Title bar:[object HTMLElement]
content_script:161 Sending start button message: 67
content_script:168 Sending stop button message: 76
Why aren't the messages sent by background.js received by content_script.js? Are they even sent?
If you want to send a message from the background.js script to the content_script.js then you need to set an onMessage listener on the content script.
What you might do in this case is, after receiving the event on the background script, just call the callback function, instead you're sending a new event with a callback function than when it is executed, calls the callback function of the first event.
Instead of doing this:
console.log("In listener");
// to send back your response to the current tab
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {fileData: response}, function(response) {
sendResponse({farewell: "goodbye"});
});
});
just do this
console.log("In listener");
// send back your response
sendResponse({farewell: "goodbye"});
I'm trying to send some data from a Chrome extension content script to a background script to my local python file. It goes from the content script to the background script fine, but when I try to send it to the python file, I just get "POST http://localhost:5000/bootstrap 400 (BAD REQUEST)." Can't figure out what's going on. I'm new to this. Many thanks!
background.js
// Sending messages from background / event page
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.status == 'complete') {
chrome.tabs.query({ active: true }, function(tabs) {
const msg = "Hello from background 🔥";
chrome.tabs.sendMessage(tabs[0].id, { "message": msg });
})
}
});
// Listening to messages page
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(request);
var articleData = request
// Callback for that request
$.ajax({
type: 'POST',
url: 'http://localhost:5000/bootstrap',
data: articleData,
success: function (newArticle){
alert('success');
}
})
sendResponse({ message: "Background has received that message 🔥" });
});
relevant part of fakenews.py
#app.route('/bootstrap', methods=['GET', 'POST'])
def bootstrap():
posted = 1
print ("bootstrap")
global article
if request.method == 'POST':
if not request.form['title'] or not request.form['url'] or not request.form['image_url'] or not request.form['snippet']:
flash('Please enter all the fields', 'error')
else:
article = Article(request.form['title'], request.form['url'], request.form['image_url'],
request.form['snippet'])
db.session.add(article)
try:
db.session.commit()
except exc.SQLAlchemyError:
flash('Article url already exists, failed to post new article')
posted = 0
#return render_template('/error.html', article_url=article.url)
article_list = Article.query.filter_by(url=article.url)
if posted == 1:
flash('Record was successfully added')
else:
db.session.rollback()
article_list = Article.query.filter_by(url=article.url)
article=article_list[0]
print ("article.id=" + str(article.id))
vote_choices = VoteChoice.getVoteChoiceList()
return render_template('/votefor.html', article_id=article.id,
article_title=article.title,
article_url=article.url, vote_choices=vote_choices
)
relevant part of content.js
chrome.runtime.sendMessage({ message: [title, image, url] }, function(response) {
console.log(response);
});
// Listening to messages in Context Script
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
console.log(request);
// Callback
sendResponse({ message: 'Content script has received that message âš¡' })
})
});
manifest.json
{
"manifest_version": 2,
"name": "OrangeBox",
"version": "1.0",
"permissions": ["http://localhost/" ,"tabs"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["contentScript.js"]
}
],
"background": { "scripts": ["jquery-3.4.1.js", "background.js"], "persistent": false },
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["jquery-3.4.1.js", "waitForKeyElements.js", "content.js"]
}
]
}
Problem
In content.js you're sending
message: [title, image, url]
The background.js is just passing this on to fakenews.py, and this is where things break...
fakenews.py is expecting a request object that looks like
{
title: <some time>,
url: <some url>,
image_url: <...>,
snippet: <...>
}
but what it's getting looks like
{
message: [
<some title>,
<some image>,
<some url>
]
}
Fix
The quick fix is changing the content.js line to be
chrome.runtime.sendMessage({"title": title, "image_url": image, "url": url, "snippet": "test"}, function(response) {
Note I'm putting a placeholder for snippet because the backend is expecting a value for it
I want to send custom cookie in POST request to another domain (my domain) from localhost
I set cookie by document.cookie="test=test";
and i can see its set correctly by console.log(document.cookie) , Now When i use following code cookie is not sent though.
$.ajax({
url: 'https://secure.domain.com',
type: 'POST',
data: "hi",
cache: false,
contentType: false,
processData: false,
xhrFields: {
withCredentials: true
},
crossDomain: true
});
I even disabled chrome security by running following command
-args --disable-web-security --user-data-dir
Only following headers are sent
Accept: */*
Content-Type: text/plain;charset=UTF-8
Origin: http://localhost:8888
Referer: http://localhost:8888
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.30 Safari/537.36
Note : This is only for my personal use , so i can disable chrome security or modify anything for my use.
modify chromium source code is bad idea, for this task you can just create extension to modify request headers, and no need argument -disable-web-security
Create folder with name like headers_ext and add the following files
manifest.json
{
"manifest_version": 2,
"name": "Modify Request Headers",
"version": "1.0",
"permissions": [
"webRequest",
"webRequestBlocking",
"<all_urls>",
"tabs",
"webNavigation"
],
"background": {
"scripts": ["background.js"]
}
}
backround.js
function modifyRequestHeaders(request) {
for (var headers = request.requestHeaders, i = 0; i < headers.length; ++i) {
if (headers[i].name.toLowerCase() == 'accept') {
// set Cookie from 'Accept' header value
headers.push({"name" : "Cookie", "value" : headers[i].value});
// normalize 'Accept' header value
headers[i].value = '*/*';
}
}
return {requestHeaders: headers};
}
function modifyResponseHeaders(response) {
for (var headers = response.responseHeaders, i = 0; i < headers.length; ++i) {
if (headers[i].name.toLowerCase() == 'access-control-allow-origin') {
headers.splice(i, 1);
break;
}
}
// Allow cross domain
headers.push({"name": "Access-Control-Allow-Origin", "value": "*"});
return {responseHeaders: headers};
}
var webRequestOptions = {urls: ["<all_urls>"], types: ["xmlhttprequest"]};
chrome.webRequest.onBeforeSendHeaders.addListener(modifyRequestHeaders,
webRequestOptions, ["blocking", "requestHeaders", 'extraHeaders']);
chrome.webRequest.onHeadersReceived.addListener(modifyResponseHeaders,
webRequestOptions, ["blocking", "responseHeaders"]);
Now, in Chrome extension page click Load unpacked extension and locate the directory.
the extension above will only modify xmlhttprequest request headers and use Accept header value for Cookie value, It also modify response header to allow cross domain request by adding header Access-Control-Allow-Origin: *.
It seem for Chrome that DPR, Downlink, Save-Data, Viewport-Width, Width headers is not yet in safe-listed so I use Accept header instead to avoid OPTIONS or Preflight request, because many website doesn't support this. And extraHeaders is filter to allow modify or create Cookie.
For more CORS information read here
Make sure you're using latest Chrome and create request like this
$.ajax({
url: 'https://example.com',
type: 'POST', // or GET or HEAD
headers: {
// it will used for 'Cookie' value by extension
'Accept': "cookieName=cookieValue"
}
});
This behaviour depends on the client - in case of Chrome, Cookie header is forbidden when used with a XMLHttpRequest and it seems that it cannot be overriden by any command line flag.
Looking at Chromium source code, this is the fragment responsible for it:
// "5. Terminate these steps if |name| is a forbidden header name."
// No script (privileged or not) can set unsafe headers.
if (FetchUtils::IsForbiddenHeaderName(name)) {
LogConsoleError(GetExecutionContext(),
"Refused to set unsafe header \"" + name + "\"");
return;
}
This method will be called whenever you will call XMLHttpRequest.setRequestHeader(header, value) with Cookie as header parameter and this is what jQuery's $.ajax({}) uses under the hood.
For more information on why this behavior may be disabled by some clients, see this answer.
Here is the complete list of forbidden header names:
ForbiddenHeaderNames::ForbiddenHeaderNames()
: proxy_header_prefix_("proxy-"), sec_header_prefix_("sec-") {
fixed_names_ = {
"accept-charset",
"accept-encoding",
"access-control-request-headers",
"access-control-request-method",
"connection",
"content-length",
"cookie",
"cookie2",
"date",
"dnt",
"expect",
"host",
"keep-alive",
"origin",
"referer",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"user-agent",
"via",
};
}
I'm having some issues trying to send and receive parameters in javascript and Ajax. I´m sending to /api/getHighScores and that function requires a timestamp for input and the output is a json-file with user information and a value, the highscore. When I receive the response, I want it to print in my Chrome plugin as well.
This is the code right now.
data = {
"startTime": JSON.stringify(1490208166633),
}
function getHighscores() {
$("#knapp2").click(function() {
$.ajax({
type:"GET",
url: "/api/getHighscores",
data:[startTime=1490208166633],
success: function (response){}
});
});
}
{
"manifest_version": 2,
"version": "1.1",
"browser_action": {
"default_icon": "resources/img/clock-icon-png-10763.png",
"default_popup": "popup/popup.html"
},
"background": {
"scripts": ["background/background.js"],
"persistent": false
},
"permissions": [
"activeTab",
"https://ajax.googleapis.com/"
]
}
I've made a few assumptions here about your API structure but this is what I would expect to be.
Typically the
api/getHighScores
endpoint would get all the high scores independent of the time stamp variable.
var data = { startTime: 1490208166633 };
var userInfomation;
$("#knapp2").click(function() {
$.ajax({
type:"GET",
url: "/api/getHighscores/" + data.startTime,
success: function (userInfo)
{
userInfomation = userInfo;
}
error: function (response)
{
// you should deal with the error case as well
// it could give you more information about whats going wrong.
}
});
});
function getHighscores() {
$("#knapp2").trigger("click");
}
If you are actually sending data under a GET method.
var data = { startTime: 1490208166633 };
var userInfomation;
$("#knapp2").click(function() {
$.ajax({
type:"GET",
url: "/api/getHighscores",
data: JSON.stringify(data);
success: function (userInfo)
{
userInfomation = userInfo;
}
});
});
My contentScript sends a message to backgroundScript which opens a popup/panel/window. This panel loads an external web page.
I'm injecting some javascript in the panel to interact with it.
What I'm trying do achieve now is to send data from the panel to the 'main page' (contentScript).
I have successfully managed to send messages from the panel to the backgroundScript.
What I don't understand/know is how to pass the data from the backgoundScript to the contentScript.
Updated script from #Haibara Ai's comment
manifest.js
{
"name": "My extension",
"version": "0.1",
"manifest_version": 2,
"description": "description",
"permissions": ["activeTab", "tabs","http://*/*","https://*/*"],
"content_scripts": [
{
// Change 'matches' attribute to load content
// script only in pages you want to.
"matches": ["SomeUrl"],
"js": ["jquery.min.js", "contentscript.js", "notifier.js"]
}
],
"background": {
"scripts": ["eventPage.js"],
"persistent": false
}
}
contentscript.js
$(document).ready(function() {
var link = document.getElementById('inputLink');
// onClick's logic below:
link.addEventListener('click', function() {
chrome.runtime.sendMessage({
action: 'createWindow',
url: $('input[name=link]').val()
}, function(message) {
console.log(message);
})
});
});
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
alert('a');
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});
eventPage.js
chrome.runtime.onMessage.addListener(function(request) {
if (request && request.action === 'createWindow' && request.url) {
chrome.windows.create(
{
url: request.url,
focused: true,
incognito: true,
type: "panel"
}, function (newWindow) {
chrome.tabs.executeScript(newWindow.tabs[0].id, {
file: "jquery.min.js"
}, function() {
chrome.tabs.executeScript(newWindow.tabs[0].id, {
file: "htmlselection.js"
});
});
chrome.tabs.insertCSS(newWindow.tabs[0].id, {file: "htmlselection.css"});
});
} else {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs){
console.log(chrome.tabs);
chrome.tabs.sendMessage(tabs[0].id, {action: "SendIt"}, function(response) {});
});
}
});
htmlselection.js (injected in the popup/panel/window)
[...]
//After a click within the popup/panel/window
chrome.runtime.sendMessage({ text: 'test' });
[...]
Thank you for your help.
Updated
If you want to send message inside chrome.runtime.onMessage, just use the callback sendResponse or use sender.tab.id as tabId to send back the message.
And there are other problems with your code:
Since you are use Programming injection to inject script, you should declare them in "web_accessible_resources" in manifest.json
"web_accessible_resources": [
"htmlselection.js",
"jquery.min.js",
"htmlselection.css"
]
In your contentscript.js, just remove message part, since you didn't receive anything in this script.
For eventpage.js, use sendResponse instead of tab.query.
else {
console.log("2");
sendResponse({ action: "SendIt" });
}
Previous
Take a look at Message Passing, you could send a message from background page using the following code snippets:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});
Fixed version of the snippet:
chrome.tabs.query({active: true, currentWindow: false}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});
Set the currentWindow:false and it worked.