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"