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     QCString str = _body.utf8();
00339     buf.duplicate(str.data(), str.size() - 1);
00340 
00341     job = KIO::http_post( url, buf, false );
00342     if(contentType.isNull())
00343       job->addMetaData( "content-type", "Content-type: text/plain" );
00344     else
00345       job->addMetaData( "content-type", contentType );
00346   }
00347   else {
00348     job = KIO::get( url, false, false );
00349   }
00350 
00351   if (!requestHeaders.isEmpty()) {
00352     QString rh;
00353     QMap<QString, QString>::ConstIterator begin = requestHeaders.begin();
00354     QMap<QString, QString>::ConstIterator end = requestHeaders.end();
00355     for (QMap<QString, QString>::ConstIterator i = begin; i != end; ++i) {
00356       if (i != begin)
00357         rh += "\r\n";
00358       rh += i.key() + ": " + i.data();
00359     }
00360 
00361     job->addMetaData("customHTTPHeader", rh);
00362   }
00363 
00364   job->addMetaData("PropagateHttpHeader", "true");
00365 
00366   // Set the default referrer if one is not already supplied
00367   // through setRequestHeader. NOTE: the user can still disable
00368   // this feature at the protocol level (kio_http).
00369   if (requestHeaders.find("Referer") == requestHeaders.end()) {
00370     KURL documentURL(doc->URL());
00371     documentURL.setPass(QString::null);
00372     documentURL.setUser(QString::null);
00373     job->addMetaData("referrer", documentURL.url());
00374     // kdDebug() << "Adding referrer: " << documentURL << endl;
00375   }
00376 
00377   if (!async) {
00378     QByteArray data;
00379     KURL finalURL;
00380     QString headers;
00381 
00382 #ifdef APPLE_CHANGES
00383     data = KWQServeSynchronousRequest(khtml::Cache::loader(), doc->docLoader(), job, finalURL, headers);
00384 #else
00385     QMap<QString, QString> metaData;
00386     if ( NetAccess::synchronousRun( job, 0, &data, &finalURL, &metaData ) ) {
00387       headers = metaData[ "HTTP-Headers" ];
00388     }
00389 #endif
00390     job = 0;
00391     processSyncLoadResults(data, finalURL, headers);
00392     return;
00393   }
00394 
00395   qObject->connect( job, SIGNAL( result( KIO::Job* ) ),
00396             SLOT( slotFinished( KIO::Job* ) ) );
00397 #ifdef APPLE_CHANGES
00398   qObject->connect( job, SIGNAL( data( KIO::Job*, const char*, int ) ),
00399             SLOT( slotData( KIO::Job*, const char*, int ) ) );
00400 #else
00401   qObject->connect( job, SIGNAL( data( KIO::Job*, const QByteArray& ) ),
00402             SLOT( slotData( KIO::Job*, const QByteArray& ) ) );
00403 #endif
00404   qObject->connect( job, SIGNAL(redirection(KIO::Job*, const KURL& ) ),
00405             SLOT( slotRedirection(KIO::Job*, const KURL&) ) );
00406 
00407 #ifdef APPLE_CHANGES
00408   KWQServeRequest(khtml::Cache::loader(), doc->docLoader(), job);
00409 #else
00410   KIO::Scheduler::scheduleJob( job );
00411 #endif
00412 }
00413 
00414 void XMLHttpRequest::abort()
00415 {
00416   if (job) {
00417     job->kill();
00418     job = 0;
00419   }
00420   delete decoder;
00421   decoder = 0;
00422   aborted = true;
00423 }
00424 
00425 void XMLHttpRequest::setRequestHeader(const QString& _name, const QString &value)
00426 {
00427   QString name = _name.lower().stripWhiteSpace();
00428 
00429   // Content-type needs to be set seperately from the other headers
00430   if(name == "content-type") {
00431     contentType = "Content-type: " + value;
00432     return;
00433   }
00434 
00435   // Sanitize the referrer header to protect against spoofing...
00436   if(name == "referer") {
00437     KURL referrerURL(value);
00438     if (urlMatchesDocumentDomain(referrerURL))
00439       requestHeaders[name] = referrerURL.url();
00440     return;
00441   }
00442 
00443   // Sanitize the request headers below and handle them as if they are
00444   // calls to open. Otherwise, we will end up ignoring them all together!
00445   // TODO: Do something about "put" which kio_http sort of supports and
00446   // the webDAV headers such as PROPFIND etc...
00447   if (name == "get"  || name == "post") {
00448     KURL reqURL (doc->URL(), value.stripWhiteSpace());
00449     open(name, reqURL, async);
00450     return;
00451   }
00452 
00453   // Reject all banned headers. See BANNED_HTTP_HEADERS above.
00454   // kdDebug() << "Banned HTTP Headers: " << BANNED_HTTP_HEADERS << endl;
00455   QStringList bannedHeaders = QStringList::split(',',
00456                                   QString::fromLatin1(BANNED_HTTP_HEADERS));
00457 
00458   if (bannedHeaders.contains(name))
00459     return;   // Denied
00460 
00461   requestHeaders[name] = value.stripWhiteSpace();
00462 }
00463 
00464 Value XMLHttpRequest::getAllResponseHeaders() const
00465 {
00466   if (responseHeaders.isEmpty()) {
00467     return Undefined();
00468   }
00469 
00470   int endOfLine = responseHeaders.find("\n");
00471 
00472   if (endOfLine == -1) {
00473     return Undefined();
00474   }
00475 
00476   return String(responseHeaders.mid(endOfLine + 1) + "\n");
00477 }
00478 
00479 Value XMLHttpRequest::getResponseHeader(const QString& name) const
00480 {
00481   if (responseHeaders.isEmpty()) {
00482     return Undefined();
00483   }
00484 
00485   QRegExp headerLinePattern(name + ":", false);
00486 
00487   int matchLength;
00488   int headerLinePos = headerLinePattern.search(responseHeaders, 0);
00489   matchLength = headerLinePattern.matchedLength();
00490   while (headerLinePos != -1) {
00491     if (headerLinePos == 0 || responseHeaders[headerLinePos-1] == '\n') {
00492       break;
00493     }
00494 
00495     headerLinePos = headerLinePattern.search(responseHeaders, headerLinePos + 1);
00496     matchLength = headerLinePattern.matchedLength();
00497   }
00498 
00499 
00500   if (headerLinePos == -1) {
00501     return Undefined();
00502   }
00503 
00504   int endOfLine = responseHeaders.find("\n", headerLinePos + matchLength);
00505 
00506   return String(responseHeaders.mid(headerLinePos + matchLength, endOfLine - (headerLinePos + matchLength)).stripWhiteSpace());
00507 }
00508 
00509 static Value httpStatus(const QString& response, bool textStatus = false)
00510 {
00511   if (response.isEmpty()) {
00512     return Undefined();
00513   }
00514 
00515   int endOfLine = response.find("\n");
00516   QString firstLine = (endOfLine == -1) ? response : response.left(endOfLine);
00517   int codeStart = firstLine.find(" ");
00518   int codeEnd = firstLine.find(" ", codeStart + 1);
00519 
00520   if (codeStart == -1 || codeEnd == -1) {
00521     return Undefined();
00522   }
00523 
00524   if (textStatus) {
00525     QString statusText = firstLine.mid(codeEnd + 1, endOfLine - (codeEnd + 1)).stripWhiteSpace();
00526     return String(statusText);
00527   }
00528 
00529   QString number = firstLine.mid(codeStart + 1, codeEnd - (codeStart + 1));
00530 
00531   bool ok = false;
00532   int code = number.toInt(&ok);
00533   if (!ok) {
00534     return Undefined();
00535   }
00536 
00537   return Number(code);
00538 }
00539 
00540 Value XMLHttpRequest::getStatus() const
00541 {
00542   return httpStatus(responseHeaders);
00543 }
00544 
00545 Value XMLHttpRequest::getStatusText() const
00546 {
00547   return httpStatus(responseHeaders, true);
00548 }
00549 
00550 void XMLHttpRequest::processSyncLoadResults(const QByteArray &data, const KURL &finalURL, const QString &headers)
00551 {
00552   if (!urlMatchesDocumentDomain(finalURL)) {
00553     abort();
00554     return;
00555   }
00556 
00557   responseHeaders = headers;
00558   changeState(Loaded);
00559   if (aborted) {
00560     return;
00561   }
00562 
00563 #ifdef APPLE_CHANGES
00564   const char *bytes = (const char *)data.data();
00565   int len = (int)data.size();
00566 
00567   slotData(0, bytes, len);
00568 #else
00569   slotData(0, data);
00570 #endif
00571 
00572   if (aborted) {
00573     return;
00574   }
00575 
00576   slotFinished(0);
00577 }
00578 
00579 void XMLHttpRequest::slotFinished(KIO::Job *)
00580 {
00581   if (decoder) {
00582     response += decoder->flush();
00583   }
00584 
00585   // make sure to forget about the job before emitting completed,
00586   // since changeState triggers JS code, which might e.g. call abort.
00587   job = 0;
00588   changeState(Completed);
00589 
00590   delete decoder;
00591   decoder = 0;
00592 }
00593 
00594 void XMLHttpRequest::slotRedirection(KIO::Job*, const KURL& url)
00595 {
00596   if (!urlMatchesDocumentDomain(url)) {
00597     abort();
00598   }
00599 }
00600 
00601 #ifdef APPLE_CHANGES
00602 void XMLHttpRequest::slotData( KIO::Job*, const char *data, int len )
00603 #else
00604 void XMLHttpRequest::slotData(KIO::Job*, const QByteArray &_data)
00605 #endif
00606 {
00607   if (state < Loaded ) {
00608     responseHeaders = job->queryMetaData("HTTP-Headers");
00609 
00610     // NOTE: Replace a 304 response with a 200! Both IE and Mozilla do this.
00611     // Problem first reported through bug# 110272.
00612     int codeStart = responseHeaders.find("304");
00613     if ( codeStart != -1) {
00614       int codeEnd = responseHeaders.find("\n", codeStart+3);
00615       if (codeEnd != -1)
00616         responseHeaders.replace(codeStart, (codeEnd-codeStart), "200 OK");
00617     }
00618 
00619     changeState(Loaded);
00620   }
00621 
00622 #ifndef APPLE_CHANGES
00623   const char *data = (const char *)_data.data();
00624   int len = (int)_data.size();
00625 #endif
00626 
00627   if ( decoder == NULL ) {
00628     int pos = responseHeaders.find("content-type:", 0, false);
00629 
00630     if ( pos > -1 ) {
00631       pos += 13;
00632       int index = responseHeaders.find('\n', pos);
00633       QString type = responseHeaders.mid(pos, (index-pos));
00634       index = type.find (';');
00635       if (index > -1)
00636         encoding = type.mid( index+1 ).remove(QRegExp("charset[ ]*=[ ]*", false)).stripWhiteSpace();
00637     }
00638 
00639     decoder = new Decoder;
00640     if (!encoding.isNull())
00641       decoder->setEncoding(encoding.latin1(), Decoder::EncodingFromHTTPHeader);
00642     else {
00643       // FIXME: Inherit the default encoding from the parent document?
00644     }
00645   }
00646   if (len == 0)
00647     return;
00648 
00649   if (len == -1)
00650     len = strlen(data);
00651 
00652   QString decoded = decoder->decode(data, len);
00653 
00654   response += decoded;
00655 
00656   if (!aborted) {
00657     changeState(Interactive);
00658   }
00659 }
00660 
00661 Value XMLHttpRequestProtoFunc::tryCall(ExecState *exec, Object &thisObj, const List &args)
00662 {
00663   if (!thisObj.inherits(&XMLHttpRequest::info)) {
00664     Object err = Error::create(exec,TypeError);
00665     exec->setException(err);
00666     return err;
00667   }
00668 
00669   XMLHttpRequest *request = static_cast<XMLHttpRequest *>(thisObj.imp());
00670   switch (id) {
00671   case XMLHttpRequest::Abort:
00672     request->abort();
00673     return Undefined();
00674   case XMLHttpRequest::GetAllResponseHeaders:
00675     if (args.size() != 0) {
00676     return Undefined();
00677     }
00678 
00679     return request->getAllResponseHeaders();
00680   case XMLHttpRequest::GetResponseHeader:
00681     if (args.size() != 1) {
00682     return Undefined();
00683     }
00684 
00685     return request->getResponseHeader(args[0].toString(exec).qstring());
00686   case XMLHttpRequest::Open:
00687     {
00688       if (args.size() < 2 || args.size() > 5) {
00689         return Undefined();
00690       }
00691 
00692       QString method = args[0].toString(exec).qstring();
00693       KHTMLPart *part = ::qt_cast<KHTMLPart *>(Window::retrieveActive(exec)->part());
00694       if (!part)
00695         return Undefined();
00696       KURL url = KURL(part->document().completeURL(args[1].toString(exec).qstring()).string());
00697 
00698       bool async = true;
00699       if (args.size() >= 3) {
00700     async = args[2].toBoolean(exec);
00701       }
00702 
00703       if (args.size() >= 4) {
00704     url.setUser(args[3].toString(exec).qstring());
00705       }
00706 
00707       if (args.size() >= 5) {
00708     url.setPass(args[4].toString(exec).qstring());
00709       }
00710 
00711       request->open(method, url, async);
00712 
00713       return Undefined();
00714     }
00715   case XMLHttpRequest::Send:
00716     {
00717       if (args.size() > 1) {
00718         return Undefined();
00719       }
00720 
00721       if (request->state != Loading) {
00722     return Undefined();
00723       }
00724 
00725       QString body;
00726       if (args.size() >= 1) {
00727         Object obj = Object::dynamicCast(args[0]);
00728         if (obj.isValid() && obj.inherits(&DOMDocument::info)) {
00729           DOM::Node docNode = static_cast<KJS::DOMDocument *>(obj.imp())->toNode();
00730           DOM::DocumentImpl *doc = static_cast<DOM::DocumentImpl *>(docNode.handle());
00731           
00732           try {
00733             body = doc->toString().string();
00734             // FIXME: also need to set content type, including encoding!
00735   
00736           } catch(DOM::DOMException& e) {
00737             Object err = Error::create(exec, GeneralError, "Exception serializing document");
00738             exec->setException(err);
00739           }
00740         } else {
00741           body = args[0].toString(exec).qstring();
00742         }
00743       }
00744 
00745       request->send(body);
00746 
00747       return Undefined();
00748     }
00749   case XMLHttpRequest::SetRequestHeader:
00750     if (args.size() != 2) {
00751       return Undefined();
00752     }
00753 
00754     request->setRequestHeader(args[0].toString(exec).qstring(), args[1].toString(exec).qstring());
00755 
00756     return Undefined();
00757   }
00758 
00759   return Undefined();
00760 }
00761 
00762 } // end namespace
00763 
00764 #include "xmlhttprequest.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys