mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 21:17:03 +00:00
The life will be easier to have the same in both environments. Warnings and errors now don’t use read only properties for line and column information. So the test cases in Toolstest.j now work in modern JS engines. It is also easier to set options for the compiler.
474 lines
13 KiB
JavaScript
Executable File
474 lines
13 KiB
JavaScript
Executable File
/*
|
|
* CFHTTPRequest.js
|
|
* Objective-J
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2010, 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
var asynchronousTimeoutCount = 0,
|
|
asynchronousTimeoutId = null,
|
|
asynchronousFunctionQueue = [];
|
|
|
|
function Asynchronous(/*Function*/ aFunction)
|
|
{
|
|
var currentAsynchronousTimeoutCount = asynchronousTimeoutCount;
|
|
|
|
if (asynchronousTimeoutId === null)
|
|
{
|
|
window.setNativeTimeout(function()
|
|
{
|
|
var queue = asynchronousFunctionQueue,
|
|
index = 0,
|
|
count = asynchronousFunctionQueue.length;
|
|
|
|
++asynchronousTimeoutCount;
|
|
asynchronousTimeoutId = null;
|
|
asynchronousFunctionQueue = [];
|
|
|
|
for (; index < count; ++index)
|
|
queue[index]();
|
|
}, 0);
|
|
}
|
|
|
|
return function()
|
|
{
|
|
var args = arguments;
|
|
|
|
if (asynchronousTimeoutCount > currentAsynchronousTimeoutCount)
|
|
aFunction.apply(this, args);
|
|
else
|
|
asynchronousFunctionQueue.push(function()
|
|
{
|
|
aFunction.apply(this, args);
|
|
});
|
|
};
|
|
}
|
|
|
|
var NativeRequest = null;
|
|
|
|
// We check ActiveXObject first, because we require local file access and
|
|
// overrideMimeType feature (which the native XMLHttpRequest does not have in IE).
|
|
if (window.XMLHttpRequest)
|
|
{
|
|
NativeRequest = window.XMLHttpRequest;
|
|
}
|
|
else if (window.ActiveXObject !== undefined)
|
|
{
|
|
// DON'T try 4.0 and 5.0: http://bit.ly/microsoft-msxml-explanation
|
|
var MSXML_XMLHTTP_OBJECTS = ["Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP.6.0"],
|
|
index = MSXML_XMLHTTP_OBJECTS.length;
|
|
|
|
while (index--)
|
|
{
|
|
try
|
|
{
|
|
var MSXML_XMLHTTP = MSXML_XMLHTTP_OBJECTS[index];
|
|
|
|
new ActiveXObject(MSXML_XMLHTTP);
|
|
|
|
NativeRequest = function()
|
|
{
|
|
return new ActiveXObject(MSXML_XMLHTTP);
|
|
};
|
|
|
|
break;
|
|
}
|
|
catch (anException)
|
|
{
|
|
}
|
|
}
|
|
}
|
|
|
|
GLOBAL(CFHTTPRequest) = function()
|
|
{
|
|
this._isOpen = false;
|
|
this._requestHeaders = {};
|
|
this._mimeType = null;
|
|
|
|
this._eventDispatcher = new EventDispatcher(this);
|
|
this._nativeRequest = new NativeRequest();
|
|
|
|
// by default, all requests will assume that credentials should not be sent.
|
|
this._withCredentials = false;
|
|
this._timeout = 60000;
|
|
|
|
var self = this;
|
|
this._stateChangeHandler = function()
|
|
{
|
|
determineAndDispatchHTTPRequestEvents(self);
|
|
};
|
|
|
|
this._timeoutHandler = function()
|
|
{
|
|
dispatchTimeoutHTTPRequestEvents(self);
|
|
};
|
|
|
|
this._nativeRequest.onreadystatechange = this._stateChangeHandler;
|
|
this._nativeRequest.ontimeout = this._timeoutHandler;
|
|
|
|
if (CFHTTPRequest.AuthenticationDelegate !== nil)
|
|
this._eventDispatcher.addEventListener("HTTP403", function()
|
|
{
|
|
CFHTTPRequest.AuthenticationDelegate(self);
|
|
});
|
|
}
|
|
|
|
CFHTTPRequest.UninitializedState = 0;
|
|
CFHTTPRequest.LoadingState = 1;
|
|
CFHTTPRequest.LoadedState = 2;
|
|
CFHTTPRequest.InteractiveState = 3;
|
|
CFHTTPRequest.CompleteState = 4;
|
|
|
|
//override to forward all CFHTTPRequest authorization failures to a single function
|
|
CFHTTPRequest.AuthenticationDelegate = nil;
|
|
|
|
CFHTTPRequest.prototype.status = function()
|
|
{
|
|
try
|
|
{
|
|
return this._nativeRequest.status || 0;
|
|
}
|
|
catch (anException)
|
|
{
|
|
return 0;
|
|
}
|
|
};
|
|
|
|
CFHTTPRequest.prototype.statusText = function()
|
|
{
|
|
try
|
|
{
|
|
return this._nativeRequest.statusText || "";
|
|
}
|
|
catch (anException)
|
|
{
|
|
return "";
|
|
}
|
|
};
|
|
|
|
CFHTTPRequest.prototype.readyState = function()
|
|
{
|
|
return this._nativeRequest.readyState;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.success = function()
|
|
{
|
|
var status = this.status();
|
|
|
|
if (status >= 200 && status < 300)
|
|
return YES;
|
|
|
|
// file:// requests return with status 0, to know if they succeeded, we
|
|
// need to know if there was any content.
|
|
return status === 0 && this.responseText() && this.responseText().length;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.responseXML = function()
|
|
{
|
|
var responseXML = this._nativeRequest.responseXML;
|
|
|
|
// Internet Explorer will return a non-null but empty request.responseXML if the response
|
|
// content type wasn't "text/html", so also check that responseXML.documentRoot is set.
|
|
// Otherwise fall back to regular parsing.
|
|
if (responseXML && (NativeRequest === window.XMLHttpRequest) && responseXML.documentRoot)
|
|
return responseXML;
|
|
|
|
return parseXML(this.responseText());
|
|
};
|
|
|
|
CFHTTPRequest.prototype.responsePropertyList = function()
|
|
{
|
|
var responseText = this.responseText();
|
|
|
|
if (CFPropertyList.sniffedFormatOfString(responseText) === CFPropertyList.FormatXML_v1_0)
|
|
return CFPropertyList.propertyListFromXML(this.responseXML());
|
|
|
|
return CFPropertyList.propertyListFromString(responseText);
|
|
};
|
|
|
|
CFHTTPRequest.prototype.responseText = function()
|
|
{
|
|
return this._nativeRequest.responseText;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.setRequestHeader = function(/*String*/ aHeader, /*Object*/ aValue)
|
|
{
|
|
this._requestHeaders[aHeader] = aValue;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.getResponseHeader = function(/*String*/ aHeader)
|
|
{
|
|
return this._nativeRequest.getResponseHeader(aHeader);
|
|
};
|
|
|
|
CFHTTPRequest.prototype.setTimeout = function(/*int*/ aTimeout)
|
|
{
|
|
this._timeout = aTimeout;
|
|
|
|
if (this._isOpen)
|
|
this._nativeRequest.timeout = aTimeout;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.getTimeout = function(/*int*/ aTimeout)
|
|
{
|
|
return this._timeout;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.getAllResponseHeaders = function()
|
|
{
|
|
return this._nativeRequest.getAllResponseHeaders();
|
|
};
|
|
|
|
CFHTTPRequest.prototype.overrideMimeType = function(/*String*/ aMimeType)
|
|
{
|
|
this._mimeType = aMimeType;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*Boolean*/ isAsynchronous, /*String*/ aUser, /*String*/ aPassword)
|
|
{
|
|
var retval;
|
|
|
|
this._isOpen = true;
|
|
this._URL = aURL;
|
|
this._async = isAsynchronous;
|
|
this._method = aMethod;
|
|
this._user = aUser;
|
|
this._password = aPassword;
|
|
|
|
requestReturnValue = this._nativeRequest.open(aMethod, aURL, isAsynchronous, aUser, aPassword);
|
|
|
|
if (this._async)
|
|
{
|
|
this._nativeRequest.withCredentials = this._withCredentials;
|
|
this._nativeRequest.timeout = this._timeout;
|
|
}
|
|
|
|
return requestReturnValue;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.send = function(/*Object*/ aBody)
|
|
{
|
|
if (!this._isOpen)
|
|
{
|
|
delete this._nativeRequest.onreadystatechange;
|
|
delete this._nativeRequest.ontimeout;
|
|
|
|
this._nativeRequest.open(this._method, this._URL, this._async, this._user, this._password);
|
|
this._nativeRequest.ontimeout = this._timeoutHandler;
|
|
this._nativeRequest.onreadystatechange = this._stateChangeHandler;
|
|
}
|
|
|
|
for (var i in this._requestHeaders)
|
|
{
|
|
if (this._requestHeaders.hasOwnProperty(i))
|
|
this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]);
|
|
}
|
|
|
|
if (this._mimeType && "overrideMimeType" in this._nativeRequest)
|
|
this._nativeRequest.overrideMimeType(this._mimeType);
|
|
|
|
this._isOpen = false;
|
|
|
|
try
|
|
{
|
|
return this._nativeRequest.send(aBody);
|
|
}
|
|
catch (anException)
|
|
{
|
|
// FIXME: Do something more complex, with 404's?
|
|
this._eventDispatcher.dispatchEvent({ type:"failure", request:this });
|
|
}
|
|
};
|
|
|
|
CFHTTPRequest.prototype.abort = function()
|
|
{
|
|
this._isOpen = false;
|
|
|
|
return this._nativeRequest.abort();
|
|
};
|
|
|
|
CFHTTPRequest.prototype.addEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
|
|
{
|
|
this._eventDispatcher.addEventListener(anEventName, anEventListener);
|
|
};
|
|
|
|
CFHTTPRequest.prototype.removeEventListener = function(/*String*/ anEventName, /*Function*/ anEventListener)
|
|
{
|
|
this._eventDispatcher.removeEventListener(anEventName, anEventListener);
|
|
};
|
|
|
|
CFHTTPRequest.prototype.setWithCredentials = function(/*Boolean*/ willSendWithCredentials)
|
|
{
|
|
this._withCredentials = willSendWithCredentials;
|
|
|
|
if (this._isOpen && this._async)
|
|
this._nativeRequest.withCredentials = willSendWithCredentials;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.withCredentials = function()
|
|
{
|
|
return this._withCredentials;
|
|
};
|
|
|
|
CFHTTPRequest.prototype.isTimeoutRequest = function()
|
|
{
|
|
// Can we consider that as a timeout ?
|
|
return !this.success() && !this._nativeRequest.response && !this._nativeRequest.responseText && !this._nativeRequest.responseType && !this._nativeRequest.responseURL && !this._nativeRequest.responseXML;
|
|
};
|
|
|
|
function dispatchTimeoutHTTPRequestEvents(/*CFHTTPRequest*/ aRequest)
|
|
{
|
|
aRequest._eventDispatcher.dispatchEvent({ type:"timeout", request:aRequest});
|
|
}
|
|
|
|
function determineAndDispatchHTTPRequestEvents(/*CFHTTPRequest*/ aRequest)
|
|
{
|
|
var eventDispatcher = aRequest._eventDispatcher,
|
|
readyStates = ["uninitialized", "loading", "loaded", "interactive", "complete"];
|
|
|
|
eventDispatcher.dispatchEvent({ type:"readystatechange", request:aRequest});
|
|
|
|
if (readyStates[aRequest.readyState()] === "complete")
|
|
{
|
|
var status = "HTTP" + aRequest.status();
|
|
eventDispatcher.dispatchEvent({ type:status, request:aRequest });
|
|
|
|
var result = aRequest.success() ? "success" : "failure";
|
|
eventDispatcher.dispatchEvent({ type:result, request:aRequest });
|
|
|
|
eventDispatcher.dispatchEvent({ type:readyStates[aRequest.readyState()], request:aRequest});
|
|
}
|
|
else
|
|
{
|
|
eventDispatcher.dispatchEvent({ type:readyStates[aRequest.readyState()], request:aRequest});
|
|
}
|
|
}
|
|
|
|
function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure, onprogress)
|
|
{
|
|
var request = new CFHTTPRequest();
|
|
|
|
if (aURL.pathExtension() === "plist")
|
|
request.overrideMimeType("text/xml");
|
|
|
|
#if COMMONJS
|
|
if (aURL.pathExtension().toLowerCase() === "j")
|
|
{
|
|
var aFilePath = aURL.toString().substring(5),
|
|
OS = require("os"),
|
|
gccFlags = require("objective-j").FileExecutable.currentGccCompilerFlags(),
|
|
chunk,
|
|
fileContents = "";
|
|
|
|
try
|
|
{
|
|
var gcc = OS.popen("gcc -E -x c -P " + (gccFlags ? gccFlags : "") + " " + OS.enquote(aFilePath), { charset:"UTF-8" });
|
|
while (chunk = gcc.stdout.read())
|
|
fileContents += chunk;
|
|
}
|
|
finally
|
|
{
|
|
gcc.stdin.close();
|
|
gcc.stdout.close();
|
|
gcc.stderr.close();
|
|
}
|
|
|
|
if (fileContents.length > 0)
|
|
{
|
|
request._nativeRequest.responseText = fileContents;
|
|
onsuccess({request: request});
|
|
}
|
|
else
|
|
{
|
|
onfailure({request: request});
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
var loaded = 0,
|
|
progressHandler = null;
|
|
|
|
function progress(progressEvent)
|
|
{
|
|
onprogress(progressEvent.loaded - loaded);
|
|
loaded = progressEvent.loaded;
|
|
}
|
|
|
|
function success(anEvent)
|
|
{
|
|
// If the browser can't handle progress events,
|
|
// we need to send a progress message with the total
|
|
// size of the file when the file finishes loading.
|
|
if (onprogress && progressHandler === null)
|
|
onprogress(anEvent.request.responseText().length);
|
|
|
|
onsuccess(anEvent);
|
|
}
|
|
|
|
if (exports.asyncLoader)
|
|
{
|
|
request.onsuccess = Asynchronous(success);
|
|
request.onfailure = Asynchronous(onfailure);
|
|
}
|
|
else
|
|
{
|
|
request.onsuccess = success;
|
|
request.onfailure = onfailure;
|
|
}
|
|
|
|
#if BROWSER
|
|
if (onprogress)
|
|
{
|
|
var supportsProgress = true;
|
|
|
|
// document.all means IE, window.atob is only supported by IE 10+
|
|
if (document.all)
|
|
supportsProgress = !!window.atob;
|
|
|
|
if (supportsProgress)
|
|
{
|
|
try
|
|
{
|
|
progressHandler = exports.asyncLoader ? Asynchronous(progress) : progress;
|
|
request._nativeRequest.onprogress = progressHandler;
|
|
}
|
|
catch (anException)
|
|
{
|
|
// Must be <= IE 9
|
|
progressHandler = null;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
request.open("GET", aURL.absoluteString(), exports.asyncLoader);
|
|
request.send("");
|
|
}
|
|
|
|
#ifdef BROWSER
|
|
exports.asyncLoader = YES;
|
|
#else
|
|
exports.asyncLoader = NO;
|
|
#endif
|
|
|
|
// FIXME: Get rid of these when we no longer need Mock requests in flatten.
|
|
exports.Asynchronous = Asynchronous;
|
|
exports.determineAndDispatchHTTPRequestEvents = determineAndDispatchHTTPRequestEvents;
|