API to create cookie for FireFox - firefox

I want to write an application which will create cookies for firefox.
I want to create Client cookies so that firefox will send cookie content in HTTP request.
Similar to the win32 API InternetSetCookie()
Can you please guide on this ?
If you can point me to some code snippet or help, I will try to figure out from that.
This cookie needs to go to SQLITE database, but it seems from old questions that this database get locked if firefox is running. This locking is done in FF 3.5
Just want to confirm if this is the case with FF9 or do we have any API ?
Regards

On Firefox, you can write an add-on to achieve that. Take a look at the source code of the following add-ons. They provide features such as adding, deleting, editing cookies while Firefox is running. It seems they all work with Firefox 9.0.1 (latest stable).
Cookie Manager+
Advanced Cookie Manager
Add N Edit Cookie
Edit Cookie
Edit:
I am posting some cookie management code from the Evernote plugin's MozillaCookieManagerImpl.js file. I think the code speaks for itself. Have a look below. It shows how to access cookies, set, get and remove them etc.
Accessing Mozilla's Cookie Management Interface
Evernote.MozillaCookieManagerImpl = function MozillaCookieManagerImpl() {
};
Evernote.inherit(Evernote.MozillaCookieManagerImpl,
Evernote.CookieManagerImpl, true);
Evernote.MozillaCookieManagerImpl.isResponsibleFor = function(navigator) {
var ua = navigator.userAgent.toLowerCase();
return (ua.indexOf("firefox") >= 0 || ua.indexOf("thunderbird") >= 0 || ua
.indexOf("shredder") >= 0);
};
Evernote.MozillaCookieManagerImpl.prototype.manager = null;
Evernote.MozillaCookieManagerImpl.prototype._ios = null;
Evernote.MozillaCookieManagerImpl.prototype._cookieSrv = null;
Evernote.MozillaCookieManagerImpl.prototype._cookieManagerSrv = null;
Evernote.MozillaCookieManagerImpl.prototype.getIOService = function() {
if (this._ios == null) {
this._ios = Components.classes["#mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
}
return this._ios;
};
Evernote.MozillaCookieManagerImpl.prototype.getCookieService = function(
force) {
if (this._cookieSrv == null || force) {
this._cookieSrv = Components.classes["#mozilla.org/cookieService;1"]
.getService(Components.interfaces.nsICookieService);
}
return this._cookieSrv;
};
Evernote.MozillaCookieManagerImpl.prototype.getCookieManagerService = function(
force) {
if (this._cookieManagerSrv == null || force) {
this._cookieManagerSrv = Components.classes["#mozilla.org/cookiemanager;1"]
.getService(Components.interfaces.nsICookieManager);
}
return this._cookieManagerSrv;
};
Get Cookie
Evernote.MozillaCookieManagerImpl.prototype.get = function(name, url) {
var uri = this.getIOService().newURI(url, null, null);
var cookieMgr = this.getCookieManagerService();
if (cookieMgr) {
for ( var e = cookieMgr.enumerator; e.hasMoreElements();) {
var cookie = e.getNext().QueryInterface(Components.interfaces.nsICookie);
if (cookie && cookie.host == uri.host && cookie.name == name) {
return new Evernote.Cookie(cookie);
}
}
}
return null;
};
Set Cookie
Evernote.MozillaCookieManagerImpl.prototype.set = function(cookie, url) {
var uri = (typeof url == 'string') ? this.getIOService().newURI(url, null,
null) : null;
if (cookie instanceof Evernote.Cookie && typeof cookie.name == 'string'
&& cookie.name.length > 0) {
this.getCookieService().setCookieString(uri, null,
(cookie.name + "=" + cookie.value + ";"), null);
}
};
Removie Cookie
Evernote.MozillaCookieManagerImpl.prototype.remove = function(name, url) {
var cookieMgr = this.getCookieManagerService();
var urlParts = url.split("://", 2);
var domain = (urlParts.length == 2) ? urlParts[1] : urlParts[0];
urlParts = domain.split("/", 2);
var path = (urlParts.length == 2) ? urlParts[1] : null;
cookieMgr.remove(domain, name, path, false);
};

Related

Keeping Data between refreshes in Blazor server

I have a multiple page Web Application that needs to be refreshed after a user has entered in data to then display the data on the table. I have Scoped dependencies so my users do not see the same data table as they are unique to each user. After they enter the data and the page refreshes the page goes blank and the scope is empty. How would I go about keeping the data from the scopes after refresh? I essentially needs a Singlton dependency that isn't shared between users. This is a Blazor Server Side Application. EDIT: Accept Button Method
if (Controlled_Variables.Attendance != null)
{
if (_JBS.CheckFields(_JBF.JobID, _JBF.WorkCenter, _JBF.SetupRun) == false)
{
string prompted = await jsRunTime.InvokeAsync<string>("alert", "Please Verify Job ID, Work Center, and Setup/Run are selected!");
}
else
{
FilledOut = true;
_JBF.transactionData = Guid.NewGuid();
_JBF.jobOperationID = _JBF.WorkCenter;
DateTime recordDate = DateTime.Now;
JobOperationRecord JOR = await _JBS.GetJobOperationRecordByOperationID(_JBF.WorkCenter);
WorkCenterRecord SingleWorkCenter = await _JBS.GetWorkCenterInformation(JOR.Work_Center);
EmployeeRecord Employee = await _JBS.GetEmployeeRecord(Controlled_Variables.Employee_ID);
try
{
if (Check.IsThisAnActiveJob(_JBF.JobID))
{
bool Complete = await First_Article_Complete(_JBF.jobOperationID, _JBF.SetupRun);
if (Complete)
{
Controlled_Variables.Sessions.Clear();
Controlled_Variables.Sessions = await _JBS.GetOpenJobOperationTimeSessionForUser(Controlled_Variables.Employee_ID);
bool activeNonGhostSession = false;
foreach (JobOperationSessionManager Session in Controlled_Variables.Sessions)
{
if (Session.ghost == "Y")
{
Controlled_Variables.ActiveSession = Session;
activeNonGhostSession = true;
}
}
_JBS.UpdateJobOperationStatus(JOR.Job_Operation.ToString(), JOR.Status == "C" ? "C" : "S");
if (!activeNonGhostSession && !_JBF.GhostedCheck)
{
_JBS.AddNewJobOperationTimeSessionForUser(Controlled_Variables.Employee_ID, "N", _JBF.transactionData.ToString(), JOR.Work_Center,
_JBF.jobOperationID, _JBF.JobID, recordDate.ToString("yyyy-MM-dd HH:mm:ss"), _JBF.jobOperationTime.ToString(), _JBF.SetupRun);
_JBF.sessionManagerCreated = true;
_JBS.InsertIntoTransactionDetail_NewEntry_JobOperation(_JBF.transactionData.ToString(), _JBF.jobOperationID, _JBF.jobOperationTime.ToString(),
JOR.Work_Center, _JBF.JobID);
_JBF.transDetailCreated = true;
_JBS.InsertIntoTransactionData_NewEntry_JobOperation(_JBF.transactionData.ToString(), Controlled_Variables.Employee_ID, recordDate.ToString());
_JBF.transDataCreated = true;
_JBS.InsertIntoJobOperationTime_NewEntry_JobOperationStart(_JBF.jobOperationID, Controlled_Variables.Employee_ID, recordDate.ToString(),
Employee.Hourly_Rate.ToString(), SingleWorkCenter.Labor_Burden.ToString(), SingleWorkCenter.Machine_Burden.ToString(), SingleWorkCenter.GA_Burden.ToString(),
_JBF.jobOperationTime.ToString(), JOR.Work_Center, _JBF.jobOperationTimeObjectID.ToString(), JOR.ObjectID.ToString(), Employee.ObjectID.ToString());
_JBF.jobOperationTimeCreated = true;
btnStartJob = true;
btnStopJob = false;
NavManage.NavigateTo(NavManage.Uri, forceLoad: true);
string prompted = await jsRunTime.InvokeAsync<string>("alert", "The job has been entered successfully.", "ERROR");
Sorry for the confusion for the use of refresh. I needed to reload the table to allow the data put in by users to be visable after they hit accept. To do this I added a navigation manager
NavManage.NavigateTo(NavManage.Uri, forceLoad: true);
Which force loads the page and displays the new data. If there may be another way to do this to keep the scope I am open to using a different method. As you can see I have tried to just recall the method in which the table data is loaded into to possibly get the new data populated but then the application never reiterates the loop to actually display the data on the page
#for (int i = 0; i < Controlled_Variables.TransactionData.Count; i++)
{
var TransacData = Controlled_Variables.TransactionData[i];
TransacData.index = i;
#for (int l = 0; l < Controlled_Variables.TransactionDetail.Count; l++)
{
var transacdetail = Controlled_Variables.TransactionDetail[l];
transacdetail.index = l;
TransactionDetailRecord selectData = Controlled_Variables.TransactionDetail[l];
#if (transacdetail.Transaction_Data == TransacData.Transaction_Data)
{
<div><input value="#selectData" type="radio" name="Select" onclick="#(() => ShowSelectedRecord(selectData, TransacData))"> </div>
<div>#transacdetail.Target_Integer</div>
<div>#transacdetail.Job</div>
<div>#Controlled_Variables.Job.Part_Number</div>
<div>#TransacData.Transaction_Start</div>
if (TransacData.Transaction_Type == 10)
{
<div>#TransacData.Transaction_Type</div>
btnStartJob = true;
btnStopJob = false;
}
else
{
<div>#TransacData.Transaction_Type</div>
btnStartJob = false;
btnStopJob = true;
}
<div>#Controlled_Variables.Job.Customer</div>
<div>#transacdetail.Work_Center</div>
<div>#transacdetail.Quantity</div>
<div>#TransacData.Transaction_End</div>
<div>#transacdetail.Entry_Type</div>
<div>#transacdetail.Labor_Hrs</div>
<div>#transacdetail.Machine_Hrs</div>
<div>#TransacData.Transaction_Data</div>
<div>#transacdetail.Transaction_Detail</div>
}
}
}

How can I check for a response cookie in Asp.net Core MVC (aka Asp.Net 5 RC1)?

I'm converting a web forms application to asp.net core mvc. In my web forms application sometimes after I set some response cookies other code needs to see if they were set, and if so, access the cookie's properties (i.e. value, Expires, Secure, http). In webforms and MVC 5 it's possible to iterate over the cookies and return any particular cookies like so (old school I know)
for(int i = 0; i < cookies.Count; i++) {
if(cookies[i].Name == cookieName) {
return cookies[i];
}
}
But the interface for accessing response cookies in asp.net core mvc looks like this:
Based on this interface I don't see a way to check to see if a response cookie exists and obtain it's properties. But there has gotta be some way to do it?
In an action method I tried setting two cookies on the response object and then immediately trying to access them. But intellisense doesn't show any methods, properties or indexers that would allow me to access them:
For a moment, I thought that perhaps I could use Response.Cookies.ToString(); and just parse the information to find my cookie info, but alas, the ToString() call returns "Microsoft.AspNet.Http.Internal.ResponseCookies" because the object doesn't override the default implementation.
Just for fun I also checked the current dev branch of GitHub to see if the interface has changed since RC1 but it has not. So given this interface, how do I check for the existence of a response cookie and get it's properties? I've thought about trying to hack in via the response headers collection but that seems pretty lame.
There is an extension method available in Microsoft.AspNetCore.Http.Extensions called GetTypedHeaders(). This can be called on HttpContext.Response to read Set-Cookie headers. For example in middleware perhaps we want to intercept a Set-Cookie response header and replace it:
public async Task Invoke(HttpContext httpContext)
{
httpContext.Response.OnStarting(state =>
{
var context = (HttpContext)state;
var setCookieHeaders = context.Response.GetTypedHeaders().SetCookie;
// We assume only one cookie is found. You could loop over multiple matches instead.
// setCookieHeaders may be null if response doesn't contain set-cookie headers
var cookie = setCookieHeaders?.FirstOrDefault(x => x.Name == "mycookiename");
if (cookie == null)
{
return Task.CompletedTask;
}
var opts = new CookieOptions
{
HttpOnly = true,
Expires = DateTimeOffset.Now.AddHours(12),
SameSite = SameSiteMode.Lax,
Secure = true
};
context.Response.Cookies.Delete(cookie.Name.Value);
context.Response.Cookies.Append(cookie.Name.Value, "mynewcookievalue", opts);
return Task.CompletedTask;
}, httpContext);
await _next(httpContext);
}
Here's how I get the value of a cookie from a Response. Something like this could be used to get the whole cookie if required:
string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
foreach (var headers in response.Headers.Values)
foreach (var header in headers)
if (header.StartsWith($"{cookieName}="))
{
var p1 = header.IndexOf('=');
var p2 = header.IndexOf(';');
return header.Substring(p1 + 1, p2 - p1 - 1);
}
return null;
}
There is an obvious problem with Shaun's answer: it will match any HTTP-header having matching value. The idea should be to match only cookies.
A minor change like this should do the trick:
string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
foreach (var headers in response.Headers)
{
if (headers.Key != "Set-Cookie")
continue;
string header = headers.Value;
if (header.StartsWith($"{cookieName}="))
{
var p1 = header.IndexOf('=');
var p2 = header.IndexOf(';');
return header.Substring(p1 + 1, p2 - p1 - 1);
}
}
return null;
}
Now the checking for a cookie name will target only actual cookie-headers.
You can use this function to get full information about the cookie set value
private SetCookieHeaderValue GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
var cookieSetHeader = response.GetTypedHeaders().SetCookie;
if (cookieSetHeader != null)
{
var setCookie = cookieSetHeader.FirstOrDefault(x => x.Name == cookieName);
return setCookie;
}
return null;
}
In my case I had to get only value of auth= and don't care about other values, but you can easy rewrite code to get dict of cookie values.
I wrote this extension for HttpResponseMessage, in order to avoid work with index and substring, I separate string to array and then cast it to dictionary
internal static string GetCookieValueFromResponse(this HttpResponseMessage httpResponse, string cookieName)
{
foreach (var cookieStr in httpResponse.Headers.GetValues("Set-Cookie"))
{
if(string.IsNullOrEmpty(cookieStr))
continue;
var array = cookieStr.Split(';')
.Where(x => x.Contains('=')).Select(x => x.Trim());
var dict = array.Select(item => item.Split(new[] {'='}, 2)).ToDictionary(s => s[0], s => s[1]);
if (dict.ContainsKey(cookieName))
return dict[cookieName];
}
return null;
}
You can get the cookie string value from the response using this method. In this case, I'm using this logic in a CookieService, that is the reason you will see that I'm using the _contextAccessor. Notice that in this case, I'm returning null if the value of the cookie is empty. (This is optional)
private string GetCookieFromResponse(string cookieName)
{
var cookieSetHeader = _contextAccessor.HttpContext.Response.GetTypedHeaders().SetCookie;
var cookie = cookieSetHeader?.FirstOrDefault(x => x.Name == cookieName && !string.IsNullOrEmpty(x.Value.ToString()));
var cookieValue = cookie?.Value.ToString();
if (!string.IsNullOrEmpty(cookieValue))
{
cookieValue = Uri.UnescapeDataString(cookieValue);
}
return cookieValue;
}
If you are looking for a solution that gets the cookie from the response or the request in that order then you can use this.
private string GetCookieString(string cookieName)
{
return GetCartCookieFromResponse(cookieName) ?? GetCartCookieFromRequest(cookieName) ?? "";
}
private string GetCookieFromRequest(string cookieName)
{
return _contextAccessor.HttpContext.Request.Cookies.ContainsKey(cookieName) ? _contextAccessor.HttpContext.Request.Cookies[cookieName] : null;
}
private string GetCookieFromResponse(string cookieName)
{
var cookieSetHeader = _contextAccessor.HttpContext.Response.GetTypedHeaders().SetCookie;
var cookie = cookieSetHeader?.FirstOrDefault(x => x.Name == cookieName && !string.IsNullOrEmpty(x.Value.ToString()));
var cookieValue = cookie?.Value.ToString();
if (!string.IsNullOrEmpty(cookieValue))
{
cookieValue = Uri.UnescapeDataString(cookieValue);
}
return cookieValue;
}
The snippet seems to work for me. The result for me (derived from Shaun and Ron) looks like:
public static string GetCookieValueFromResponse(HttpResponse response, string cookieName)
{
// inspect the cookies in HttpResponse.
string match = $"{cookieName}=";
var p1 = match.Length;
foreach (var headers in response.Headers)
{
if (headers.Key != "Set-Cookie")
continue;
foreach (string header in headers.Value)
{
if (header.StartsWith(match) && header.Length > p1 && header[p1] != ';')
{
var p2 = header.IndexOf(';', p1);
return header.Substring(p1 + 1, p2 - p1 - 1);
}
}
}
return null;
}
In cases where multiple "Set-Cookie" headers exists the last one should be used:
private string GetCookieValueFromResponse(Microsoft.AspNetCore.Http.HttpResponse response, string cookieName)
{
var value = string.Empty;
foreach (var header in response.Headers["Set-Cookie"])
{
if (!header.Trim().StartsWith($"{cookieName}="))
continue;
var p1 = header.IndexOf('=');
var p2 = header.IndexOf(';');
value = header.Substring(p1 + 1, p2 - p1 - 1);
}
return value;
}

NicEdit link creation doesn't work in IE 8 and FireFox if text wasn't selected

I have a problem with nicEdit link creation tool in IE and Firefox.
In general, I think the problem is related to the execCommand in IE and FireFox. It seems document doesn't get updated after execCommand executes.
This is an example of my problem with nicEdit create link command.
if(!this.ln) {
var tmp = 'javascript:nicTemp();';
this.ne.nicCommand("createlink",tmp);
this.ln = this.findElm('A','href',tmp);
// set the link text to the title or the url if there is no text selected
alert(this.ln);
if (this.ln.innerHTML == tmp) {
this.ln.innerHTML = this.inputs['title'].value || url;
};
}
The code above is called when no text is selected, Chrome returns 'javascript:nicTemp()' for the alert(this.ln), while IE 8 and Firefox return 'undefined', so the next line after the alert encounters an error in IE and Firefox.
it seems findElem can't find the newly created link by nicCommand which in turn calls execCommand
I had similar problems when I try to find and modify tags created with execCommand, it seems the dom isn't updated to include them.
Am I right? How can I solve this problem? how can I force the document to be updated ....
please help
my trick for nicEdit, in the situation when no text is selected, is to paste the title given via the Add Link form into the document and select it, then the rest code works as it works when a text is selected.
I used the function pasteHtmlAtCaret described in the following link to paste the title
Insert html at caret in a contenteditable div
this.removePane();
var url = this.inputs.href.value;
var selected = getSelected();
var B= 'javascript:nicTemp()';
if (selected == '')
{
var B = url;
pasteHtmlAtCaret(this.inputs['title'].value || url,true);
}
if(!this.ln){
this.inputs.title.value;this.ne.nicCommand("createlink",B);
this.ln=this.findElm("A","href",B)
}
the getSelected is also a simple function as below
function getSelected()
{
if (document.selection)
return document.selection.createRange().text;
else
return window.getSelection();
}
Ahmad, just use this variation of the "submit" function to avoid the "insert/edit" problem with the link, it worked for me:
submit : function(e) {
var url = this.inputs['href'].value;
if(url == "http://" || url == "") {
alert("Introduce una URL valida para crear el Link.");
return false;
}
this.removePane();
if(!this.ln) {
//**************** YOUR CHANGE WITH A BIT OF VARIATION **************
var selected = this.getSelected();
var tmp = 'javascript:void(0)';
if (selected == '') {
tmp = url;
this.pasteHtmlAtCaret(this.inputs['title'].value || tmp, true);
}
//**************** END OF YOUR CHANGE WITH A BIT OF VARIATION **************
this.ne.nicCommand("createlink",tmp);
this.ln = this.findElm('A','href',tmp);
// set the link text to the title or the url if there is no text selected
if (this.ln.innerHTML == tmp) {
this.ln.innerHTML = this.inputs['title'].value || url;
};
}
if(this.ln) {
var oldTitle = this.ln.title;
this.ln.setAttributes({
href: this.inputs['href'].value,
title: this.inputs['title'].value,
target: '_blank'
});
// set the link text to the title or the url if the old text was the old title
if (this.ln.innerHTML == oldTitle) {
this.ln.innerHTML = this.inputs['title'].value || this.inputs['href'].value;
};
}
}
this.removePane();
var url = this.inputs['href'].value;
var selected = getSelected();
var isChrome = /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor);
var tmp = "";
if(isChrome == true){
tmp=url;
}
else{tmp='javascript:nicTemp()'}
if (selected == '' && isChrome == false)
{
pasteHtmlAtCaret(this.inputs['title'].value || url,true);
}
if (!this.ln) {
//var tmp = this.inputs['title'].value == "" ? this.inputs['href'].value : this.inputs['title'].value;
this.ne.nicCommand("createlink", tmp);
this.ln = this.findElm('A', 'href', tmp);
}
function getSelected()
{
if (document.selection)
return document.selection.createRange().text;
else
return window.getSelection();
}
function pasteHtmlAtCaret(html) {
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
// Range.createContextualFragment() would be useful here but is
// non-standard and not supported in all browsers (IE9, for one)
var el = document.createElement("div");
//create a link format
el.innerHTML = ''+ html +'';
var frag = document.createDocumentFragment(), node, lastNode;
while ( (node = el.firstChild) ) {
lastNode = frag.appendChild(node);
}
range.insertNode(frag);
// Preserve the selection
if (lastNode) {
range = range.cloneRange();
range.setStartAfter(lastNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
document.selection.createRange().pasteHTML(html);
}
}

How to modify http response in Firefox extension

Hey i have been able to write an nsIStreamListener listener to listen on responses and get the response text following tutorials at nsitraceablechannel-intercept-http-traffic .But i am unable to modify the response sent to browser.Actually if i return the reponse and sent back to chain it reflects in firebug but not in browser.
What i am guessing is we will have to replace default listener rather than listening in the chain.I cant get any docs anywhere which explains how to do this.
Could anyone give me some insight into this.This is mainly for education purposes.
Thanks in advance
Edit : As of now i have arrived at a little solutions i am able to do this
var old;
function TracingListener() {}
TracingListener.prototype = {
originalListener: null,
receivedData: null, //will be an array for incoming data.
//For the listener this is step 1.
onStartRequest: function (request, context) {
this.receivedData = []; //initialize the array
//Pass on the onStartRequest call to the next listener in the chain -- VERY IMPORTANT
//old.onStartRequest(request, context);
},
//This is step 2. This gets called every time additional data is available
onDataAvailable: function (request, context, inputStream, offset, count) {
var binaryInputStream = CCIN("#mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream");
binaryInputStream.setInputStream(inputStream);
var storageStream = CCIN("#mozilla.org/storagestream;1",
"nsIStorageStream");
//8192 is the segment size in bytes, count is the maximum size of the stream in bytes
storageStream.init(8192, count, null);
var binaryOutputStream = CCIN("#mozilla.org/binaryoutputstream;1",
"nsIBinaryOutputStream");
binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));
// Copy received data as they come.
var data = binaryInputStream.readBytes(count);
this.receivedData.push(data);
binaryOutputStream.writeBytes(data, count);
//Pass it on down the chain
//old.onDataAvailable(request, context,storageStream.newInputStream(0), offset, count);
},
onStopRequest: function (request, context, statusCode) {
try {
//QueryInterface into HttpChannel to access originalURI and requestMethod properties
request.QueryInterface(Ci.nsIHttpChannel);
//Combine the response into a single string
var responseSource = this.receivedData.join('');
//edit data as needed
responseSource = "test";
console.log(responseSource);
} catch (e) {
//standard function to dump a formatted version of the error to console
dumpError(e);
}
var stream = Cc["#mozilla.org/io/string-input-stream;1"]
.createInstance(Ci.nsIStringInputStream);
stream.setData(responseSource, -1);
//Pass it to the original listener
//old.originalListener=null;
old.onStartRequest(channel, context);
old.onDataAvailable(channel, context, stream, 0, stream.available());
old.onStopRequest(channel, context, statusCode);
},
QueryInterface: function (aIID) {
if (aIID.equals(Ci.nsIStreamListener) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw components.results.NS_NOINTERFACE;
},
readPostTextFromRequest: function (request, context) {
try {
var is = request.QueryInterface(Ci.nsIUploadChannel).uploadStream;
if (is) {
var ss = is.QueryInterface(Ci.nsISeekableStream);
var prevOffset;
if (ss) {
prevOffset = ss.tell();
ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
}
// Read data from the stream..
var charset = "UTF-8";
var text = this.readFromStream(is, charset, true);
if (ss && prevOffset == 0)
ss.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
return text;
} else {
dump("Failed to Query Interface for upload stream.\n");
}
} catch (exc) {
dumpError(exc);
}
return null;
},
readFromStream: function (stream, charset, noClose) {
var sis = CCSV("#mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream");
sis.setInputStream(stream);
var segments = [];
for (var count = stream.available(); count; count = stream.available())
segments.push(sis.readBytes(count));
if (!noClose)
sis.close();
var text = segments.join("");
return text;
}
}
httpRequestObserver = {
observe: function (request, aTopic, aData) {
if (typeof Cc == "undefined") {
var Cc = components.classes;
}
if (typeof Ci == "undefined") {
var Ci = components.interfaces;
}
if (aTopic == "http-on-examine-response") {
request.QueryInterface(Ci.nsIHttpChannel);
console.log(request.statusCode);
var newListener = new TracingListener();
request.QueryInterface(Ci.nsITraceableChannel);
channel = request;
//newListener.originalListener
//add new listener as default and save old one
old = request.setNewListener(newListener);
old.originalListener = null;
var threadManager = Cc["#mozilla.org/thread-manager;1"]
.getService(Ci.nsIThreadManager);
threadManager.currentThread.dispatch(newListener, Ci.nsIEventTarget.DISPATCH_NORMAL);
}
},
QueryInterface: function (aIID) {
if (typeof Cc == "undefined") {
var Cc = components.classes;
}
if (typeof Ci == "undefined") {
var Ci = components.interfaces;
}
if (aIID.equals(Ci.nsIObserver) ||
aIID.equals(Ci.nsISupports)) {
return this;
}
throw components.results.NS_NOINTERFACE;
},
};
var observerService = Cc["#mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
observerService.addObserver(httpRequestObserver,
"http-on-examine-response", false);
This example works for me on Firefox 34 (current nightly): https://github.com/Noitidart/demo-nsITraceableChannel
I downloaded the xpi, edited bootstrap.js to modify the stream:
132 // Copy received data as they come.
133 var data = binaryInputStream.readBytes(count);
134 data = data.replace(/GitHub/g, "TEST");
135 this.receivedData.push(data);
installed the XPI then reloaded the github page. It read "TEST" in the footer.
The version of code you posted doesn't actually pass the results back to the old listener, so that's the first thing that ought to be changed.
It also may have interacted with Firebug or another extension badly. It's a good idea to try reproducing the problem in a clean profile (with only your extension installed).

AJAX POST Request Only Works Once in Safari 5

I use my own custom AJAX library (I'm not interested in using jQuery, etc.), which is working flawlessly in the following browsers:
Firefox 7
Chrome 14
IE 8
IE 8 (compatibility mode)
Using my custom AJAX library in the aforementioned browsers, I can make as many AJAX requests as I want, in any order, using GET and/or POST methods, and they all work flawlessly. Since a new AJAX object is created for every request (see code below), I can even have more than one AJAX request process simultaneously with success.
However, in Safari 5 an AJAX POST request only passes POST data to the server if it is the absolute first AJAX request to execute. Even if I execute the exact same AJAX POST request twice in a row, the POST data is only passed to the server during the first request. Here is the JavaScript in my custom AJAX library:
if (!Array.indexOf)
{
Array.prototype.indexOf = function(obj) { for (var i = 0; i < this.length; i++) { if (this[i] == obj) { return i; } } return -1; };
}
function ajaxObject()
{
if (window.ActiveXObject)
{
var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
for (var i = 0; i < activexmodes.length; i++)
{
try
{
return new ActiveXObject(activexmodes[i]);
}
catch (e)
{
}
}
}
else if (window.XMLHttpRequest)
{
return new XMLHttpRequest();
}
else
{
return false;
}
}
function ajaxRequest(aURI, aContainerId, aPostData, aResponseType, aAvoidBrowserCache)
{
// Initialize
var xmlhttp = new ajaxObject();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
{
if (aResponseType != "eval" && aResponseType != "EVAL")
{
// Show HTML for response
document.getElementById(aContainerId).innerHTML = xmlhttp.responseText;
}
else
{
// Parse & execute JavaScript for response
var responseText = xmlhttp.responseText;
var startPos, endPos;
for (var i = 0; i < responseText.length; i++)
{
if (responseText.substring(i, i + 6) == "<eval>")
{
startPos = i + 6;
break;
}
}
for (var i = startPos; i < responseText.length; i++)
{
if (responseText.substring(i, i + 7) == "</eval>")
{
endPos = i;
break;
}
}
textToEval = responseText.substring(startPos, endPos);
eval(textToEval);
}
}
else
{
try
{
if (xmlhttp.status != 0 && xmlhttp.status != 200)
{
alert('Error ' + xmlhttp.status);
}
}
catch (e)
{
// Handle IE8 debug "unknown error"
}
}
}
if (aAvoidBrowserCache != false)
{
// Combat browser caching:
aURI = aURI + (aURI.indexOf("?") == -1 ? "?" : "&");
theTime = new Date().getTime();
aURI = aURI + theTime + "=" + theTime;
}
// Make request
if (typeof aPostData == "undefined" || aPostData == null || aPostData == "")
{
// GET request
xmlhttp.open("GET", aURI, true);
xmlhttp.send();
}
else
{
// POST request
var parameters = "";
if (aPostData.constructor.toString().indexOf("Array") != -1)
{
// Use parameters passed as array
for (var postCount = 0; postCount < aPostData.length; postCount++)
{
if (parameters != "")
{
parameters = parameters + "&";
}
parameters = parameters + aPostData[postCount][0] + "=" + encodeURIComponent(aPostData[postCount][1]);
}
}
else
{
// Use parameters passed as string
parameters = aPostData;
}
xmlhttp.open("POST", aURI, true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(parameters);
}
}
So for example, either of the following AJAX POST requests will pass POST data if they are the absolute first AJAX request (whether GET or POST); otherwise, the POST data is not passed:
ajaxRequest("test.aspx", "", [["name1","value1"],["name2","value2"]], "eval");
or
ajaxRequest("test.aspx", "", "name1=value1&name2=value2", "eval");
I have added debug statements all throughout my AJAX library, and the POST parameters are being created in the "parameters" variable as expected prior to each POST request. I have absolutely no idea why, only in Safari 5 (out of the mentioned browsers), I have this problem. Any ideas?
Thanks in advance!
Jesse
The reason the call is failing is because of a bug in Safari when working with Windows Authentication under IIS. Go to the Authentication settings of your website. Right click on Windows Authentication, choose providers and remove Negotiate, leaving NTLM which works fine. I haven't tested Kerberos.
This issue only appears in certain builds of safari.
Came here from the thread you mentioned might be a dupe. I never solved our problem, but have you tried a simple page making post requests? With our issue it's a post problem, not an AJAX problem, we're still stumped though.
What version of IIS are you running on the server?
I can confirm that the problem seems related to some sort of interaction between Safari & IIS. Luckily, I only develop and test this portion of the code on Windows. I moved it unchanged to a LAMP (Linux/Apache) staging server (prior to moving to our LAMP production server) and the problem went away. I was seeing the problem with Safari 5, IIS 5.1, & an ActiveState Perl 5.6 CGI.
Under RHEL 5, Apache 2.2, & Perl 5.8, it is gone.

Resources