I've seen loads of examples of creating xhr requests from Firefox Add-ons, but I am trying to use the new WebExtensions stuff (where require and Components are undefined) and can't seem to see why I can't send a simple XmlHttpRequest from within the extension?
It's worth noting that the ajax request is going to a completely different URL, but the host has CORs set to allow all origins.
As soon as .send() is fired I get the error:
[Exception... "Failure" nsresult: "0x80004005 (NS_ERROR_FAILURE)" location: "JS frame :: resource://gre/modules/ExtensionContent.jsm -> moz-extension://9ca18411-9a95-4fda-8184-9dcd3448a41a/myapp.js :: GM_xmlhttpRequest :: line 162" data: no]"1 whatsapp.js:166:9
The code looks like this:
function GM_xmlhttpRequest(orders) {
try {
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", function(a1, a2, a3) {
console.log('xhr.load: %s, %s, %s', a1, a2, a3);
});
// open synchronously
oReq.open(orders.method, orders.url, false);
// headers
for (var key in orders.headers) {
oReq.setRequestHeader(key, orders.headers[key]);
}
// send
var res = oReq.send(orders.data);
console.log('xhr result: %s', res);
} catch(e) {
debugger;
console.warn('could not send ajax request %s to %s, reason %s', orders.method, orders.url, e.toString());
}
}
I've added webRequest permissions to my manifest.json, I realise that is not what it means, but am struggling to understand what is stopping the ajax request? Any ideas?
{
"manifest_version": 2,
"name": "MyApp",
"version": "1.0",
"description": "TestXHR",
"icons": {
"48": "icons/myapp-48.png"
},
"applications": {
"gecko": {
"id": "software#vigilantapps.com",
"strict_min_version": "45.0"
}
},
"content_scripts": [
{
"matches": ["*://web.myapp.com/*"],
"js": ["myapp.js"]
}
],
"permissions": [
"https://thehost.all-xhr-sent-here.net/*",
"webRequest"
]
}
The problem was the permissions URL specified. I changed the sub domain to an asterisk and the protocol to an asterisk and it seemed to work after that.
Related
I'm currently working on an extension to integrate two systems at my work,
The chrome extension is running just fine and working well, but my team also asked for a firefox version, in firefox I'm having trouble with the xmlhttprequest(),
the function that calls the request is this one:
function getModelList(API_KEY)
{
return new Promise(resolve => {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "POST", 'http://10.255.12.128/api/get_models/', true );
xmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xmlHttp.onload = function(e) {
resolve(xmlHttp.response);
};
xmlHttp.onerror = function (e) {
resolve(undefined);
console.error("** An error occurred during the XMLHttpRequest");
};
xmlHttp.send( 'API_KEY='+API_KEY );
})
}
(I know it not secure to send the API_KEY in the POST, but it only runs in our localnetwork so its probably fine for now)
When I run it, it goes direcly to onerror, It is faster then the timeout, and don't show in the network tab on inspection, I think I need to give it some permissions or something in firefox so it can run?
also, the manifest for the extension is this one(maybe the problem is here?):
{
"name" : "Zabbix-Bitrix Integration",
"version": "0.0.4",
"manifest_version": 2,
"description" : "Insere funções extras ao zabbix",
"options_ui": {
"page": "options.html",
"open_in_tab": false,
"browser_style": true,
"chrome_style": true
},
"content_scripts" : [
{
"js" : ["init.js"],
"css": ["styles.css"],
"matches" : ["*://zabbix.monitor.redeunifique.com.br/zabbix.php?action=problem.view*"]
}
],
"permissions": ["storage","webRequest"]
}
#edit, after some digging, found an error message:
Error: Got a request http://10.255.12.128/api/get_models/ without a browsingContextID set
The function that calls the error has this comment:
// Ensure that we have a browsing context ID for all requests when debugging a tab (=`browserId` is defined).
// Only privileged requests debugged via the Browser Toolbox (=`browserId` null) can be unrelated to any browsing context.
if (!this._browsingContextID && this._networkEventWatcher.browserId) {
throw new Error(
`Got a request ${this._request.url} without a browsingContextID set`
);
}
How and where do I set this Context ID ?
I want to run console app by clicking button on webpage
Console app will get Information and put in clipboard, and then I will get that information on webpage.
I am following this blog
I did this 3-4 times, all other things looks fine, but console app is not being called/executed.
I am getting these errors.
on webpage console
Unchecked runtime.lastError: The message port closed before a response was received.
on background file
Unchecked runtime.lastError: Specified native messaging host not found.
Unchecked runtime.lastError: The message port closed before a response was received.
my codes are
manifest.json
{
"name": "EID Reader",
"version": "1.0",
"manifest_version": 2,
"description": "Read Emirates ID",
"permissions": [ "contextMenus", "activeTab", "clipboardRead", "nativeMessaging" ],
"icons": {
"16": "eid16.png",
"48": "eid48.png",
"128": "eid128.png"
},
"background": {
"scripts": [ "eid.js" ]
},
"content_scripts": [
{
"matches": [ "http://*/*", "https://*/*", "file://*/*"],
"js": [ "content_script.js", "jquery-3.3.1.js" ],
"all_frames": true,
"run_at": "document_start"
}
]
}
content_script.js
// Listener to catch the event raised by the webpage button
document.addEventListener("EID_EVENT", function (data) {
// send message to background process to read emirates ID and send back the data
chrome.runtime.sendMessage("ifligfijbkpijeafdfbpljjibfbppmeb", function (response) {
});
});
// Listener to catch the data coming from the background process
chrome.extension.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action == 'EID_DATA') {
//Parse the data and fill the form accordingly
try {
var json = $.parseJSON(msg.response);
$(json).each(function (i, val) {
$.each(val, function (key, value) {
if (key == 'EIDNumber')
$("#txtNumber").val(value);
if (key == 'Name')
$("#txtName").val(value);
if (key == 'Email')
$("#txtEmail").val(value);
if (key == 'PassportNumber')
$("#txtPassport").val(value);
});
});
}
catch (e) {
var error = "error" + e;
}
}
});
eid.js (background)
var port = null;
var tabId = null;
/* listener for messages coming from the content_scrip */
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
tabId=sender.tab.id;
var hostName = "ae.eid.chrome";
port = chrome.runtime.connectNative(hostName);
port.onDisconnect.addListener(onDisconnected);
});
/* THIS WILL BE CALLED ONCE EXE FINISH */
function onDisconnected() {
port = null;
SendResponse();
}
function SendResponse() {
//create a textarea, focus on it, and make a "paste" command to get the clipboard, then send the pasted value back to the content_script
bg = chrome.extension.getBackgroundPage();
bg.document.body.innerHTML = ""; // clear the background page
var helper = null;
if (helper == null) {
helper = bg.document.createElement("textarea");
helper.style.position = "absolute";
helper.style.border = "none";
document.body.appendChild(helper);
}
//Focus the textarea
helper.select();
// perform a Paste in the selected control, here the textarea
bg.document.execCommand("Paste");
// Send data back to content_script
chrome.tabs.sendMessage(tabId, { action: "EID_DATA", response: helper.value }, function (response) { });
}
ae.eid.chrome.json
{
"name": "ae.eid.chrome",
"description": "chrome extension to read EID",
"path": "EIDSampleConsole.exe",
"type": "stdio",
"allowed_origins": [
"chrome-extension://ifligfijbkpijeafdfbpljjibfbppmeb/"
]
}
install_host.bat
REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\ae.eid.chrome" /ve /t REG_SZ /d "%~dp0ae.eid.chrome.json" /f
I spent 2 days didnot get anything helpful.
Am I doing some error or Google Chrome prevented or changed the way to register host.
I have solved all the issues and posted all the steps at
http://www.codingsips.com/emirates-id-reader-and-google-chrome-via-extension-and-console-app/
Also I have published chrome extension, it you follow above steps the same extension will also work for you
chrome extension
https://chrome.google.com/webstore/detail/adcs-eid-reader/ipcncgpbppgjclagpdlodiiapmggolkf
I'm building a chrome extension that will read the user's emails and check them for typos. However, when trying to authenticate the user in my background.js I'm running into this error:
uO {message: "Invalid cookiePolicy", stack:
"gapi.auth2.ExternallyVisibleError: Invalid cookieP… at handleResponse
(extensions::sendRequest:67:7)"}
Here is how I'm trying to authenticate them:
background.js
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://apis.google.com/js/client.js?onload=callbackFunction";
head.appendChild(script);
chrome.identity.getAuthToken({interactive: true}, authorize);
function authorize(token) {
gapi.auth.authorize({
client_id: '800382879116-k3luktdc1lmb1e1fml8i8u.apps.googleusercontent.com',
immediate: true,
scope: 'https://www.googleapis.com/auth/gmail.readonly'
},
function(result){
console.log(result);
gapi.client.load('gmail', 'v1', callback);
});
}
background.html
<!DOCTYPE html>
<html>
<body>
<script src='scripts/background.js'></script>
</body>
</html>
manifest.json
{
"name": "Gmail Typo Analyzer",
"version": "0.1",
"description": "Gmail Typo Analyzer",
"permissions": [
"identity",
"storage"
],
"content_security_policy": "script-src 'self' https://apis.google.com; object-src 'self'",
"oauth2": {
"client_id": "82879116-k3luktdc1li8u.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/userinfo.email"
]
},
"browser_action": {
"default_popup": "popup.html",
"default_icon": "images/Icon_16.png"
},
"background": {
"page": "background.html",
"persistent": false
},
"icons": {
"16": "images/Icon_16.png",
"32": "images/Icon_32.png",
"48": "images/Icon_48.png",
"128": "images/Icon_128.png"
},
"manifest_version": 2,
"key": "c0Kn5f+t92r4P8lmmoDlKtQ6X9Q42UfFtkkiSRBAVMPHnIHqOQvYC67VczJefSNTGpUYa8+wQDFoFj/clH9SfR+BvOGgI6BUVKBNGGoFS"
}
I'm super lost right now as their doesn't seem to be a definitive guide on achieving what I'm trying to do anywhere. Does anyone know what I might be doing wrong?
You didn't post your manifest.json file, where you would set the oauth2 credentials, so I would try something like:
manifest.json
...
"oauth2" : "client_id": "800382879116-k3luktdc1lmb1e1fml8i8u.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly"
]
...
background.js
chrome.identity.getAuthToken({interactive: true}, authorize);
function authorize(token) {
if (token) {
//user has given authorization, use token for requests.
//...
} else {
//no authorization received.
console.log('No authorization. Error: ' + chrome.runtime.lastError);
}
};
And you don't need to load Google API client, you can access Gmail's Restful API with XMLHttpRequests or Fetch API.
I used to get that cookie error too, but for Chrome extensions I only know how to load them from unpacked in the Extensions tab. So using gapi directly never worked for me.
As Ivan mentioned, Chrome extensions have this support "built-in" by setting the "oauth2" section in the manifest. Then you can call the APIs directly with Ajax like Ivan's example.
To build on Ivan's example, this is my working code to get a list of contacts. I haven't read from the XHR object yet, but Fiddler confirms that the data is coming back fine without any cookie or CORS errors. Of course make sure you enable these APIs in console.developers.google.com.
chrome.identity.getAuthToken({ interactive: true }, authorize);
function authorize(token) {
if (token) {
console.log(token);
var xhr = new XMLHttpRequest();
xhr.open('GET',
"https://people.googleapis.com/v1/people/me/connections?personFields=names");
xhr.setRequestHeader('Authorization',
'Bearer ' + token);
xhr.send();
//user has given authorization, use token for requests.
//...
} else {
console.log('No authorization. Error: ' + chrome.runtime.lastError);
}
};
I post the code below:
manifest.json
{
"manifest_version": 2,
"name": "Demo",
"description": "all_frames test",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*/*"],
"js": ["content.js"],
"all_frames": true
}],
"permissions": [
"tabs",
"*://*/*"
]
}
background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
var tabStatus = changeInfo.status;
if (tabStatus == 'complete') {
function return_msg_callback() {
console.log('Got a msg from cs...')
}
chrome.tabs.sendMessage(tabId, {
text: 'hey_cs'
}, return_msg_callback);
}
});
content.js
/* Listen for messages */
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
/* If the received message has the expected format... */
if (msg.text && (msg.text == 'hey_cs')) {
console.log('Received a msg from bp...')
sendResponse('hey_bp');
}
});
Then, if I go to a site that includes multiples cross-origin iFrames, e.g., http://www.sport.es/ you would see that all the iFrames within the page receive the message from the background page but only one of them is able to response back. Is this a normal behavior?
Thanks in advance for your answer.
You send just one message with a direct callback so naturally Chrome can use this response callback just one time (it's a one-time connection to one entity, be it a page or an iframe).
Solution 1: send multiple messages to each iframe explicitly:
manifest.json, additional permissions:
"permissions": [
"webNavigation"
],
background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
.............
// before Chrome 49 it was chrome.webNavigation.getAllFrames(tabId, .....
// starting with Chrome 49 tabId is passed inside an object
chrome.webNavigation.getAllFrames({tabId: tabId}, function(details) {
details.forEach(function(frame) {
chrome.tabs.sendMessage(
tabId,
{text: 'hey_cs'},
{frameId: frame.frameId},
function(response) { console.log(response) }
);
});
});
});
Solution 2: rework your background script logic so that the content script is the lead in communication and let it send the message once it's loaded.
content.js
chrome.runtime.sendMessage({text: "hey"}, function(response) {
console.log("Response: ", response);
});
background.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId);
sendResponse("Gotcha!");
});
Communicating between a content script and the background page in a Chrome extension
Content script to background page
Send info to background page
chrome.extension.sendRequest({message: contentScriptMessage});
Receive info from content script
chrome.extension.onRequest.addListener(function(request, sender) {
console.log(request.message);
});
Background page to content script
Send info to content script
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, { message: "TEST" });
});
Receive info from background page
chrome.runtime.onMessage.addListener(function(request, sender) {
console.log(request.message);
});
Instead of messaging, you can use executeScript for your purposes. While the callback's argument is rarely used (and I don't think many know how it works), it's perfect here:
chrome.tabs.executeScript(tabId, {file: "script.js"}, function(results) {
// Whichever is returned by the last executed statement of script.js
// is considered a result.
// "results" is an Array of all results - collected from all frames
})
You can make sure, for instance, that the last executed statement is something like
// script.js
/* ... */
result = { someFrameIdentifier: ..., data: ...};
// Note: you shouldn't do a "return" statement - it'll be an error,
// since it's not a function call. It just needs to evaluate to what you want.
Make sure you make script.js able to execute more than once on the same context.
For a frame identifier, you can devise your own algorithm. Perhaps a URL is enough, perhaps you can use the frame's position in the hierarchy.
I am trying to create a channel to my Google App Engine (Python) server, and there seems to be a problem but I am unsure why. When the user toggles the extension, it authenticates the user. If successful, the server replies with a channel token which I use to create the channel. When I authenticate the user, alert("a") appears, but alert("b") does not which makes me believe there is a problem with the line var channel = new goog.appengine.Channel(msg.token);, but the console does not report an error.
I have also copied the javascript code from here and placed it in my manifest as oppose to putting <script type="text/javascript" src="/_ah/channel/jsapi"></script> in background.html.
//script.js
function authenticate(callback) {
var url = "https://r-notes.appspot.com/init/api/authenticate.json?username=" + username + "&password=" + password;
$.post(url, function(data) {
if (data.status == "200") {
channelToken = data.channeltoken;
if (callback) {
callback();
}
var port = chrome.extension.connect({name: "myChannel"});
port.postMessage({token: channelToken});
port.onMessage.addListener(function(msg) {
console.log(msg.question);
});
}
});
}
//background.html
chrome.extension.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
alert("a"); //pops up
var channel = new goog.appengine.Channel(msg.token);
alert("b"); //does not pop up
console.log(channel); //display error ' Error in event handler for 'undefined': ReferenceError: goog is not defined '
var socket = channel.open()
socket.onopen = function() {
// Do stuff right after opening a channel
console.log('socket opened');
}
socket.onmessage = function(evt) {
// Do more cool stuff when a channel message comes in
console.log('message recieved');
console.log(evt);
}
});
});
//manifest.json
{
"name": "moot",
"description": "Clicking on the moot button will display a sidebar!",
"version": "0.2.69",
"background_page": "html/background.html",
"browser_action": {
"default_icon": "img/icon_64.png",
"default_title": "moot"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/channelApi.js",
"js/script.js", "js/mootsOnSidebar.js", "js/mootsOnPage.js", "js/authenticate.js", "js/otherFunctions.js",
"js/jquery/jquery-1.7.1.js", "js/jquery/jquery.mCustomScrollbar.js", "js/jquery/jquery-ui.min.js",
"js/jquery/jquery.autosize.js", "js/jquery/jquery.mousewheel.min.js", "js/jquery/jquery.easing.1.3.js",
"js/channel.js"],
"css": ["css/cssReset.css", "css/sidebar.css", "css/onPageCreate.css", "css/onPageExists.css", "css/scrollbar.css", "css/authenticate.css"]
}
],
"permissions": [
"tabs", "contextMenus", "http://*/*", "https://*/"
],
"icons": {
"16": "img/icon_16.png",
"64": "img/icon_64.png"
}
}
EDIT - After doing console.log(channel), I discovered the error ' Error in event handler for 'undefined': ReferenceError: goog is not defined '. I am unsure why I receive this error as I did include the required javascript file as I followed this post.
So the solution is that you need to include the file <script type="text/javascript" src="https://talkgadget.google.com/talkgadget/channel.js"></script> in a HTML page. I placed this on the first row of background.html.
My mistake was saving a local copy of channel.js, and refer to it in manifest.json.
I'm now going to place a copy of channel.js on my server, and refer to my server's copy. I don't think there will be any issues with that.
Make a console log for the value of msg direct between alert("a") and var channel = ...
and inspect the value.