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