You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
811 lines
22 KiB
811 lines
22 KiB
// -*- c-basic-offset: 2 -*-
|
|
/*
|
|
* This file is part of the KDE libraries
|
|
* Copyright (C) 2003 Apple Computer, 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 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
|
|
*/
|
|
|
|
#include "xmlhttprequest.h"
|
|
#include "xmlhttprequest.lut.h"
|
|
#include "kjs_window.h"
|
|
#include "kjs_events.h"
|
|
|
|
#include "dom/dom_doc.h"
|
|
#include "dom/dom_exception.h"
|
|
#include "dom/dom_string.h"
|
|
#include "misc/loader.h"
|
|
#include "html/html_documentimpl.h"
|
|
#include "xml/dom2_eventsimpl.h"
|
|
|
|
#include "khtml_part.h"
|
|
#include "khtmlview.h"
|
|
|
|
#include <kio/scheduler.h>
|
|
#include <kio/job.h>
|
|
#include <tqobject.h>
|
|
#include <kdebug.h>
|
|
|
|
#ifdef APPLE_CHANGES
|
|
#include "KWQLoader.h"
|
|
#else
|
|
#include <kio/netaccess.h>
|
|
using KIO::NetAccess;
|
|
#endif
|
|
|
|
#define BANNED_HTTP_HEADERS "authorization,proxy-authorization,"\
|
|
"content-length,host,connect,copy,move,"\
|
|
"delete,head,trace,put,propfind,proppatch,"\
|
|
"mkcol,lock,unlock,options,via,"\
|
|
"accept-charset,accept-encoding,expect,date,"\
|
|
"keep-alive,te,trailer,"\
|
|
"transfer-encoding,upgrade"
|
|
|
|
using khtml::Decoder;
|
|
|
|
namespace KJS {
|
|
|
|
////////////////////// XMLHttpRequest Object ////////////////////////
|
|
|
|
/* Source for XMLHttpRequestProtoTable.
|
|
@begin XMLHttpRequestProtoTable 7
|
|
abort XMLHttpRequest::Abort DontDelete|Function 0
|
|
getAllResponseHeaders XMLHttpRequest::GetAllResponseHeaders DontDelete|Function 0
|
|
getResponseHeader XMLHttpRequest::GetResponseHeader DontDelete|Function 1
|
|
open XMLHttpRequest::Open DontDelete|Function 5
|
|
overrideMimeType XMLHttpRequest::OverrideMIMEType DontDelete|Function 1
|
|
send XMLHttpRequest::Send DontDelete|Function 1
|
|
setRequestHeader XMLHttpRequest::SetRequestHeader DontDelete|Function 2
|
|
@end
|
|
*/
|
|
KJS_DEFINE_PROTOTYPE(XMLHttpRequestProto)
|
|
IMPLEMENT_PROTOFUNC_DOM(XMLHttpRequestProtoFunc)
|
|
KJS_IMPLEMENT_PROTOTYPE("XMLHttpRequest", XMLHttpRequestProto,XMLHttpRequestProtoFunc)
|
|
|
|
|
|
XMLHttpRequestQObject::XMLHttpRequestQObject(XMLHttpRequest *_jsObject)
|
|
{
|
|
jsObject = _jsObject;
|
|
}
|
|
|
|
#ifdef APPLE_CHANGES
|
|
void XMLHttpRequestQObject::slotData( KIO::Job* job, const char *data, int size )
|
|
{
|
|
jsObject->slotData(job, data, size);
|
|
}
|
|
#else
|
|
void XMLHttpRequestQObject::slotData( KIO::Job* job, const TQByteArray &data )
|
|
{
|
|
jsObject->slotData(job, data);
|
|
}
|
|
#endif
|
|
|
|
void XMLHttpRequestQObject::slotFinished( KIO::Job* job )
|
|
{
|
|
jsObject->slotFinished(job);
|
|
}
|
|
|
|
void XMLHttpRequestQObject::slotRedirection( KIO::Job* job, const KURL& url)
|
|
{
|
|
jsObject->slotRedirection( job, url );
|
|
}
|
|
|
|
XMLHttpRequestConstructorImp::XMLHttpRequestConstructorImp(ExecState *, const DOM::Document &d)
|
|
: ObjectImp(), doc(d)
|
|
{
|
|
}
|
|
|
|
bool XMLHttpRequestConstructorImp::implementsConstruct() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Object XMLHttpRequestConstructorImp::construct(ExecState *exec, const List &)
|
|
{
|
|
return Object(new XMLHttpRequest(exec, doc));
|
|
}
|
|
|
|
const ClassInfo XMLHttpRequest::info = { "XMLHttpRequest", 0, &XMLHttpRequestTable, 0 };
|
|
|
|
|
|
/* Source for XMLHttpRequestTable.
|
|
@begin XMLHttpRequestTable 7
|
|
readyState XMLHttpRequest::ReadyState DontDelete|ReadOnly
|
|
responseText XMLHttpRequest::ResponseText DontDelete|ReadOnly
|
|
responseXML XMLHttpRequest::ResponseXML DontDelete|ReadOnly
|
|
status XMLHttpRequest::Status DontDelete|ReadOnly
|
|
statusText XMLHttpRequest::StatusText DontDelete|ReadOnly
|
|
onreadystatechange XMLHttpRequest::Onreadystatechange DontDelete
|
|
onload XMLHttpRequest::Onload DontDelete
|
|
@end
|
|
*/
|
|
|
|
Value XMLHttpRequest::tryGet(ExecState *exec, const Identifier &propertyName) const
|
|
{
|
|
return DOMObjectLookupGetValue<XMLHttpRequest,DOMObject>(exec, propertyName, &XMLHttpRequestTable, this);
|
|
}
|
|
|
|
Value XMLHttpRequest::getValueProperty(ExecState *exec, int token) const
|
|
{
|
|
switch (token) {
|
|
case ReadyState:
|
|
return Number(state);
|
|
case ResponseText:
|
|
return getString(DOM::DOMString(response));
|
|
case ResponseXML:
|
|
if (state != Completed) {
|
|
return Null();
|
|
}
|
|
if (!createdDocument) {
|
|
TQString mimeType = "text/xml";
|
|
|
|
if (!m_mimeTypeOverride.isEmpty()) {
|
|
mimeType = m_mimeTypeOverride;
|
|
} else {
|
|
Value header = getResponseHeader("Content-Type");
|
|
if (header.type() != UndefinedType) {
|
|
mimeType = TQStringList::split(";", header.toString(exec).qstring())[0].stripWhiteSpace();
|
|
}
|
|
}
|
|
|
|
if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "application/xhtml+xml") {
|
|
responseXML = DOM::Document(doc->implementation()->createDocument());
|
|
|
|
DOM::DocumentImpl *docImpl = static_cast<DOM::DocumentImpl *>(responseXML.handle());
|
|
|
|
docImpl->open();
|
|
docImpl->write(response);
|
|
docImpl->finishParsing();
|
|
docImpl->close();
|
|
|
|
typeIsXML = true;
|
|
} else {
|
|
typeIsXML = false;
|
|
}
|
|
createdDocument = true;
|
|
}
|
|
|
|
if (!typeIsXML) {
|
|
return Undefined();
|
|
}
|
|
|
|
return getDOMNode(exec,responseXML);
|
|
case Status:
|
|
return getStatus();
|
|
case StatusText:
|
|
return getStatusText();
|
|
case Onreadystatechange:
|
|
if (onReadyStateChangeListener && onReadyStateChangeListener->listenerObjImp()) {
|
|
return onReadyStateChangeListener->listenerObj();
|
|
} else {
|
|
return Null();
|
|
}
|
|
case Onload:
|
|
if (onLoadListener && onLoadListener->listenerObjImp()) {
|
|
return onLoadListener->listenerObj();
|
|
} else {
|
|
return Null();
|
|
}
|
|
default:
|
|
kdWarning() << "XMLHttpRequest::getValueProperty unhandled token " << token << endl;
|
|
return Value();
|
|
}
|
|
}
|
|
|
|
void XMLHttpRequest::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
|
|
{
|
|
DOMObjectLookupPut<XMLHttpRequest,DOMObject>(exec, propertyName, value, attr, &XMLHttpRequestTable, this );
|
|
}
|
|
|
|
void XMLHttpRequest::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/)
|
|
{
|
|
JSEventListener* newListener;
|
|
switch(token) {
|
|
case Onreadystatechange:
|
|
newListener = Window::retrieveActive(exec)->getJSEventListener(value, true);
|
|
if (newListener != onReadyStateChangeListener) {
|
|
if (onReadyStateChangeListener) onReadyStateChangeListener->deref();
|
|
onReadyStateChangeListener = newListener;
|
|
if (onReadyStateChangeListener) onReadyStateChangeListener->ref();
|
|
}
|
|
break;
|
|
case Onload:
|
|
newListener = Window::retrieveActive(exec)->getJSEventListener(value, true);
|
|
if (newListener != onLoadListener) {
|
|
if (onLoadListener) onLoadListener->deref();
|
|
onLoadListener = newListener;
|
|
if (onLoadListener) onLoadListener->ref();
|
|
}
|
|
break;
|
|
default:
|
|
kdWarning() << "XMLHttpRequest::putValue unhandled token " << token << endl;
|
|
}
|
|
}
|
|
|
|
XMLHttpRequest::XMLHttpRequest(ExecState *exec, const DOM::Document &d)
|
|
: DOMObject(XMLHttpRequestProto::self(exec)),
|
|
qObject(new XMLHttpRequestQObject(this)),
|
|
doc(static_cast<DOM::DocumentImpl*>(d.handle())),
|
|
async(true),
|
|
contentType(TQString::null),
|
|
job(0),
|
|
state(Uninitialized),
|
|
onReadyStateChangeListener(0),
|
|
onLoadListener(0),
|
|
decoder(0),
|
|
createdDocument(false),
|
|
aborted(false)
|
|
{
|
|
}
|
|
|
|
XMLHttpRequest::~XMLHttpRequest()
|
|
{
|
|
if (onReadyStateChangeListener)
|
|
onReadyStateChangeListener->deref();
|
|
if (onLoadListener)
|
|
onLoadListener->deref();
|
|
delete qObject;
|
|
qObject = 0;
|
|
delete decoder;
|
|
decoder = 0;
|
|
}
|
|
|
|
void XMLHttpRequest::changeState(XMLHttpRequestState newState)
|
|
{
|
|
if (state != newState) {
|
|
state = newState;
|
|
|
|
ref();
|
|
|
|
if (onReadyStateChangeListener != 0 && doc->view() && doc->view()->part()) {
|
|
DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents");
|
|
ev.initEvent("readystatechange", true, true);
|
|
onReadyStateChangeListener->handleEvent(ev);
|
|
}
|
|
|
|
if (state == Completed && onLoadListener != 0 && doc->view() && doc->view()->part()) {
|
|
DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents");
|
|
ev.initEvent("load", true, true);
|
|
onLoadListener->handleEvent(ev);
|
|
}
|
|
|
|
deref();
|
|
}
|
|
}
|
|
|
|
bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& _url) const
|
|
{
|
|
// No need to do work if _url is not valid...
|
|
if (!_url.isValid())
|
|
return false;
|
|
|
|
KURL documentURL(doc->URL());
|
|
|
|
// a local file can load anything
|
|
if (documentURL.protocol().lower() == "file") {
|
|
return true;
|
|
}
|
|
|
|
// but a remote document can only load from the same port on the server
|
|
if (documentURL.protocol().lower() == _url.protocol().lower() &&
|
|
documentURL.host().lower() == _url.host().lower() &&
|
|
documentURL.port() == _url.port()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void XMLHttpRequest::open(const TQString& _method, const KURL& _url, bool _async)
|
|
{
|
|
abort();
|
|
aborted = false;
|
|
|
|
// clear stuff from possible previous load
|
|
requestHeaders.clear();
|
|
responseHeaders = TQString();
|
|
response = TQString();
|
|
createdDocument = false;
|
|
responseXML = DOM::Document();
|
|
|
|
changeState(Uninitialized);
|
|
|
|
if (aborted) {
|
|
return;
|
|
}
|
|
|
|
if (!urlMatchesDocumentDomain(_url)) {
|
|
return;
|
|
}
|
|
|
|
|
|
method = _method.lower();
|
|
url = _url;
|
|
async = _async;
|
|
|
|
changeState(Loading);
|
|
}
|
|
|
|
void XMLHttpRequest::send(const TQString& _body)
|
|
{
|
|
aborted = false;
|
|
|
|
const TQString protocol = url.protocol().lower();
|
|
// Abandon the request when the protocol is other than "http",
|
|
// instead of blindly doing a KIO::get on other protocols like file:/.
|
|
if (!protocol.startsWith("http") && !protocol.startsWith("webdav"))
|
|
{
|
|
abort();
|
|
return;
|
|
}
|
|
|
|
if (method == "post") {
|
|
|
|
// FIXME: determine post encoding correctly by looking in headers
|
|
// for charset.
|
|
TQByteArray buf;
|
|
TQCString str = _body.utf8();
|
|
buf.duplicate(str.data(), str.size() - 1);
|
|
|
|
job = KIO::http_post( url, buf, false );
|
|
if(contentType.isNull())
|
|
job->addMetaData( "content-type", "Content-type: text/plain" );
|
|
else
|
|
job->addMetaData( "content-type", contentType );
|
|
}
|
|
else {
|
|
job = KIO::get( url, false, false );
|
|
}
|
|
|
|
if (!requestHeaders.isEmpty()) {
|
|
TQString rh;
|
|
TQMap<TQString, TQString>::ConstIterator begin = requestHeaders.begin();
|
|
TQMap<TQString, TQString>::ConstIterator end = requestHeaders.end();
|
|
for (TQMap<TQString, TQString>::ConstIterator i = begin; i != end; ++i) {
|
|
TQString key = i.key();
|
|
TQString value = i.data();
|
|
if (key == "accept") {
|
|
// The HTTP KIO slave supports an override this way
|
|
job->addMetaData("accept", value);
|
|
} else {
|
|
if (i != begin)
|
|
rh += "\r\n";
|
|
rh += key + ": " + value;
|
|
}
|
|
}
|
|
|
|
job->addMetaData("customHTTPHeader", rh);
|
|
}
|
|
|
|
job->addMetaData("PropagateHttpHeader", "true");
|
|
|
|
// Set the default referrer if one is not already supplied
|
|
// through setRequestHeader. NOTE: the user can still disable
|
|
// this feature at the protocol level (kio_http).
|
|
// ### does find() ever succeed? the headers are stored in lower case!
|
|
if (requestHeaders.tqfind("Referer") == requestHeaders.end()) {
|
|
KURL documentURL(doc->URL());
|
|
documentURL.setPass(TQString::null);
|
|
documentURL.setUser(TQString::null);
|
|
job->addMetaData("referrer", documentURL.url());
|
|
// kdDebug() << "Adding referrer: " << documentURL << endl;
|
|
}
|
|
|
|
if (!async) {
|
|
TQByteArray data;
|
|
KURL finalURL;
|
|
TQString headers;
|
|
|
|
#ifdef APPLE_CHANGES
|
|
data = KWQServeSynchronousRequest(khtml::Cache::loader(), doc->docLoader(), job, finalURL, headers);
|
|
#else
|
|
TQMap<TQString, TQString> metaData;
|
|
if ( NetAccess::synchronousRun( job, 0, &data, &finalURL, &metaData ) ) {
|
|
headers = metaData[ "HTTP-Headers" ];
|
|
}
|
|
#endif
|
|
job = 0;
|
|
processSyncLoadResults(data, finalURL, headers);
|
|
return;
|
|
}
|
|
|
|
qObject->connect( job, TQT_SIGNAL( result( KIO::Job* ) ),
|
|
TQT_SLOT( slotFinished( KIO::Job* ) ) );
|
|
#ifdef APPLE_CHANGES
|
|
qObject->connect( job, TQT_SIGNAL( data( KIO::Job*, const char*, int ) ),
|
|
TQT_SLOT( slotData( KIO::Job*, const char*, int ) ) );
|
|
#else
|
|
qObject->connect( job, TQT_SIGNAL( data( KIO::Job*, const TQByteArray& ) ),
|
|
TQT_SLOT( slotData( KIO::Job*, const TQByteArray& ) ) );
|
|
#endif
|
|
qObject->connect( job, TQT_SIGNAL(redirection(KIO::Job*, const KURL& ) ),
|
|
TQT_SLOT( slotRedirection(KIO::Job*, const KURL&) ) );
|
|
|
|
#ifdef APPLE_CHANGES
|
|
KWQServeRequest(khtml::Cache::loader(), doc->docLoader(), job);
|
|
#else
|
|
KIO::Scheduler::scheduleJob( job );
|
|
#endif
|
|
}
|
|
|
|
void XMLHttpRequest::abort()
|
|
{
|
|
if (job) {
|
|
job->kill();
|
|
job = 0;
|
|
}
|
|
delete decoder;
|
|
decoder = 0;
|
|
aborted = true;
|
|
}
|
|
|
|
void XMLHttpRequest::overrideMIMEType(const TQString& override)
|
|
{
|
|
m_mimeTypeOverride = override;
|
|
}
|
|
|
|
void XMLHttpRequest::setRequestHeader(const TQString& _name, const TQString &value)
|
|
{
|
|
TQString name = _name.lower().stripWhiteSpace();
|
|
|
|
// Content-type needs to be set seperately from the other headers
|
|
if(name == "content-type") {
|
|
contentType = "Content-type: " + value;
|
|
return;
|
|
}
|
|
|
|
// Sanitize the referrer header to protect against spoofing...
|
|
if(name == "referer") {
|
|
KURL referrerURL(value);
|
|
if (urlMatchesDocumentDomain(referrerURL))
|
|
requestHeaders[name] = referrerURL.url();
|
|
return;
|
|
}
|
|
|
|
// Sanitize the request headers below and handle them as if they are
|
|
// calls to open. Otherwise, we will end up ignoring them all together!
|
|
// TODO: Do something about "put" which kio_http sort of supports and
|
|
// the webDAV headers such as PROPFIND etc...
|
|
if (name == "get" || name == "post") {
|
|
KURL reqURL (doc->URL(), value.stripWhiteSpace());
|
|
open(name, reqURL, async);
|
|
return;
|
|
}
|
|
|
|
// Reject all banned headers. See BANNED_HTTP_HEADERS above.
|
|
// kdDebug() << "Banned HTTP Headers: " << BANNED_HTTP_HEADERS << endl;
|
|
TQStringList bannedHeaders = TQStringList::split(',',
|
|
TQString::tqfromLatin1(BANNED_HTTP_HEADERS));
|
|
|
|
if (bannedHeaders.tqcontains(name))
|
|
return; // Denied
|
|
|
|
requestHeaders[name] = value.stripWhiteSpace();
|
|
}
|
|
|
|
Value XMLHttpRequest::getAllResponseHeaders() const
|
|
{
|
|
if (responseHeaders.isEmpty()) {
|
|
return Undefined();
|
|
}
|
|
|
|
int endOfLine = responseHeaders.tqfind("\n");
|
|
|
|
if (endOfLine == -1) {
|
|
return Undefined();
|
|
}
|
|
|
|
return String(responseHeaders.mid(endOfLine + 1) + "\n");
|
|
}
|
|
|
|
Value XMLHttpRequest::getResponseHeader(const TQString& name) const
|
|
{
|
|
if (responseHeaders.isEmpty()) {
|
|
return Undefined();
|
|
}
|
|
|
|
TQRegExp headerLinePattern(name + ":", false);
|
|
|
|
int matchLength;
|
|
int headerLinePos = headerLinePattern.search(responseHeaders, 0);
|
|
matchLength = headerLinePattern.matchedLength();
|
|
while (headerLinePos != -1) {
|
|
if (headerLinePos == 0 || responseHeaders[headerLinePos-1] == '\n') {
|
|
break;
|
|
}
|
|
|
|
headerLinePos = headerLinePattern.search(responseHeaders, headerLinePos + 1);
|
|
matchLength = headerLinePattern.matchedLength();
|
|
}
|
|
|
|
|
|
if (headerLinePos == -1) {
|
|
return Undefined();
|
|
}
|
|
|
|
int endOfLine = responseHeaders.tqfind("\n", headerLinePos + matchLength);
|
|
|
|
return String(responseHeaders.mid(headerLinePos + matchLength, endOfLine - (headerLinePos + matchLength)).stripWhiteSpace());
|
|
}
|
|
|
|
static Value httpStatus(const TQString& response, bool textStatus = false)
|
|
{
|
|
if (response.isEmpty()) {
|
|
return Undefined();
|
|
}
|
|
|
|
int endOfLine = response.tqfind("\n");
|
|
TQString firstLine = (endOfLine == -1) ? response : response.left(endOfLine);
|
|
int codeStart = firstLine.tqfind(" ");
|
|
int codeEnd = firstLine.tqfind(" ", codeStart + 1);
|
|
|
|
if (codeStart == -1 || codeEnd == -1) {
|
|
return Undefined();
|
|
}
|
|
|
|
if (textStatus) {
|
|
TQString statusText = firstLine.mid(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace();
|
|
return String(statusText);
|
|
}
|
|
|
|
TQString number = firstLine.mid(codeStart + 1, codeEnd - (codeStart + 1));
|
|
|
|
bool ok = false;
|
|
int code = number.toInt(&ok);
|
|
if (!ok) {
|
|
return Undefined();
|
|
}
|
|
|
|
return Number(code);
|
|
}
|
|
|
|
Value XMLHttpRequest::getStatus() const
|
|
{
|
|
return httpStatus(responseHeaders);
|
|
}
|
|
|
|
Value XMLHttpRequest::getStatusText() const
|
|
{
|
|
return httpStatus(responseHeaders, true);
|
|
}
|
|
|
|
void XMLHttpRequest::processSyncLoadResults(const TQByteArray &data, const KURL &finalURL, const TQString &headers)
|
|
{
|
|
if (!urlMatchesDocumentDomain(finalURL)) {
|
|
abort();
|
|
return;
|
|
}
|
|
|
|
responseHeaders = headers;
|
|
changeState(Loaded);
|
|
if (aborted) {
|
|
return;
|
|
}
|
|
|
|
#ifdef APPLE_CHANGES
|
|
const char *bytes = (const char *)data.data();
|
|
int len = (int)data.size();
|
|
|
|
slotData(0, bytes, len);
|
|
#else
|
|
slotData(0, data);
|
|
#endif
|
|
|
|
if (aborted) {
|
|
return;
|
|
}
|
|
|
|
slotFinished(0);
|
|
}
|
|
|
|
void XMLHttpRequest::slotFinished(KIO::Job *)
|
|
{
|
|
if (decoder) {
|
|
response += decoder->flush();
|
|
}
|
|
|
|
// make sure to forget about the job before emitting completed,
|
|
// since changeState triggers JS code, which might e.g. call abort.
|
|
job = 0;
|
|
changeState(Completed);
|
|
|
|
delete decoder;
|
|
decoder = 0;
|
|
}
|
|
|
|
void XMLHttpRequest::slotRedirection(KIO::Job*, const KURL& url)
|
|
{
|
|
if (!urlMatchesDocumentDomain(url)) {
|
|
abort();
|
|
}
|
|
}
|
|
|
|
#ifdef APPLE_CHANGES
|
|
void XMLHttpRequest::slotData( KIO::Job*, const char *data, int len )
|
|
#else
|
|
void XMLHttpRequest::slotData(KIO::Job*, const TQByteArray &_data)
|
|
#endif
|
|
{
|
|
if (state < Loaded ) {
|
|
responseHeaders = job->queryMetaData("HTTP-Headers");
|
|
|
|
// NOTE: Replace a 304 response with a 200! Both IE and Mozilla do this.
|
|
// Problem first reported through bug# 110272.
|
|
int codeStart = responseHeaders.tqfind("304");
|
|
if ( codeStart != -1) {
|
|
int codeEnd = responseHeaders.tqfind("\n", codeStart+3);
|
|
if (codeEnd != -1)
|
|
responseHeaders.replace(codeStart, (codeEnd-codeStart), "200 OK");
|
|
}
|
|
|
|
changeState(Loaded);
|
|
}
|
|
|
|
#ifndef APPLE_CHANGES
|
|
const char *data = (const char *)_data.data();
|
|
int len = (int)_data.size();
|
|
#endif
|
|
|
|
if ( decoder == NULL ) {
|
|
int pos = responseHeaders.tqfind("content-type:", 0, false);
|
|
|
|
if ( pos > -1 ) {
|
|
pos += 13;
|
|
int index = responseHeaders.tqfind('\n', pos);
|
|
TQString type = responseHeaders.mid(pos, (index-pos));
|
|
index = type.tqfind (';');
|
|
if (index > -1)
|
|
encoding = TQString(type.mid( index+1 ).remove(TQRegExp("charset[ ]*=[ ]*", false))).stripWhiteSpace();
|
|
}
|
|
|
|
decoder = new Decoder;
|
|
if (!encoding.isNull())
|
|
decoder->setEncoding(encoding.latin1(), Decoder::EncodingFromHTTPHeader);
|
|
else {
|
|
// Per section 2 of W3C working draft spec, fall back to "UTF-8".
|
|
decoder->setEncoding("UTF-8", Decoder::DefaultEncoding);
|
|
}
|
|
}
|
|
if (len == 0)
|
|
return;
|
|
|
|
if (len == -1)
|
|
len = strlen(data);
|
|
|
|
TQString decoded = decoder->decode(data, len);
|
|
|
|
response += decoded;
|
|
|
|
if (!aborted) {
|
|
changeState(Interactive);
|
|
}
|
|
}
|
|
|
|
Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
|
|
{
|
|
if (!thisObj.inherits(&XMLHttpRequest::info)) {
|
|
Object err = Error::create(exec,TypeError);
|
|
exec->setException(err);
|
|
return err;
|
|
}
|
|
|
|
XMLHttpRequest *request = static_cast<XMLHttpRequest *>(thisObj.imp());
|
|
switch (id) {
|
|
case XMLHttpRequest::Abort:
|
|
request->abort();
|
|
return Undefined();
|
|
case XMLHttpRequest::GetAllResponseHeaders:
|
|
if (args.size() != 0) {
|
|
return Undefined();
|
|
}
|
|
|
|
return request->getAllResponseHeaders();
|
|
case XMLHttpRequest::GetResponseHeader:
|
|
if (args.size() != 1) {
|
|
return Undefined();
|
|
}
|
|
|
|
return request->getResponseHeader(args[0].toString(exec).qstring());
|
|
case XMLHttpRequest::Open:
|
|
{
|
|
if (args.size() < 2 || args.size() > 5) {
|
|
return Undefined();
|
|
}
|
|
|
|
TQString method = args[0].toString(exec).qstring();
|
|
KHTMLPart *part = ::tqqt_cast<KHTMLPart *>(Window::retrieveActive(exec)->part());
|
|
if (!part)
|
|
return Undefined();
|
|
KURL url = KURL(part->document().completeURL(args[1].toString(exec).qstring()).string());
|
|
|
|
bool async = true;
|
|
if (args.size() >= 3) {
|
|
async = args[2].toBoolean(exec);
|
|
}
|
|
|
|
if (args.size() >= 4) {
|
|
url.setUser(args[3].toString(exec).qstring());
|
|
}
|
|
|
|
if (args.size() >= 5) {
|
|
url.setPass(args[4].toString(exec).qstring());
|
|
}
|
|
|
|
request->open(method, url, async);
|
|
|
|
return Undefined();
|
|
}
|
|
case XMLHttpRequest::Send:
|
|
{
|
|
if (args.size() > 1) {
|
|
return Undefined();
|
|
}
|
|
|
|
if (request->state != Loading) {
|
|
return Undefined();
|
|
}
|
|
|
|
TQString body;
|
|
if (args.size() >= 1) {
|
|
Object obj = Object::dynamicCast(args[0]);
|
|
if (obj.isValid() && obj.inherits(&DOMDocument::info)) {
|
|
DOM::Node docNode = static_cast<KJS::DOMDocument *>(obj.imp())->toNode();
|
|
DOM::DocumentImpl *doc = static_cast<DOM::DocumentImpl *>(docNode.handle());
|
|
|
|
try {
|
|
body = doc->toString().string();
|
|
// FIXME: also need to set content type, including encoding!
|
|
|
|
} catch(DOM::DOMException& e) {
|
|
Object err = Error::create(exec, GeneralError, "Exception serializing document");
|
|
exec->setException(err);
|
|
}
|
|
} else {
|
|
body = args[0].toString(exec).qstring();
|
|
}
|
|
}
|
|
|
|
request->send(body);
|
|
|
|
return Undefined();
|
|
}
|
|
case XMLHttpRequest::SetRequestHeader:
|
|
if (args.size() != 2) {
|
|
return Undefined();
|
|
}
|
|
|
|
request->setRequestHeader(args[0].toString(exec).qstring(), args[1].toString(exec).qstring());
|
|
|
|
return Undefined();
|
|
|
|
case XMLHttpRequest::OverrideMIMEType:
|
|
if (args.size() < 1) {
|
|
Object err = Error::create(exec, SyntaxError, "Not enough arguments");
|
|
exec->setException(err);
|
|
return err;
|
|
}
|
|
|
|
request->overrideMIMEType(args[0].toString(exec).qstring());
|
|
return Undefined();
|
|
}
|
|
|
|
return Undefined();
|
|
}
|
|
|
|
} // end namespace
|
|
|
|
|
|
#include "xmlhttprequest.moc"
|