xmlhttprequest.cpp

00001 // -*- c-basic-offset: 2 -*-
00002 /*
00003  *  This file is part of the KDE libraries
00004  *  Copyright (C) 2003 Apple Computer, Inc.
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Lesser General Public
00008  *  License as published by the Free Software Foundation; either
00009  *  version 2 of the License, or (at your option) any later version.
00010  *
00011  *  This library is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014  *  Lesser General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU Lesser General Public
00017  *  License along with this library; if not, write to the Free Software
00018  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
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 #define BANNED_HTTP_HEADERS "authorization,proxy-authorization,"\
00049                             "content-length,host,connect,copy,move,"\
00050                             "delete,head,trace,put,propfind,proppatch,"\
00051                             "mkcol,lock,unlock,options,via"
00052 
00053 using namespace KJS;
00054 using khtml::Decoder;
00055 
00057 
00058 /* Source for XMLHttpRequestProtoTable.
00059 @begin XMLHttpRequestProtoTable 7
00060   abort         XMLHttpRequest::Abort           DontDelete|Function 0
00061   getAllResponseHeaders XMLHttpRequest::GetAllResponseHeaders   DontDelete|Function 0
00062   getResponseHeader XMLHttpRequest::GetResponseHeader   DontDelete|Function 1
00063   open          XMLHttpRequest::Open            DontDelete|Function 5
00064   send          XMLHttpRequest::Send            DontDelete|Function 1
00065   setRequestHeader  XMLHttpRequest::SetRequestHeader    DontDelete|Function 2
00066 @end
00067 */
00068 DEFINE_PROTOTYPE("XMLHttpRequest",XMLHttpRequestProto)
00069 IMPLEMENT_PROTOFUNC_DOM(XMLHttpRequestProtoFunc)
00070 IMPLEMENT_PROTOTYPE(XMLHttpRequestProto,XMLHttpRequestProtoFunc)
00071 
00072 namespace KJS {
00073 
00074 XMLHttpRequestQObject::XMLHttpRequestQObject(XMLHttpRequest *_jsObject)
00075 {
00076   jsObject = _jsObject;
00077 }
00078 
00079 #ifdef APPLE_CHANGES
00080 void XMLHttpRequestQObject::slotData( KIO::Job* job, const char *data, int size )
00081 {
00082   jsObject->slotData(job, data, size);
00083 }
00084 #else
00085 void XMLHttpRequestQObject::slotData( KIO::Job* job, const QByteArray &data )
00086 {
00087   jsObject->slotData(job, data);
00088 }
00089 #endif
00090 
00091 void XMLHttpRequestQObject::slotFinished( KIO::Job* job )
00092 {
00093   jsObject->slotFinished(job);
00094 }
00095 
00096 void XMLHttpRequestQObject::slotRedirection( KIO::Job* job, const KURL& url)
00097 {
00098   jsObject->slotRedirection( job, url );
00099 }
00100 
00101 XMLHttpRequestConstructorImp::XMLHttpRequestConstructorImp(ExecState *, const DOM::Document &d)
00102     : ObjectImp(), doc(d)
00103 {
00104 }
00105 
00106 bool XMLHttpRequestConstructorImp::implementsConstruct() const
00107 {
00108   return true;
00109 }
00110 
00111 Object XMLHttpRequestConstructorImp::construct(ExecState *exec, const List &)
00112 {
00113   return Object(new XMLHttpRequest(exec, doc));
00114 }
00115 
00116 const ClassInfo XMLHttpRequest::info = { "XMLHttpRequest", 0, &XMLHttpRequestTable, 0 };
00117 
00118 
00119 /* Source for XMLHttpRequestTable.
00120 @begin XMLHttpRequestTable 7
00121   readyState        XMLHttpRequest::ReadyState      DontDelete|ReadOnly
00122   responseText      XMLHttpRequest::ResponseText        DontDelete|ReadOnly
00123   responseXML       XMLHttpRequest::ResponseXML     DontDelete|ReadOnly
00124   status        XMLHttpRequest::Status          DontDelete|ReadOnly
00125   statusText        XMLHttpRequest::StatusText      DontDelete|ReadOnly
00126   onreadystatechange    XMLHttpRequest::Onreadystatechange  DontDelete
00127   onload        XMLHttpRequest::Onload          DontDelete
00128 @end
00129 */
00130 
00131 Value XMLHttpRequest::tryGet(ExecState *exec, const Identifier &propertyName) const
00132 {
00133   return DOMObjectLookupGetValue<XMLHttpRequest,DOMObject>(exec, propertyName, &XMLHttpRequestTable, this);
00134 }
00135 
00136 Value XMLHttpRequest::getValueProperty(ExecState *exec, int token) const
00137 {
00138   switch (token) {
00139   case ReadyState:
00140     return Number(state);
00141   case ResponseText:
00142     return getString(DOM::DOMString(response));
00143   case ResponseXML:
00144     if (state != Completed) {
00145       return Undefined();
00146     }
00147     if (!createdDocument) {
00148       QString mimeType = "text/xml";
00149 
00150       Value header = getResponseHeader("Content-Type");
00151       if (header.type() != UndefinedType) {
00152     mimeType = QStringList::split(";", header.toString(exec).qstring())[0].stripWhiteSpace();
00153       }
00154 
00155       if (mimeType == "text/xml" || mimeType == "application/xml" || mimeType == "application/xhtml+xml") {
00156     responseXML = DOM::Document(doc->implementation()->createDocument());
00157 
00158     DOM::DocumentImpl *docImpl = static_cast<DOM::DocumentImpl *>(responseXML.handle());
00159 
00160     docImpl->open();
00161     docImpl->write(response);
00162     docImpl->finishParsing();
00163     docImpl->close();
00164 
00165     typeIsXML = true;
00166       } else {
00167     typeIsXML = false;
00168       }
00169       createdDocument = true;
00170     }
00171 
00172     if (!typeIsXML) {
00173       return Undefined();
00174     }
00175 
00176     return getDOMNode(exec,responseXML);
00177   case Status:
00178     return getStatus();
00179   case StatusText:
00180     return getStatusText();
00181   case Onreadystatechange:
00182    if (onReadyStateChangeListener && onReadyStateChangeListener->listenerObjImp()) {
00183      return onReadyStateChangeListener->listenerObj();
00184    } else {
00185      return Null();
00186    }
00187   case Onload:
00188    if (onLoadListener && onLoadListener->listenerObjImp()) {
00189      return onLoadListener->listenerObj();
00190    } else {
00191     return Null();
00192    }
00193   default:
00194     kdWarning() << "XMLHttpRequest::getValueProperty unhandled token " << token << endl;
00195     return Value();
00196   }
00197 }
00198 
00199 void XMLHttpRequest::tryPut(ExecState *exec, const Identifier &propertyName, const Value& value, int attr)
00200 {
00201   DOMObjectLookupPut<XMLHttpRequest,DOMObject>(exec, propertyName, value, attr, &XMLHttpRequestTable, this );
00202 }
00203 
00204 void XMLHttpRequest::putValueProperty(ExecState *exec, int token, const Value& value, int /*attr*/)
00205 {
00206   switch(token) {
00207   case Onreadystatechange:
00208     onReadyStateChangeListener = Window::retrieveActive(exec)->getJSEventListener(value, true);
00209     if (onReadyStateChangeListener) onReadyStateChangeListener->ref();
00210     break;
00211   case Onload:
00212     onLoadListener = Window::retrieveActive(exec)->getJSEventListener(value, true);
00213     if (onLoadListener) onLoadListener->ref();
00214     break;
00215   default:
00216     kdWarning() << "XMLHttpRequest::putValue unhandled token " << token << endl;
00217   }
00218 }
00219 
00220 XMLHttpRequest::XMLHttpRequest(ExecState *exec, const DOM::Document &d)
00221   : DOMObject(XMLHttpRequestProto::self(exec)),
00222     qObject(new XMLHttpRequestQObject(this)),
00223     doc(static_cast<DOM::DocumentImpl*>(d.handle())),
00224     async(true),
00225     contentType(QString::null),
00226     job(0),
00227     state(Uninitialized),
00228     onReadyStateChangeListener(0),
00229     onLoadListener(0),
00230     decoder(0),
00231     createdDocument(false),
00232     aborted(false)
00233 {
00234 }
00235 
00236 XMLHttpRequest::~XMLHttpRequest()
00237 {
00238   delete qObject;
00239   qObject = 0;
00240   delete decoder;
00241   decoder = 0;
00242 }
00243 
00244 void XMLHttpRequest::changeState(XMLHttpRequestState newState)
00245 {
00246   if (state != newState) {
00247     state = newState;
00248 
00249     ref();
00250 
00251     if (onReadyStateChangeListener != 0 && doc->view() && doc->view()->part()) {
00252       DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents");
00253       ev.initEvent("readystatechange", true, true);
00254       onReadyStateChangeListener->handleEvent(ev);
00255     }
00256 
00257     if (state == Completed && onLoadListener != 0 && doc->view() && doc->view()->part()) {
00258       DOM::Event ev = doc->view()->part()->document().createEvent("HTMLEvents");
00259       ev.initEvent("load", true, true);
00260       onLoadListener->handleEvent(ev);
00261     }
00262 
00263     deref();
00264   }
00265 }
00266 
00267 bool XMLHttpRequest::urlMatchesDocumentDomain(const KURL& _url) const
00268 {
00269   // No need to do work if _url is not valid...
00270   if (!_url.isValid())
00271     return false;
00272 
00273   KURL documentURL(doc->URL());
00274 
00275   // a local file can load anything
00276   if (documentURL.protocol().lower() == "file") {
00277     return true;
00278   }
00279 
00280   // but a remote document can only load from the same port on the server
00281   if (documentURL.protocol().lower() == _url.protocol().lower() &&
00282       documentURL.host().lower() == _url.host().lower() &&
00283       documentURL.port() == _url.port()) {
00284     return true;
00285   }
00286 
00287   return false;
00288 }
00289 
00290 void XMLHttpRequest::open(const QString& _method, const KURL& _url, bool _async)
00291 {
00292   abort();
00293   aborted = false;
00294 
00295   // clear stuff from possible previous load
00296   requestHeaders.clear();
00297   responseHeaders = QString();
00298   response = QString();
00299   createdDocument = false;
00300   responseXML = DOM::Document();
00301 
00302   changeState(Uninitialized);
00303 
00304   if (aborted) {
00305     return;
00306   }
00307 
00308   if (!urlMatchesDocumentDomain(_url)) {
00309     return;
00310   }
00311 
00312 
00313   method = _method.lower();
00314   url = _url;
00315   async = _async;
00316 
00317   changeState(Loading);
00318 }
00319 
00320 void XMLHttpRequest::send(const QString& _body)
00321 {
00322   aborted = false;
00323 
00324   if (method == "post") {
00325     QString protocol = url.protocol().lower();
00326 
00327     // Abondon the request when the protocol is other than "http",
00328     // instead of blindly changing it to a "get" request.
00329     if (!protocol.startsWith("http") && !protocol.startsWith("webdav"))
00330     {
00331       abort();
00332       return;
00333     }
00334 
00335     // FIXME: determine post encoding correctly by looking in headers
00336     // for charset.
00337     QByteArray buf;
00338     buf.duplicate(_body.utf8().data(), _body.length());
00339 
00340     job = KIO::http_post( url, buf, false );
00341     if(contentType.isNull())
00342       job->addMetaData( "content-type", "Content-type: text/plain" );
00343     else
00344       job->addMetaData( "content-type", contentType );
00345   }
00346   else {
00347     job = KIO::get( url, false, false );
00348   }
00349 
00350   if (!requestHeaders.isEmpty()) {
00351     QString rh;
00352     QMap<QString, QString>::ConstIterator begin = requestHeaders.begin();
00353     QMap<QString, QString>::ConstIterator end = requestHeaders.end();
00354     for (QMap<QString, QString>::ConstIterator i = begin; i != end; ++i) {
00355       if (i != begin)
00356         rh += "\r\n";
00357       rh += i.key() + ": " + i.data();
00358     }
00359 
00360     job->addMetaData("customHTTPHeader", rh);
00361   }
00362 
00363   job->addMetaData("PropagateHttpHeader", "true");
00364 
00365   // Set the default referrer if one is not already supplied
00366   // through setRequestHeader. NOTE: the user can still disable
00367   // this feature at the protocol level (kio_http).
00368   if (requestHeaders.find("Referer") == requestHeaders.end()) {
00369     KURL documentURL(doc->URL());
00370     documentURL.setPass(QString::null);
00371     documentURL.setUser(QString::null);
00372     job->addMetaData("referrer", documentURL.url());
00373     // kdDebug() << "Adding referrer: " << documentURL << endl;
00374   }
00375 
00376   if (!async) {
00377     QByteArray data;
00378     KURL finalURL;
00379     QString headers;
00380 
00381 #ifdef APPLE_CHANGES
00382     data = KWQServeSynchronousRequest(khtml::Cache::loader(), doc->docLoader(), job, finalURL, headers);
00383 #else
00384     QMap<QString, QString> metaData;
00385     if ( NetAccess::synchronousRun( job, 0, &data, &finalURL, &metaData ) ) {
00386       headers = metaData[ "HTTP-Headers" ];
00387     }
00388 #endif
00389     job = 0;
00390     processSyncLoadResults(data, finalURL, headers);
00391     return;
00392   }
00393 
00394   qObject->connect( job, SIGNAL( result( KIO::Job* ) ),
00395             SLOT( slotFinished( KIO::Job* ) ) );
00396 #ifdef APPLE_CHANGES
00397   qObject->connect( job, SIGNAL( data( KIO::Job*, const char*, int ) ),
00398             SLOT( slotData( KIO::Job*, const char*, int ) ) );
00399 #else
00400   qObject->connect( job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00401             SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
00402 #endif
00403   qObject->connect( job, SIGNAL(redirection(KIO::Job*, const KURL& ) ),
00404             SLOT( slotRedirection(KIO::Job*, const KURL&) ) );
00405 
00406 #ifdef APPLE_CHANGES
00407   KWQServeRequest(khtml::Cache::loader(), doc->docLoader(), job);
00408 #else
00409   KIO::Scheduler::scheduleJob( job );
00410 #endif
00411 }
00412 
00413 void XMLHttpRequest::abort()
00414 {
00415   if (job) {
00416     job->kill();
00417     job = 0;
00418   }
00419   delete decoder;
00420   decoder = 0;
00421   aborted = true;
00422 }
00423 
00424 void XMLHttpRequest::setRequestHeader(const QString& _name, const QString &value)
00425 {
00426   QString name = _name.lower().stripWhiteSpace();
00427 
00428   // Content-type needs to be set seperately from the other headers
00429   if(name == "content-type") {
00430     contentType = "Content-type: " + value;
00431     return;
00432   }
00433 
00434   // Sanitize the referrer header to protect against spoofing...
00435   if(name == "referer") {
00436     KURL referrerURL(value);
00437     if (urlMatchesDocumentDomain(referrerURL))
00438       requestHeaders[name] = referrerURL.url();
00439     return;
00440   }
00441 
00442   // Sanitize the request headers below and handle them as if they are
00443   // calls to open. Otherwise, we will end up ignoring them all together!
00444   // TODO: Do something about "put" which kio_http sort of supports and
00445   // the webDAV headers such as PROPFIND etc...
00446   if (name == "get"  || name == "post") {
00447     KURL reqURL (doc->URL(), value.stripWhiteSpace());
00448     open(name, reqURL, async);
00449     return;
00450   }
00451 
00452   // Reject all banned headers. See BANNED_HTTP_HEADERS above.
00453   // kdDebug() << "Banned HTTP Headers: " << BANNED_HTTP_HEADERS << endl;
00454   QStringList bannedHeaders = QStringList::split(',',
00455                                   QString::fromLatin1(BANNED_HTTP_HEADERS));
00456 
00457   if (bannedHeaders.contains(name))
00458     return;   // Denied
00459 
00460   requestHeaders[name] = value.stripWhiteSpace();
00461 }
00462 
00463 Value XMLHttpRequest::getAllResponseHeaders() const
00464 {
00465   if (responseHeaders.isEmpty()) {
00466     return Undefined();
00467   }
00468 
00469   int endOfLine = responseHeaders.find("\n");
00470 
00471   if (endOfLine == -1) {
00472     return Undefined();
00473   }
00474 
00475   return String(responseHeaders.mid(endOfLine + 1) + "\n");
00476 }
00477 
00478 Value XMLHttpRequest::getResponseHeader(const QString& name) const
00479 {
00480   if (responseHeaders.isEmpty()) {
00481     return Undefined();
00482   }
00483 
00484   QRegExp headerLinePattern(name + ":", false);
00485 
00486   int matchLength;
00487   int headerLinePos = headerLinePattern.search(responseHeaders, 0);
00488   matchLength = headerLinePattern.matchedLength();
00489   while (headerLinePos != -1) {
00490     if (headerLinePos == 0 || responseHeaders[headerLinePos-1] == '\n') {
00491       break;
00492     }
00493 
00494     headerLinePos = headerLinePattern.search(responseHeaders, headerLinePos + 1);
00495     matchLength = headerLinePattern.matchedLength();
00496   }
00497 
00498 
00499   if (headerLinePos == -1) {
00500     return Undefined();
00501   }
00502 
00503   int endOfLine = responseHeaders.find("\n", headerLinePos + matchLength);
00504 
00505   return String(responseHeaders.mid(headerLinePos + matchLength, endOfLine - (headerLinePos + matchLength)).stripWhiteSpace());
00506 }
00507 
00508 static Value httpStatus(const QString& response, bool textStatus = false)
00509 {
00510   if (response.isEmpty()) {
00511     return Undefined();
00512   }
00513 
00514   int endOfLine = response.find("\n");
00515   QString firstLine = (endOfLine == -1) ? response : response.left(endOfLine);
00516   int codeStart = firstLine.find(" ");
00517   int codeEnd = firstLine.find(" ", codeStart + 1);
00518 
00519   if (codeStart == -1 || codeEnd == -1) {
00520     return Undefined();
00521   }
00522 
00523   if (textStatus) {
00524     QString statusText = firstLine.mid(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace();
00525     return String(statusText);
00526   }
00527 
00528   QString number = firstLine.mid(codeStart + 1, codeEnd - (codeStart + 1));
00529 
00530   bool ok = false;
00531   int code = number.toInt(&ok);
00532   if (!ok) {
00533     return Undefined();
00534   }
00535 
00536   return Number(code);
00537 }
00538 
00539 Value XMLHttpRequest::getStatus() const
00540 {
00541   return httpStatus(responseHeaders);
00542 }
00543 
00544 Value XMLHttpRequest::getStatusText() const
00545 {
00546   return httpStatus(responseHeaders, true);
00547 }
00548 
00549 void XMLHttpRequest::processSyncLoadResults(const QByteArray &data, const KURL &finalURL, const QString &headers)
00550 {
00551   if (!urlMatchesDocumentDomain(finalURL)) {
00552     abort();
00553     return;
00554   }
00555 
00556   responseHeaders = headers;
00557   changeState(Loaded);
00558   if (aborted) {
00559     return;
00560   }
00561 
00562 #ifdef APPLE_CHANGES
00563   const char *bytes = (const char *)data.data();
00564   int len = (int)data.size();
00565 
00566   slotData(0, bytes, len);
00567 #else
00568   slotData(0, data);
00569 #endif
00570 
00571   if (aborted) {
00572     return;
00573   }
00574 
00575   slotFinished(0);
00576 }
00577 
00578 void XMLHttpRequest::slotFinished(KIO::Job *)
00579 {
00580   if (decoder) {
00581     response += decoder->flush();
00582   }
00583 
00584   // make sure to forget about the job before emitting completed,
00585   // since changeState triggers JS code, which might e.g. call abort.
00586   job = 0;
00587   changeState(Completed);
00588 
00589   delete decoder;
00590   decoder = 0;
00591 }
00592 
00593 void XMLHttpRequest::slotRedirection(KIO::Job*, const KURL& url)
00594 {
00595   if (!urlMatchesDocumentDomain(url)) {
00596     abort();
00597   }
00598 }
00599 
00600 #ifdef APPLE_CHANGES
00601 void XMLHttpRequest::slotData( KIO::Job*, const char *data, int len )
00602 #else
00603 void XMLHttpRequest::slotData(KIO::Job*, const QByteArray &_data)
00604 #endif
00605 {
00606   if (state < Loaded ) {
00607     responseHeaders = job->queryMetaData("HTTP-Headers");
00608 
00609     // NOTE: Replace a 304 response with a 200! Both IE and Mozilla do this.
00610     // Problem first reported through bug# 110272.
00611     int codeStart = responseHeaders.find("304");
00612     if ( codeStart != -1) {
00613       int codeEnd = responseHeaders.find("\n", codeStart+3);
00614       if (codeEnd != -1)
00615         responseHeaders.replace(codeStart, (codeEnd-codeStart), "200 OK");
00616     }
00617 
00618     changeState(Loaded);
00619   }
00620 
00621 #ifndef APPLE_CHANGES
00622   const char *data = (const char *)_data.data();
00623   int len = (int)_data.size();
00624 #endif
00625 
00626   if ( decoder == NULL ) {
00627     int pos = responseHeaders.find("content-type:", 0, false);
00628 
00629     if ( pos > -1 ) {
00630       pos += 13;
00631       int index = responseHeaders.find('\n', pos);
00632       QString type = responseHeaders.mid(pos, (index-pos));
00633       index = type.find (';');
00634       if (index > -1)
00635         encoding = type.mid( index+1 ).remove(QRegExp("charset[ ]*=[ ]*", false)).stripWhiteSpace();
00636     }
00637 
00638     decoder = new Decoder;
00639     if (!encoding.isNull())
00640       decoder->setEncoding(encoding.latin1(), Decoder::EncodingFromHTTPHeader);
00641     else {
00642       // FIXME: Inherit the default encoding from the parent document?
00643     }
00644   }
00645   if (len == 0)
00646     return;
00647 
00648   if (len == -1)
00649     len = strlen(data);
00650 
00651   QString decoded = decoder->decode(data, len);
00652 
00653   response += decoded;
00654 
00655   if (!aborted) {
00656     changeState(Interactive);
00657   }
00658 }
00659 
00660 Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
00661 {
00662   if (!thisObj.inherits(&XMLHttpRequest::info)) {
00663     Object err = Error::create(exec,TypeError);
00664     exec->setException(err);
00665     return err;
00666   }
00667 
00668   XMLHttpRequest *request = static_cast<XMLHttpRequest *>(thisObj.imp());
00669   switch (id) {
00670   case XMLHttpRequest::Abort:
00671     request->abort();
00672     return Undefined();
00673   case XMLHttpRequest::GetAllResponseHeaders:
00674     if (args.size() != 0) {
00675     return Undefined();
00676     }
00677 
00678     return request->getAllResponseHeaders();
00679   case XMLHttpRequest::GetResponseHeader:
00680     if (args.size() != 1) {
00681     return Undefined();
00682     }
00683 
00684     return request->getResponseHeader(args[0].toString(exec).qstring());
00685   case XMLHttpRequest::Open:
00686     {
00687       if (args.size() < 2 || args.size() > 5) {
00688         return Undefined();
00689       }
00690 
00691       QString method = args[0].toString(exec).qstring();
00692       KHTMLPart *part = ::qt_cast<KHTMLPart *>(Window::retrieveActive(exec)->part());
00693       if (!part)
00694         return Undefined();
00695       KURL url = KURL(part->document().completeURL(args[1].toString(exec).qstring()).string());
00696 
00697       bool async = true;
00698       if (args.size() >= 3) {
00699     async = args[2].toBoolean(exec);
00700       }
00701 
00702       if (args.size() >= 4) {
00703     url.setUser(args[3].toString(exec).qstring());
00704       }
00705 
00706       if (args.size() >= 5) {
00707     url.setPass(args[4].toString(exec).qstring());
00708       }
00709 
00710       request->open(method, url, async);
00711 
00712       return Undefined();
00713     }
00714   case XMLHttpRequest::Send:
00715     {
00716       if (args.size() > 1) {
00717         return Undefined();
00718       }
00719 
00720       if (request->state != Loading) {
00721     return Undefined();
00722       }
00723 
00724       QString body;
00725       if (args.size() >= 1) {
00726         Object obj = Object::dynamicCast(args[0]);
00727         if (obj.isValid() && obj.inherits(&DOMDocument::info)) {
00728           DOM::Node docNode = static_cast<KJS::DOMDocument *>(obj.imp())->toNode();
00729           DOM::DocumentImpl *doc = static_cast<DOM::DocumentImpl *>(docNode.handle());
00730           
00731           try {
00732             body = doc->toString().string();
00733             // FIXME: also need to set content type, including encoding!
00734   
00735           } catch(DOM::DOMException& e) {
00736             Object err = Error::create(exec, GeneralError, "Exception serializing document");
00737             exec->setException(err);
00738           }
00739         } else {
00740           body = args[0].toString(exec).qstring();
00741         }
00742       }
00743 
00744       request->send(body);
00745 
00746       return Undefined();
00747     }
00748   case XMLHttpRequest::SetRequestHeader:
00749     if (args.size() != 2) {
00750       return Undefined();
00751     }
00752 
00753     request->setRequestHeader(args[0].toString(exec).qstring(), args[1].toString(exec).qstring());
00754 
00755     return Undefined();
00756   }
00757 
00758   return Undefined();
00759 }
00760 
00761 } // end namespace
00762 
00763 #include "xmlhttprequest.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys