I am building a Chrome extension and trying to log some requests: the ones with POST parameters (plain text data)
This is the code I'm using:
var requestFilter = {urls: ["<all_urls>"]};
var extraInfoSpec = ['requestHeaders','requestBody','blocking']; // note: without 'requestBody' it works perfectly, but there's no POST data available.
var handler = function( details ) {
console.log(details);
};
chrome.webRequest.onBeforeSendHeaders.addListener(handler, requestFilter, extraInfoSpec);
I use requestHeaders and blocking for other things that are not in the example (don't worry about them now)
I am getting this error at page load: Uncaught Error: Invalid value for argument 2. Property '.1': Value must be one of: [requestHeaders, blocking].
I am develping under Chrome Version 36.0.1985.125 m
Form chrome webRequest documentation: Stable since Chrome 23. Contains the HTTP request body data. Only provided if extraInfoSpec contains 'requestBody'.
Any experience with this error? Any known solution? How can I solve this?
You are listening to the wrong event.
If you look at the documentation, onBeforeSendHeaders does not list requestBody in the callback details.
It is, however, available in onBeforeRequest.
So, if you need both the headers and the body, you have to correlate the two events by requestId.
Related
I'm working on my first extension for Google Chrome. I want to add a custom header to (all) http(s) requests based on options that the user can select on the options page of my extension.
I managed the following successfully:
Manually add custom headers to all http requests
Create an options page
Save and restore options in chrome.storage.sync
Now I'm struggling to combine the above. Here's what I tried:
chrome.webRequest.onBeforeSendHeaders.addListener(function(details){
var demoOptions = new Array();
var demoHeader = '';
var bkg = chrome.extension.getBackgroundPage(); // for debugging
chrome.storage.sync.get({
demoOption: true,
}, function(items) {
if(items.demoOption){
demoOptions.push('demo-header-1');
}
demoHeader = demoOptions.join();
details.requestHeaders.push({
name: 'X-Demo-Header',
value: demoHeader //this is not showing up in the http headers
});
bkg.console.log('demoHeader:' + demoHeader); //this is putting the correct value to the debugging console
bkg.console.log(details.requestHeaders); //this is putting the headers to the debugging console, incl. x-demo-header1 and x-demo-header2 (see below)
});
details.requestHeaders.push({
name: 'X-Demo-Header2',
value: 'Demo-Header-2' //this is correctly added to the headers and included in the request
});
return { requestHeaders: details.requestHeaders };
},
{urls: [ "*://*/*" ]},['requestHeaders','blocking']);
Observations:
X-Demo-Header2 is correctly inserted (visible in Chrome header inspection and n the debigging console)
x-demo-header1 is not part of the header sent to the site, but it is visible in the debugging output
x-demo-header2 is in the header before X-Demo-Header1 in the header array in the console (see screenshot)
Assumption: function(items) to fetch my options is executed asynchronyously and requestHeaders are returned before the headers from my options are inserted.
Can I avoid this? Is there a better option to add headers based on options?
Screenshot:
TL;DR
It currently won't work in Chrome (Feb. 15th 2021)
Long answer
JavaScript heavily relies on the idea of asynchronous code execution. In your code you return { requestHeaders: details.requestHeaders } before the event listener registered in chrome.storage.sync.get(key, listener) has finished. Therefore, demo-header-1 is added too late.
That's why you need to use a Promise. There is an example in the docs how to do that. On that page scroll down to where it says: "This code is exactly like the previous example, except that the listener is asynchronous, returning a Promise which is resolved with the new headers".
In your case the modified version of your code would look this like this:
chrome.webRequest.onBeforeSendHeaders.addListener(
function (details) {
return new Promise((resolve, reject) => {
chrome.storage.sync.get({ demoOption: true }, function (items) {
if (items.demoOption) {
details.requestHeaders.push({
name: 'X-Demo-Header',
value: 'demo-header-1',
});
}
resolve({ requestHeaders: details.requestHeaders });
});
});
},
{ urls: ['*://*/*'] },
['blocking', 'requestHeaders']
);
As you can see we now return a Promise instead of an object literal. In this Promise we wait for the read operation of chrome.storage.sync.get to finish. Once it's done we resolve the Promise.
This should work in browsers which support this.
In Chrome this doesn't work because:
There is no support in Chrome for returning a Promise from webRequest.onBeforeSendHeaders since 2018
Unless you can find a way to load the data from chrome.storage.sync.get in a synchronous way you are out of luck I'm afraid.
As a last resort you can try to post a complaint in that Chromium issue.
I have a Google Script-JSON-javascript webapp working but when I changed the JSON output, saved a new Google Script version, and published as web app, the webpage throws a CORB error.
Going back a version in GS, allowed it to work again. Why would a new GS version cause an error. Even with the same code but in a new version the error is still thrown. Is there a GS version caching issue? What I can do to update the version?
Working page with current version: https://arcacademy.ca/arc-academy-calendar-2018-2019/
Note: You can see in Console the JSON text being sent. The new code has the same format/structure, except changed the color values from hex to 1 to 10.
To tried to fix this by creating a new GS script but receive the same error:
Cross-Origin Read Blocking (CORB) blocked cross-origin response
https://script.google.com/macros/s/AKfycbyM51kxwQOYM3hRyrW7semhmUka2z2w-jU09KBPL38IxKapeQQ1/exec?callback=receivedCalendarEvents
with MIME type text/html. See
https://www.chromestatus.com/feature/5629709824032768 for more
details.
Here's the not working page throwing above error in console: https://arcacademy.ca/clone-of-arc-academy-calendar-2018-2019/
Google Script web app deployed with:
Execute as Me
Anyone, even Anonymous
Google Script code:
function doGet(e) {
if (e.parameter.method=="populate_events") {
var scriptProperties = PropertiesService.getScriptProperties();
var calendarId = scriptProperties.getProperty('calendarId') || 'primary';
var v = extractCalendarDateColors( calendarId, e.year );
return ContentService.createTextOutput(e.parameter.callback + "(" + JSON.stringify(v) + ")")
.setMimeType(ContentService.MimeType.JAVASCRIPT);
}
}
Javascript code for working page (src= is the only thing changed for error page):
<script>
calendarEvents = {};
function receivedCalendarEvents(jsonData) {
console.log('received',jsonData);
calendarEvents = JSON.parse(jsonData);
}
</script>
<script src="https://script.google.com/macros/s/AKfycbzMCDiTzxGx2cN5dtXqCG2gvxJ6FGZ_t6UuPiT-HyDesu2--EY/exec?callback=receivedCalendarEvents"></script>
<script>
Google Console Cloud -- I have set the following:
Enabled Calendar API
Added Apps Script Client ID
I feel like I'm lost in Google access land. Any help would be greatly appreciated!
Thank you Tanaike for the inspiration on this problem. The CORB (cross origin error) was, as you mentioned above, likely to do with javascript call.
I updated the javascript: https://script.google.com/macros/s/#####/exec?method=populate_events&year=2018
And updated the google script to use the parameters without throwing an error:
e.parameter.method
e.parameter.year
So it seems all the time I was getting the cross origin error, it was from a poorly formed javascript call and an error on the google script side, which returned an error and not a properly formed JSONP object.
Thank you for your help Tanaike!! :)
I am trying to write an extension that will download audio files when it detects them being requested by chrome. I am basing this project on the code of the two samples "Download_links" and "catifier" provided by Google. Here is what I currently have:
var RequestMatcher = chrome.declarativeWebRequest.RequestMatcher;
var IgnoreRules = chrome.declarativeWebRequest.IgnoreRules;
var RedirectRequest = chrome.declarativeWebRequest.RedirectRequest;
var songFileURL = "http://somefile.mp3";
...
function registerRules() {
var redirectRule = {
priority: 100,
conditions: [
new RequestMatcher({
contentType: ['audio/mp3']
}),
],
actions: [
chrome.downloads.download({url: songFileURL}),
]
};
...
When I load this up and try it out, I get an error: Uncaught Error: Invalid value for argument 1. Property '.0': Value does not match any valid type choices. No matter what I try I cannot figure out what is causing this error. I am fairly to Chrome extensions and JavaScript in general, so I am sure that this is an easy fix, but I cannot figure it out. Any ideas?
I think the problem is that you specify unsupported action. The list of available actions can be found on the chrome.declarativeWebRequest page.
I think you should use chrome.webRequest.onBeforeRequest, onHeadersReceived, or onComplete to trace media links and initiate custom downloads from there, possibly in some deferred way, because I'm not sure downloads will work just from another request handler.
To make a start you may have a look at Google's CatBlock example, or to another related answer or another one. Basically, you need to add appropriate event handler by means of addListener, and in the handler invoke chrome.downloads.download({url: request.url}), where the request is passed to the handler as input parameter.
Which one of events to choose (for example, onBeforeRequest or onComplete) you should decide based on your requirements. As I understand, you don't want to block original request, so it may be useful to wait utill original downloading is completed, and then process it in onComplete handler, so that Chrome would optimize the process by just copying already downloaded file from cache.
As alternative, you can block initial download by returning {cancel: true} from onBeforeRequest handler for every sound file, and then start your download as a single one, possibly with saveAs option involved.
I've a problem...I use jQuery ajax to call a web service that returns XML. The jQuery ajax stuff works awesome for every browser except for ie.
So for ie browsers, I am using XDomainRequest. Here is the code:
if ($.browser.msie && window.XDomainRequest) {
// Use Microsoft XDR
var xdr = new XDomainRequest();
xdr.open("get", theUserUrl);
xdr.timeout = 95000;
xdr.onerror = function () {
console.log('we have an error!');
}
xdr.onprogress = function () {
console.log('this sucks!');
};
xdr.ontimeout = function () {
console.log('it timed out!');
};
xdr.onopen = function () {
console.log('we open the xdomainrequest');
};
xdr.onload = function () {
// XDomainRequest doesn't provide responseXml, so if you need it:
var xml2 = new ActiveXObject("Microsoft.XMLDOM");
xml2.async = false;
xml2.loadXML(xdr.responseText);
console.log('do we get any response text at all?: ' + xdr.responseText);
ParseOwnershipObjects(xml2);
//AddServiceRequestsToMap(xml2, map, spinner);
};
xdr.send();
}
This exact code works fine elsewhere in the application with a
different url.
The url is fine, it returns exactly what it should in the browser
(and hence why the jquery ajax call works). Couple of things to
note:
I am integrating my own html/javascript with another guy's asp.net
project.
In the global.asax.cs file, I have:
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET,OPTIONS");
}
so I don't think that it's a header problem.
None of my handlers fire. Not the onprogress, ontimeout, onerror...nothing!
I don't have time to convert the web service to JSON.
Any thoughts?
Thanks!
Disclaimer - I actually haven't used 'XDomainRequest' - when using jQ I set data to jsonp for xdomain requests...
When debugging - are you using IE Dev tools (F12)? If not, the error is likely console.log
EDIT:
mea culpa, disregard the jsonp stuff - missed the part you mentioned XML
Update:
Out of curiosity I'm trying XDomainRequest. I copied your code and just added a value for theUserUrl.
as above/expected, unless I have Internet Explorer Developer tools running, console is undefined - and may give the impression that "none of your handlers are firing".
Once I have the IE dev tools enabled (docked or otherwise) xdr.onerror fires. we have an error is logged in the IE console. So while there is an error, the handler does fire.
A quick read on XDomainRequest requires the responding server to have the Access-Control-Allow-Origin header. I'm calling my own server and I know I don't have this header set, so without further debugging, it would be a good guess that's why xdr.onerror is being fired.
As it turns out, there were special characters in the url parameters that were not being correctly dealt with by the XDomainRequest object. Instead of the GET request, I am going to use the POST request on internet explorer-only queries.
EDIT - I ended up switching the web service over to return output in JSON format, thus negating the need for the XDomainRequest. Using JSON speeds things up a bit too, I recommend it!
Here's what I'm doing:
swfu = new SWFUpload(...);
swfu.addPostParam('id', 1);
When swfu.startUpload(); is called, I've set a breakpoint. Looking into the swfu object I can see the params are set appropriately:
When checking the resulting post in fiddler however, I can't see this parameter. It also isn't being picked up by the server I'm posting to (.net mvc 3).
I've tried version 2.2 and 2.5 of SWFUpload, no dice with either. What am I missing?
Edit:
Enabling debug mode, I can see this error is raised:
Exception calling flash function 'SetPostParams': __flash__argumentsToXML is not defined
The root problem basically came down to SWFU not being fully initialised when I tried to set the params. This has to be done in one of the events that fire before an upload commences. I'm using the
upload_start_handler
event, and setting the params there with addPostParam() method.
Try using setPostParams instead of addPostParam.
I remember having some problems with addPostParam as well back in the day and now all my code uses setPostParams.