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