00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
#include "xmlhttprequest.h"
00022
#include "xmlhttprequest.lut.h"
00023
#include "kjs_window.h"
00024
#include "kjs_events.h"
00025
00026
#include "dom/dom_doc.h"
00027
#include "dom/dom_exception.h"
00028
#include "dom/dom_string.h"
00029
#include "misc/loader.h"
00030
#include "html/html_documentimpl.h"
00031
#include "xml/dom2_eventsimpl.h"
00032
00033
#include "khtml_part.h"
00034
#include "khtmlview.h"
00035
00036
#include <kio/scheduler.h>
00037
#include <kio/job.h>
00038
#include <qobject.h>
00039
#include <kdebug.h>
00040
00041
#ifdef APPLE_CHANGES
00042
#include "KWQLoader.h"
00043
#else
00044
#include <kio/netaccess.h>
00045
using KIO::NetAccess;
00046
#endif
00047
00048
using namespace KJS;
00049
using khtml::Decoder;
00050
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063 DEFINE_PROTOTYPE(
"XMLHttpRequest",XMLHttpRequestProto)
00064 IMPLEMENT_PROTOFUNC_DOM(XMLHttpRequestProtoFunc)
00065 IMPLEMENT_PROTOTYPE(XMLHttpRequestProto,XMLHttpRequestProtoFunc)
00066
00067 namespace KJS {
00068
00069 XMLHttpRequestQObject::XMLHttpRequestQObject(XMLHttpRequest *_jsObject)
00070 {
00071 jsObject = _jsObject;
00072 }
00073
00074
#ifdef APPLE_CHANGES
00075
void XMLHttpRequestQObject::slotData(
KIO::Job* job,
const char *data,
int size )
00076 {
00077 jsObject->slotData(job, data, size);
00078 }
00079
#else
00080
void XMLHttpRequestQObject::slotData(
KIO::Job* job,
const QByteArray &data )
00081 {
00082 jsObject->slotData(job, data);
00083 }
00084
#endif
00085
00086
void XMLHttpRequestQObject::slotFinished(
KIO::Job* job )
00087 {
00088 jsObject->slotFinished(job);
00089 }
00090
00091
void XMLHttpRequestQObject::slotRedirection(
KIO::Job* job,
const KURL& url)
00092 {
00093 jsObject->slotRedirection( job, url );
00094 }
00095
00096 XMLHttpRequestConstructorImp::XMLHttpRequestConstructorImp(ExecState *,
const DOM::Document &d)
00097 : ObjectImp(), doc(d)
00098 {
00099 }
00100
00101
bool XMLHttpRequestConstructorImp::implementsConstruct()
const
00102
{
00103
return true;
00104 }
00105
00106 Object XMLHttpRequestConstructorImp::construct(ExecState *exec,
const List &)
00107 {
00108
return Object(
new XMLHttpRequest(exec, doc));
00109 }
00110
00111
const ClassInfo XMLHttpRequest::info = {
"XMLHttpRequest", 0, &XMLHttpRequestTable, 0 };
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126 Value XMLHttpRequest::tryGet(ExecState *exec,
const Identifier &propertyName)
const
00127
{
00128
return DOMObjectLookupGetValue<XMLHttpRequest,DOMObject>(exec, propertyName, &XMLHttpRequestTable,
this);
00129 }
00130
00131 Value XMLHttpRequest::getValueProperty(ExecState *exec,
int token)
const
00132
{
00133
switch (token) {
00134
case ReadyState:
00135
return Number(state);
00136
case ResponseText:
00137
return getString(
DOM::DOMString(response));
00138
case ResponseXML:
00139
if (state != Completed) {
00140
return Undefined();
00141 }
00142
if (!createdDocument) {
00143
QString mimeType =
"text/xml";
00144
00145 Value header = getResponseHeader(
"Content-Type");
00146
if (header.type() != UndefinedType) {
00147 mimeType = QStringList::split(
";", header.toString(exec).qstring())[0].stripWhiteSpace();
00148 }
00149
00150
if (mimeType ==
"text/xml" || mimeType ==
"application/xml" || mimeType ==
"application/xhtml+xml") {
00151 responseXML =
DOM::Document(doc->implementation()->createDocument());
00152
00153 DOM::DocumentImpl *docImpl = static_cast<DOM::DocumentImpl *>(responseXML.handle());
00154
00155 docImpl->open();
00156 docImpl->write(response);
00157 docImpl->finishParsing();
00158 docImpl->close();
00159
00160 typeIsXML =
true;
00161 }
else {
00162 typeIsXML =
false;
00163 }
00164 createdDocument =
true;
00165 }
00166
00167
if (!typeIsXML) {
00168
return Undefined();
00169 }
00170
00171
return getDOMNode(exec,responseXML);
00172
case Status:
00173
return getStatus();
00174
case StatusText:
00175
return getStatusText();
00176
case Onreadystatechange:
00177
if (onReadyStateChangeListener && onReadyStateChangeListener->listenerObjImp()) {
00178
return onReadyStateChangeListener->listenerObj();
00179 }
else {
00180
return Null();
00181 }
00182
case Onload:
00183
if (onLoadListener && onLoadListener->listenerObjImp()) {
00184
return onLoadListener->listenerObj();
00185 }
else {
00186
return Null();
00187 }
00188
default:
00189
kdWarning() <<
"XMLHttpRequest::getValueProperty unhandled token " << token <<
endl;
00190
return Value();
00191 }
00192 }
00193
00194
void XMLHttpRequest::tryPut(ExecState *exec,
const Identifier &propertyName,
const Value& value,
int attr)
00195 {
00196 DOMObjectLookupPut<XMLHttpRequest,DOMObject>(exec, propertyName, value, attr, &XMLHttpRequestTable,
this );
00197 }
00198
00199
void XMLHttpRequest::putValueProperty(ExecState *exec,
int token,
const Value& value,
int )
00200 {
00201
switch(token) {
00202
case Onreadystatechange:
00203 onReadyStateChangeListener = Window::retrieveActive(exec)->getJSEventListener(value,
true);
00204
if (onReadyStateChangeListener) onReadyStateChangeListener->ref();
00205
break;
00206
case Onload:
00207 onLoadListener = Window::retrieveActive(exec)->getJSEventListener(value,
true);
00208
if (onLoadListener) onLoadListener->ref();
00209
break;
00210
default:
00211
kdWarning() <<
"XMLHttpRequest::putValue unhandled token " << token <<
endl;
00212 }
00213 }
00214
00215 XMLHttpRequest::XMLHttpRequest(ExecState *exec,
const DOM::Document &d)
00216 :
DOMObject(XMLHttpRequestProto::self(exec)),
00217 qObject(new XMLHttpRequestQObject(this)),
00218 doc(static_cast<DOM::DocumentImpl*>(d.handle())),
00219 async(true),
00220 contentType(
QString::null),
00221 job(0),
00222 state(Uninitialized),
00223 onReadyStateChangeListener(0),
00224 onLoadListener(0),
00225 decoder(0),
00226 createdDocument(false),
00227 aborted(false)
00228 {
00229 }
00230
00231 XMLHttpRequest::~XMLHttpRequest()
00232 {
00233
delete qObject;
00234 qObject = 0;
00235
delete decoder;
00236 decoder = 0;
00237 }
00238
00239
void XMLHttpRequest::changeState(XMLHttpRequestState newState)
00240 {
00241
if (state != newState) {
00242 state = newState;
00243
00244
if (onReadyStateChangeListener != 0 && doc->view() && doc->view()->part()) {
00245
DOM::Event ev = doc->view()->part()->document().createEvent(
"HTMLEvents");
00246 ev.
initEvent(
"readystatechange",
true,
true);
00247 onReadyStateChangeListener->handleEvent(ev);
00248 }
00249
00250
if (state == Completed && onLoadListener != 0 && doc->view() && doc->view()->part()) {
00251
DOM::Event ev = doc->view()->part()->document().createEvent(
"HTMLEvents");
00252 ev.
initEvent(
"load",
true,
true);
00253 onLoadListener->handleEvent(ev);
00254 }
00255 }
00256 }
00257
00258
bool XMLHttpRequest::urlMatchesDocumentDomain(
const KURL& _url)
const
00259
{
00260
KURL documentURL(doc->URL());
00261
00262
00263
if (documentURL.
protocol().lower() ==
"file") {
00264
return true;
00265 }
00266
00267
00268
if (documentURL.
protocol().lower() == _url.
protocol().lower() &&
00269 documentURL.
host().lower() == _url.
host().lower() &&
00270 documentURL.
port() == _url.
port()) {
00271
return true;
00272 }
00273
00274
return false;
00275 }
00276
00277
void XMLHttpRequest::open(
const QString& _method,
const KURL& _url,
bool _async)
00278 {
00279 abort();
00280 aborted =
false;
00281
00282
00283 requestHeaders =
QString();
00284 responseHeaders = QString();
00285 response = QString();
00286 createdDocument =
false;
00287 responseXML =
DOM::Document();
00288
00289 changeState(Uninitialized);
00290
00291
if (aborted) {
00292
return;
00293 }
00294
00295
if (!urlMatchesDocumentDomain(_url)) {
00296
return;
00297 }
00298
00299
00300 method = _method;
00301
url = _url;
00302 async = _async;
00303
00304 changeState(Loading);
00305 }
00306
00307
void XMLHttpRequest::send(
const QString& _body)
00308 {
00309 aborted =
false;
00310
if (method.lower() ==
"post" && (
url.
protocol().lower() ==
"http" ||
url.
protocol().lower() ==
"https") ) {
00311
00312 job =
KIO::http_post( url,
QCString(_body.utf8()),
false );
00313
if(contentType.isNull())
00314 job->addMetaData(
"content-type",
"Content-type: text/plain" );
00315
else
00316 job->addMetaData(
"content-type", contentType );
00317 }
00318
else
00319 {
00320 job =
KIO::get( url,
false,
false );
00321 }
00322
if (requestHeaders.length() > 0) {
00323 job->addMetaData(
"customHTTPHeader", requestHeaders);
00324 }
00325 job->addMetaData(
"PropagateHttpHeader",
"true" );
00326
00327
if (!async) {
00328
QByteArray data;
00329
KURL finalURL;
00330
QString headers;
00331
00332
#ifdef APPLE_CHANGES
00333
data = KWQServeSynchronousRequest(khtml::Cache::loader(), doc->docLoader(), job, finalURL, headers);
00334
#else
00335
QMap<QString, QString> metaData;
00336
if ( NetAccess::synchronousRun( job, 0, &data, &finalURL, &metaData ) ) {
00337 headers = metaData[
"HTTP-Headers" ];
00338 }
00339
#endif
00340
job = 0;
00341 processSyncLoadResults(data, finalURL, headers);
00342
return;
00343 }
00344
00345 qObject->connect( job, SIGNAL( result(
KIO::Job* ) ),
00346 SLOT( slotFinished(
KIO::Job* ) ) );
00347
#ifdef APPLE_CHANGES
00348
qObject->connect( job, SIGNAL( data(
KIO::Job*,
const char*,
int ) ),
00349 SLOT( slotData(
KIO::Job*,
const char*,
int ) ) );
00350
#else
00351
qObject->connect( job, SIGNAL( data(
KIO::Job*,
const QByteArray& ) ),
00352 SLOT( slotData(
KIO::Job*,
const QByteArray& ) ) );
00353
#endif
00354
qObject->connect( job, SIGNAL(redirection(
KIO::Job*,
const KURL& ) ),
00355 SLOT( slotRedirection(
KIO::Job*,
const KURL&) ) );
00356
00357
#ifdef APPLE_CHANGES
00358
KWQServeRequest(khtml::Cache::loader(), doc->docLoader(), job);
00359
#else
00360
KIO::Scheduler::scheduleJob( job );
00361
#endif
00362
}
00363
00364
void XMLHttpRequest::abort()
00365 {
00366
if (job) {
00367 job->kill();
00368 job = 0;
00369 }
00370
delete decoder;
00371 decoder = 0;
00372 aborted =
true;
00373 }
00374
00375
void XMLHttpRequest::setRequestHeader(
const QString& name,
const QString &value)
00376 {
00377
00378
if(
name.lower() ==
"content-type") {
00379 contentType =
"Content-type: " + value;
00380
return;
00381 }
00382
if (requestHeaders.length() > 0) {
00383 requestHeaders +=
"\r\n";
00384 }
00385 requestHeaders +=
name;
00386 requestHeaders +=
": ";
00387 requestHeaders += value;
00388 }
00389
00390 Value XMLHttpRequest::getAllResponseHeaders()
const
00391
{
00392
if (responseHeaders.isEmpty()) {
00393
return Undefined();
00394 }
00395
00396
int endOfLine = responseHeaders.find(
"\n");
00397
00398
if (
endOfLine == -1) {
00399
return Undefined();
00400 }
00401
00402
return String(responseHeaders.mid(endOfLine + 1) +
"\n");
00403 }
00404
00405 Value XMLHttpRequest::getResponseHeader(
const QString& name)
const
00406
{
00407
if (responseHeaders.isEmpty()) {
00408
return Undefined();
00409 }
00410
00411
QRegExp headerLinePattern(name +
":",
false);
00412
00413
int matchLength;
00414
int headerLinePos = headerLinePattern.search(responseHeaders, 0);
00415 matchLength = headerLinePattern.matchedLength();
00416
while (headerLinePos != -1) {
00417
if (headerLinePos == 0 || responseHeaders[headerLinePos-1] ==
'\n') {
00418
break;
00419 }
00420
00421 headerLinePos = headerLinePattern.search(responseHeaders, headerLinePos + 1);
00422 matchLength = headerLinePattern.matchedLength();
00423 }
00424
00425
00426
if (headerLinePos == -1) {
00427
return Undefined();
00428 }
00429
00430
int endOfLine = responseHeaders.find(
"\n", headerLinePos + matchLength);
00431
00432
return String(responseHeaders.mid(headerLinePos + matchLength, endOfLine - (headerLinePos + matchLength)).stripWhiteSpace());
00433 }
00434
00435 Value XMLHttpRequest::getStatus()
const
00436
{
00437
if (responseHeaders.isEmpty()) {
00438
return Undefined();
00439 }
00440
00441
int endOfLine = responseHeaders.find(
"\n");
00442
QString firstLine =
endOfLine == -1 ? responseHeaders : responseHeaders.left(endOfLine);
00443
int codeStart = firstLine.find(
" ");
00444
int codeEnd = firstLine.find(
" ", codeStart + 1);
00445
00446
if (codeStart == -1 || codeEnd == -1) {
00447
return Undefined();
00448 }
00449
00450
QString number = firstLine.mid(codeStart + 1, codeEnd - (codeStart + 1));
00451
00452
bool ok =
false;
00453
int code =
number.toInt(&ok);
00454
if (!ok) {
00455
return Undefined();
00456 }
00457
00458
return Number(code);
00459 }
00460
00461 Value XMLHttpRequest::getStatusText()
const
00462
{
00463
if (responseHeaders.isEmpty()) {
00464
return Undefined();
00465 }
00466
00467
int endOfLine = responseHeaders.find(
"\n");
00468
QString firstLine =
endOfLine == -1 ? responseHeaders : responseHeaders.left(endOfLine);
00469
int codeStart = firstLine.find(
" ");
00470
int codeEnd = firstLine.find(
" ", codeStart + 1);
00471
00472
if (codeStart == -1 || codeEnd == -1) {
00473
return Undefined();
00474 }
00475
00476
QString statusText = firstLine.mid(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace();
00477
00478
return String(statusText);
00479 }
00480
00481
void XMLHttpRequest::processSyncLoadResults(
const QByteArray &data,
const KURL &finalURL,
const QString &headers)
00482 {
00483
if (!urlMatchesDocumentDomain(finalURL)) {
00484 abort();
00485
return;
00486 }
00487
00488 responseHeaders = headers;
00489 changeState(Loaded);
00490
if (aborted) {
00491
return;
00492 }
00493
00494
#ifdef APPLE_CHANGES
00495
const char *bytes = (
const char *)data.data();
00496
int len = (
int)data.size();
00497
00498 slotData(0, bytes, len);
00499
#else
00500
slotData(0, data);
00501
#endif
00502
00503
if (aborted) {
00504
return;
00505 }
00506
00507 slotFinished(0);
00508 }
00509
00510
void XMLHttpRequest::slotFinished(
KIO::Job *)
00511 {
00512
if (decoder) {
00513 response += decoder->flush();
00514 }
00515
00516
00517
00518 job = 0;
00519 changeState(Completed);
00520
00521
delete decoder;
00522 decoder = 0;
00523 }
00524
00525
void XMLHttpRequest::slotRedirection(
KIO::Job*,
const KURL& url)
00526 {
00527
if (!urlMatchesDocumentDomain(url)) {
00528 abort();
00529 }
00530 }
00531
00532
#ifdef APPLE_CHANGES
00533
void XMLHttpRequest::slotData(
KIO::Job*,
const char *data,
int len )
00534 #
else
00535
void XMLHttpRequest::slotData(
KIO::Job*,
const QByteArray &_data)
00536 #endif
00537 {
00538
if (state < Loaded ) {
00539 responseHeaders = job->queryMetaData(
"HTTP-Headers");
00540 changeState(Loaded);
00541 }
00542
00543
#ifndef APPLE_CHANGES
00544
const char *data = (
const char *)_data.data();
00545
int len = (
int)_data.size();
00546
#endif
00547
00548
if ( decoder == NULL ) {
00549 decoder =
new Decoder;
00550
if (!encoding.isNull())
00551 decoder->setEncoding(encoding.latin1(), Decoder::EncodingFromHTTPHeader);
00552
else {
00553
00554 }
00555 }
00556
if (len == 0)
00557
return;
00558
00559
if (len == -1)
00560 len = strlen(data);
00561
00562
QString decoded = decoder->decode(data, len);
00563
00564 response += decoded;
00565
00566
if (!aborted) {
00567 changeState(Interactive);
00568 }
00569 }
00570
00571 Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj,
const List &args)
00572 {
00573
if (!thisObj.inherits(&XMLHttpRequest::info)) {
00574 Object err = Error::create(exec,TypeError);
00575 exec->setException(err);
00576
return err;
00577 }
00578
00579 XMLHttpRequest *request = static_cast<XMLHttpRequest *>(thisObj.imp());
00580
switch (
id) {
00581
case XMLHttpRequest::Abort:
00582 request->abort();
00583
return Undefined();
00584
case XMLHttpRequest::GetAllResponseHeaders:
00585
if (args.size() != 0) {
00586
return Undefined();
00587 }
00588
00589
return request->getAllResponseHeaders();
00590
case XMLHttpRequest::GetResponseHeader:
00591
if (args.size() != 1) {
00592
return Undefined();
00593 }
00594
00595
return request->getResponseHeader(args[0].toString(exec).qstring());
00596
case XMLHttpRequest::Open:
00597 {
00598
if (args.size() < 2 || args.size() > 5) {
00599
return Undefined();
00600 }
00601
00602
QString method = args[0].toString(exec).qstring();
00603
KHTMLPart *part = ::qt_cast<KHTMLPart *>(Window::retrieveActive(exec)->part());
00604
if (!part)
00605
return Undefined();
00606
KURL url =
KURL(part->
document().
completeURL(args[1].toString(exec).qstring()).
string());
00607
00608
bool async =
true;
00609
if (args.size() >= 3) {
00610 async = args[2].toBoolean(exec);
00611 }
00612
00613
if (args.size() >= 4) {
00614 url.
setUser(args[3].toString(exec).qstring());
00615 }
00616
00617
if (args.size() >= 5) {
00618 url.
setPass(args[4].toString(exec).qstring());
00619 }
00620
00621 request->open(method, url, async);
00622
00623
return Undefined();
00624 }
00625
case XMLHttpRequest::Send:
00626 {
00627
if (args.size() > 1) {
00628
return Undefined();
00629 }
00630
00631
if (request->state != Loading) {
00632
return Undefined();
00633 }
00634
00635
QString body;
00636
00637
if (args.size() >= 1) {
00638 Object obj = Object::dynamicCast(args[0]);
00639
if (!obj.isNull() && obj.inherits(&DOMDocument::info)) {
00640
DOM::Node docNode = static_cast<KJS::DOMDocument *>(obj.imp())->toNode();
00641 DOM::DocumentImpl *doc = static_cast<DOM::DocumentImpl *>(docNode.
handle());
00642
00643
try {
00644 body = doc->toString().string();
00645
00646
00647 }
catch(
DOM::DOMException& e) {
00648 Object err = Error::create(exec, GeneralError,
"Exception serializing document");
00649 exec->setException(err);
00650 }
00651 }
else {
00652 body = args[0].toString(exec).qstring();
00653 }
00654 }
00655
00656 request->send(body);
00657
00658
return Undefined();
00659 }
00660
case XMLHttpRequest::SetRequestHeader:
00661
if (args.size() != 2) {
00662
return Undefined();
00663 }
00664
00665 request->setRequestHeader(args[0].toString(exec).qstring(), args[1].toString(exec).qstring());
00666
00667
return Undefined();
00668 }
00669
00670
return Undefined();
00671 }
00672
00673 }
00674
00675
#include "xmlhttprequest.moc"