khtml Library API Documentation

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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 #endif 00044 00045 using namespace KJS; 00046 using khtml::Decoder; 00047 00049 00050 /* Source for XMLHttpRequestProtoTable. 00051 @begin XMLHttpRequestProtoTable 7 00052 abort XMLHttpRequest::Abort DontDelete|Function 0 00053 getAllResponseHeaders XMLHttpRequest::GetAllResponseHeaders DontDelete|Function 0 00054 getResponseHeader XMLHttpRequest::GetResponseHeader DontDelete|Function 1 00055 open XMLHttpRequest::Open DontDelete|Function 5 00056 send XMLHttpRequest::Send DontDelete|Function 1 00057 setRequestHeader XMLHttpRequest::SetRequestHeader DontDelete|Function 2 00058 @end 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 /* Source for XMLHttpRequestTable. 00111 @begin XMLHttpRequestTable 7 00112 readyState XMLHttpRequest::ReadyState DontDelete|ReadOnly 00113 responseText XMLHttpRequest::ResponseText DontDelete|ReadOnly 00114 responseXML XMLHttpRequest::ResponseXML DontDelete|ReadOnly 00115 status XMLHttpRequest::Status DontDelete|ReadOnly 00116 statusText XMLHttpRequest::StatusText DontDelete|ReadOnly 00117 onreadystatechange XMLHttpRequest::Onreadystatechange DontDelete 00118 onload XMLHttpRequest::Onload DontDelete 00119 @end 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 /*attr*/) 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 // a local file can load anything 00258 if (documentURL.protocol().lower() == "file") { 00259 return true; 00260 } 00261 00262 // but a remote document can only load from the same port on the server 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 // clear stuff from possible previous load 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 // FIXME: determine post encoding correctly by looking in headers for charset 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 // FIXME: Inherit the default encoding from the parent document? 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 // FIXME: also need to set content type, including encoding! 00627 00628 } catch(DOM::DOMException& e) { 00629 Object err = Error::create(exec, GeneralError, "Exception serializing document"); 00630 exec->setException(err); 00631 } 00632 } else { 00633 // converting certain values (like null) to object can set an exception 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 } // end namespace 00657 00658 #include "xmlhttprequest.moc"
KDE Logo
This file is part of the documentation for khtml Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:44:57 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003