As you know, when send $.ajax(..) request to another domain (cross-domain), most browser throw exception like:
XMLHttpRequest cannot load http://mysite.com/test.php. Origin
http://127.0.0.1:8888 is not allowed by Access-Control-Allow-Origin.
I am creating chrome extension and it should send a request to my website. First , i expected to see above message,too. But i confused when i see it worked fine.
First, It’s seem good, it’s working and i have what i want. But it can be horrible. Every one can use such way (only a simple script) to attack my site and grab its data.
Of course, grabbing could be happen in other ways, too.
I am new in api programing and chrome extension. Do anyone may show me the way?
manifest.json
{
"manifest_version": 2,
"name": "MyTestExtension",
"description": "this extension is for test",
"version": "1.0",
"icons": {
"128": "icon.png"
},
"browser_action": {
"default_icon": "icon.png"
},
"permissions": [
"tabs" ,
"*://*/*"
],
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["jquery-1.7.2.min.js","content_script.js"],
"run_at": "document_end"
}
]
}
content_script.js
$(document).ready(function(){
$('html').mouseup(function() {
var selectedText = getSelectedText();
if(selectedText > ''){
my_syncTest(selectedText) // here : selected test send to my site
}
});
function getSelectedText() {
if (window.getSelection) {
var selection = window.getSelection().toString();
if(selection.trim() > ''){
return selection;
}
} else if (document.selection) {
var selection = document.selection.createRange().text;
if(selection.trim() > ''){
return selection;
}
}
return '';
} });
function my_syncTest(word){
var qs = 'word='+word+'&header=555&simwords=1';
$.ajax(
{
type: "POST",
url: 'http://mysite.com/test.php',
dataType: 'json',
data : qs,
success:function(res){
console.log(res.success +" - "+ res.idWord + " - " + res.header +" - " + res.meaning);
}});
}
XMLHttpRequests from your extension work because you defined these permissions in the manifest:
"permissions": [
"*://*/*"
]
When a user installs your extension, he is informed that this extension can access his data on all sites. I prefer only including the exact site you need instead of wildcards.
http://developer.chrome.com/extensions/xhr.html
This mechanism is to protect the user, not to protect your site. If you don't want everybody to use your API, use API-keys, or look into oAuth:
http://en.wikipedia.org/wiki/OAuth
If you want to learn more about cross origin requests:
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
Related
To begin, I would like to note that I am a complete extension noob, so please forgive me if this is a silly question. I have been working on this for a couple days and have found nothing on the web that helps with this particular problem.
Here is what I am trying to do:
Say that a user is browsing a site that displays part numbers and info. Each time the user clicks on something from the site, the site loads new information and the URL changes. The extension then grabs the UID (unique identifier) from the URL and searches a database for that UID. If the UID is found in the database, a message is returned to the extension. From here, I would like to write a div to the page saying "This part is already found in the database".
The problem here appears to be that the page changes, but it's not a new page... it's just new information. I can get the extension to write to the page on initial load, but when the page changes, nothing is written.
Here is the relevant code that I am using:
Manifest
{
"manifest_version": 2,
"name": "My Extension",
"version": "1.0",
"description": "An extension to write to a page",
"icons": {
"128": "icon128.png",
"48": "icon48.png",
"16": "icon16.png"
},
"browser_action": {
"default_icon": "icon16.png",
"default_popup": "popup.html"
},
"background": {
"scripts":["./js/jquery.min.js","./js/extension-check.js"]
},
"content_scripts": [{
"js": ["./js/jquery.min.js","./js/extension-content.js"],
"matches": ["https://www.example.com/*"]
}],
"permissions": [
"activeTab",
"tabs",
"http://*/*",
"https://*/*"
]
}
The popup.html does nothing but give some buttons that the user can click and go to some sites. It has no relevant code.
extension-check.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
if (tabs === undefined || tabs[0] === undefined) return;
var url = tabs[0].url;
if (url.indexOf("example.com/specific-directory") > 0) {
var part_url = // get part of the url
var search_url = 'myexample.com/searching?' + part_url;
$.ajax({
url: search_url,
type: 'post',
dataType: 'JSON',
success: async function(data, textStatus, jqXHR)
{
if (Object.keys(data).length != 0 && data.constructor != Object) {
// Because this script is running in the
// background, I need to send the command to
// something that has permissions to write to the page
chrome.tabs.executeScript(tabId, {
file: './js/extension-content.js'
}, function() {
chrome.tabs.sendMessage(tabId, {parameter: data});
});
}
},
error: function(jqXHR, textStatus, errorThrown)
{
console.log(jqXHR);
console.log(textStatus);
console.log(errorThrown);
}
});
}
}
});
});
extension-content.js
(function() {
chrome.runtime.onMessage.addListener(function(message) {
var receivedParameter = message.parameter;
alert ("In this - " + receivedParameter);
$(".core").prepend("<div>This is a test - " + receivedParameter + "</div>");
});
})();
NOTE: If I change this page to:
$(document).ready(function() {
$(".core").prepend("<div>This is a test</div>");
});
The div IS written to the page. But, this is written to the page once and never changes or goes away. This would display incorrect information.
The ajax is working perfectly... The alert (which is only there for a test) does display and displays with the passed parameter. However, NOTHING is written to the page.
Can someone please help me? In addition, if I have something wrong in the manifest (such as permissions), I would be ecstatic for some feedback. Again, noob at this, so I may have added that I don't need, or don't have things that I do need.
Thanks in advance.
I try to create a WebExtension which do some things if it's on a specific site in a specific folder. I tried to recognice the site and the subfolder like this:
var on = true;
//This function checks if the current site is Google after something was searched
function check_if_engine() {
var loc = window.location.href;
if (loc.includes("google")) {
alert("Includes google");
if (log.includes("search?")) {
return true;
}
return false;
}
return false;
//...
function start_all() {
if (on) {
alert("Addon activated!");
if (check_if_engine()) {
alert("Website is Google");
//...
}
//Checks if the tab loaded a new URL
browser.tabs.onUpdated.addListener(start_all);
mainfest.json:
{
"manifest_version": 2,
"name": "Is this a Google Search?",
"version": "1.0",
"description": "This Addon tells you if you are on Google Search Page",
"icons": {
"48": "icon/icon.png"
},
"browser_action": {
"default_icon": "icon/icon.png",
"default_title": "Is this a Google search?"
},
"permissions": [
"activeTab"
],
"background": {
"scripts": ["main.js"]
}
}
There aren't any other background/content scripts.
But if I tried it, there was no message, which said that "Includes google" and the returned value was "false" even if I loaded the Google Page (Instant Search disabled) and searched for something. May you can help me.
Thanks
If you use document.URL instead of location.href it should work
if(window.document.URL == "http://example.org"){
// code
}
The web you want to compare (http://example.org) always includes the protocol.
You can see the reference in the Mozilla Docs: https://developer.mozilla.org/es/docs/Web/API/Document/URL or in W3Schools: https://www.w3schools.com/jsref/prop_doc_url.asp
I used a content script which is activated at the domains specified in the manifest.json. It sends a dummy message to the background script via browser.runtime. Then it sends a message to the user
I'm building a chrome extension which communicates with a nodejs server through websockets. The point of it is to track browsing history with content. It all seems to work, but occasionally (30% of the time) the callback in a function passed to onMessage.addListener doesn't fire correctly. Let me show you the code:
background.js
var socket = io('http://localhost:3000/');
var tabLoad = function (tab) {
socket.emit('page load', tab);
};
var tabUpdate = function (tabid, changeinfo, tab) {
var url = tab.url;
if (url !== undefined && changeinfo.status == "complete") {
tab.user_agent = navigator.userAgent;
tab.description = '';
tab.content = '';
socket.emit('insert', tab);
}
};
socket.on('inserted', function(page){
socket.emit('event', 'Requesting page content\n');
//page = {tab: page, id: docs._id};
chrome.tabs.sendMessage(page.tab_id, {requested: "content", page: page}, function(data) {
socket.emit('content', data);
});
});
try {
chrome.tabs.onCreated.addListener(tabLoad);
chrome.tabs.onUpdated.addListener(tabUpdate);
} catch(e) {
alert('Error in background.js: ' + e.message);
}
content script - public.js
var messageHandler = function(request, sender, sendContent) {
if (request.requested == "content") {
var html = document.getElementsByTagName('html')[0].innerHTML;
var data = {
content: html,
page: request.page
};
sendContent(data);
return true;
}
};
chrome.extension.onMessage.addListener(messageHandler);
The problem is that sometimes data in sendContent is undefined, while sometimes it is alright. Any ideas how to debug this or what i'm doing wrong?
I've tried replacing document.getElementsByTagName('html')[0].innerHTML with a hardcoded 'test' string, but that didn't help.
Pages like youtube/wikipedia seem to never work, while facebook/google works.
Edit: The sendContent callback does fire 100% of the time it's just that the data passed to it is undefined.
Edit: Here's the manifest file
{
"manifest_version": 2,
"name": "Socket test",
"description": "sockets are cool",
"version": "1.0",
"permissions": [
"http://st-api.localhost/",
"http://localhost:3000/",
"tabs",
"background",
"history",
"idle",
"notifications"
],
"content_scripts": [{
"matches": ["*://*/"],
"js": ["public/public.js"]
//"run_at": "document_start"
}],
//"browser_action": {
// "default_icon": "logo.png",
// "default_popup": "index.html"
//},
"background": {
//"page" : "background.html",
"scripts": ["socket-io.js", "background.js"],
"persistent": true
}
}
First off, your understanding that sendContent is executed 100% of the time is wrong.
As established in the comments, the sendMessage callback also gets executed when there was an error; and this error is, in your case, "Receiving end does not exist"
The error lies in your manifest declaration of the content script. A match pattern "*://*/" will only match top-level pages on http and https URIs. I.e. http://example.com/ will match, while http://example.com/test will not.
The easiest fix is "*://*/*", but I would recommend the universal match pattern "<all_urls>".
With that fixed, there are still a couple of improvements to your code.
Replace chrome.extension.onMessage (which is deprecated) and use chrome.runtime.onMessage
Modify the sendMessage part to be more resilient, by checking for chrome.runtime.lastError. Despite the wide permission, Chrome still won't inject any content scripts into some pages (e.g. chrome:// pages, Chrome Web Store)
Make sure you use "run_at" : "document_start" in your content script, to make sure onUpdated with "complete" is not fired before your script is ready.
I have a background script in my extension that creates a context menu item and handles it. When it is clicked, a cookie is created with specific details. Here is the source for that file:
script.js
function createC() {
var x = 1;
var y = 2;
//Create Cookie
document.cookie = document.URL + "=" + " " + x + " " + y + " ; " + 4102444799;
console.log("Cookie Created.");
}
chrome.contextMenus.create({
title: "Create Cookie",
contexts:["all"],
onclick: createC,
});
Obviously the variables used in it are for testing. When I run document.cookie; in the console, the cookie does not appear. I have tried using the chrome.cookies API and had the same issue.
Does the cookie not appear because it is created in the background script? I am trying to set it on the current tab the user is on, not the background page itself.
manifest.json
{
"manifest_version": 2,
"name": "MyExtension",
"description": "Do stuff",
"version": "0.1",
"icons": { "16": "icon.png",
"48": "icon.png",
"128": "icon.png" },
"options_page": "options.html",
"permissions": [
"tabs", "<all_urls>", "contextMenus", "cookies"
],
"background": {
"scripts": ["script.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["nav.js"]
}
]
}
In background script, 'document' is not for the current page, but for the extension background page(chrome-extension://[your extension id]/bacground.html). So, you can't use 'document.cookie', you need to try chrome.cookies.get like this:
/**
* Create cookie for the special page
* #param {Object<key, value>} detail
* #param {Function=} opt_callback
*/
function createCookie(detail, opt_callback) {
chrome.cookies.set(detail, opt_callback);
}
You need to use javascript code in specific tab if you want to use the document the current page instead of background.html.
This can be done by function executeScript, your syntax is :
chrome.tabs.executeScript( tabId, details, callback )
chrome.tabs.executeScript( MyTabIdNumberMandatoryInYourCase, MyScriptCodeInLineOrUrl, MyCallbackOptional )
tabId matches the ID of the active tab page, background.js file is executed under the main background.html, then you need to pass the correct ID if you do not pass it, and hopefully it will execute the background.html as the active tab.
All WebRequest events, has a variable called details and she carries a tabid value, and you access it via details.tabId, below is a code that I use in one of my extensions already created.
var onCompletedExecuteScriptDetails = {
// You can run all the code in the inline form,
// rather than using the parameter "file", use the "code" parameter, but is very ugly,
// is much more elegant to use the "file" mode
// my-script.js is a file with code to create cookie
file : "my-script.js"
};
var onCompletedExecuteScript = function ( details ) {
chrome.tabs.executeScript( details.tabId, onCompletedExecuteScriptDetails );
};
var onCompletedCallback = function ( details ) {
document.addEventListener( 'DOMContentLoaded', onCompletedExecuteScript( details ) );
};
var onCompletedFilter = {
urls : [
"http://*/*",
"https://*/*"
]
};
chrome.webRequest.onCompleted.addListener( onCompletedCallback, onCompletedFilter, onCompletedInfo );
executeScript
I ended up using:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.url != null) {
url = changeInfo.url;
}
});
This is what I ended up doing.
In manifest.json:
"permissions": [
"cookies",
"*://*.target_website.com/",
"*://*/_generated_background_page.html"
]
In background.js
chrome.cookies.set({
"name": "cookie's name",
"url": "the URL you want to apply the cookies to",
"value": "cookie's value"
}, function(cookie) {
if (chrome.extension.lastError) {
console.log(chrome.extension.lastError);
}
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
}
});
I'm trying to implement the chrome.webRequest API in my extension but for some reason it's just not working no matter what I do. Can someone post an example of usage? or correct my mistakes? Basically what I'm trying to do is to intercept the recieved headers from a response.
This is an implementation for onBeforeSendHeaders but I'd like to use OnHeadersRecieved as well
:
var requestFilter = {
urls: [ "<all_urls>" ]
},
// The 'extraInfoSpec' parameter modifies how Chrome calls your
// listener function. 'requestHeaders' ensures that the 'details'
// object has a key called 'requestHeaders' containing the headers,
// and 'blocking' ensures that the object your function returns is
// used to overwrite the headers
extraInfoSpec = ['requestHeaders','blocking'],
// Chrome will call your listener function in response to every
// HTTP request
handler = function( details ) {
alert(details);
var headers = details.requestHeaders,
blockingResponse = {};
// Each header parameter is stored in an array. Since Chrome
// makes no guarantee about the contents/order of this array,
// you'll have to iterate through it to find for the
// 'User-Agent' element
for( var i = 0, l = headers.length; i < l; ++i ) {
if( headers[i].name == 'User-Agent' ) {
headers[i].value = '>>> Your new user agent string here <<<';
break;
}
// If you want to modify other headers, this is the place to
// do it. Either remove the 'break;' statement and add in more
// conditionals or use a 'switch' statement on 'headers[i].name'
}
blockingResponse.requestHeaders = headers;
return blockingResponse;
};
chrome.webRequest.onBeforeSendHeaders.addListener( handler, requestFilter, extraInfoSpec );
this is my manifest file:
{
"background_page": "iRBackground.html",
"browser_action": {
"default_icon": "Off.png",
"popup": "iRMenu.html"
},
"content_scripts": [ {
"js": [ "Content.js" ],
"matches": [ "http://*/*" ],
"run_at": "document_start"
} ],
"description": "***",
"icons": {
"128": "On128x128.png",
"16": "On.png",
"48": "On48x48.png"
},
"key": "****",
"manifest_version": 2,
"name": "***",
"permissions": [ "tabs", "notifications", "unlimitedStorage", "webRequest", “webRequestBlocking”, “<all_urls>”],
"update_url": "***/Chrome/UpdateVersion.xml",
"version": "1.3"
}
the error I get from Chrome is: Uncaught TypeError: Cannot read property 'onBeforeSendHeaders' of undefined
Anyone see anything wrong??? thanks
Well for an example of usage I can give you this working code. I wrote it this way because the other way seems backwards to me but that is just my personal preference, they should both work the same.
Manifest
{
"name": "Chrome webrequest test",
"version": "0.1",
"description": "A test for webrequest",
"manifest_version": 2,
"permissions": [
"<all_urls>","webRequest","webRequestBlocking"
],
"background": {
"scripts": ["bgp.js"],
"persistent": true
}
}
bgp.js
chrome.webRequest.onBeforeSendHeaders.addListener(function(details){
//console.log(JSON.stringify(details));
var headers = details.requestHeaders,
blockingResponse = {};
// Each header parameter is stored in an array. Since Chrome
// makes no guarantee about the contents/order of this array,
// you'll have to iterate through it to find for the
// 'User-Agent' element
for( var i = 0, l = headers.length; i < l; ++i ) {
if( headers[i].name == 'User-Agent' ) {
headers[i].value = '>>> Your new user agent string here <<<';
console.log(headers[i].value);
break;
}
// If you want to modify other headers, this is the place to
// do it. Either remove the 'break;' statement and add in more
// conditionals or use a 'switch' statement on 'headers[i].name'
}
blockingResponse.requestHeaders = headers;
return blockingResponse;
},
{urls: [ "<all_urls>" ]},['requestHeaders','blocking']);
I just fixed this in my extension here: https://github.com/devinrhode2/tweet-bar
What I needed to do was use chrome.webRequest.onBeforeSendHeaders.addListener, but that also meant adding in the webRequest, webRequestBlocking permissions.. would be better to use declarativeWebRequest, but this project isn't that important to me.
Key things:
manifest.json "background": { "persistent": true,
"permissions": [ "webRequest", "webRequestBlocking",
When you make these changes in the manifest.json, you should actually consider re-installing the extension just to make sure the change is being picked up.
This is my filter code. Yours should not be identical. See the docs here https://developer.chrome.com/extensions/webRequest
chrome.webRequest.onBeforeSendHeaders.addListener((req) => {
console.log('onBeforeSendHeaders');
req.requestHeaders.forEach(function(header, index){
console.log(header.name+':', header.value);
if (headers[header.name.toLowerCase()]) {
console.log('set header:'+header.name, 'to:'+headers[header.name.toLowerCase()]);
req.requestHeaders[index].value = headers[header.name.toLowerCase()]
}
})
return {requestHeaders: req.requestHeaders};
},{
urls: ['https://twitter.com/i/tweet/create'],
types: ["xmlhttprequest"]
},[
'blocking',
'requestHeaders'
]);
I also added these headers to my xhr request, which doesn't hurt, makes you appear more similar to the normal site:
//add headers:
var headers = {
'content-type': 'application/x-www-form-urlencoded',
accept: 'application/json, text/javascript, */*; q=0.01',
origin: 'https://twitter.com',
referer: 'https://twitter.com/',
'x-requested-with': 'XMLHttpRequest'
};
console.log('change')
Object.keys(headers).forEach((header) => {
postXhr.setRequestHeader(header, headers[header]);
})
Add the required permissions for the extension in manifest.json, you might not need webRequestBlocking depending on what u want to do.
...
"permissions": [
"<all_urls>","webRequest","webRequestBlocking"
],"background": {
"scripts": ["background.js"],
"persistent": true
}
...
After adding the required permissions for your extension in the manifest.json file, make sure you click on the update button and if that does not work or the browser does not have an update button then reinstall the extension.
Here is the manifest config
"permissions": [
"webRequestBlocking"
,"webRequest"
,"http://*.beibei.com/*"
],
"background" : {
"page" : "xxx.html",
"persistent" : true
}
Here is the javascript demo code
$( function() {
// add event listners
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
console.log('onBeforeRequest', details);
},
{urls: ["http://www.beibei.com/"]},
[]
);
chrome.webRequest.onBeforeSendHeaders.addListener(
function(details) {
console.log('onBeforeSendHeaders', details);
},
{urls: ["http://www.beibei.com/"]},
["requestHeaders"]
);
chrome.webRequest.onCompleted.addListener(
function(details) {
console.log('onCompleted', details);
},
{urls: ["http://www.beibei.com/"]},
[]
);
// do a GET request, so that relative events will be fired, need jquery here
$.get('http://www.beibei.com/');
});