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());
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"