2ad959d2eecfdd166146668605b92fbde0530f0b
[WebKit-https.git] / WebCore / bindings / js / JSXMLHttpRequest.cpp
1 /*
2  *  This file is part of the KDE libraries
3  *  Copyright (C) 2004 Apple Computer, Inc.
4  *  Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@nypop.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "JSXMLHttpRequest.h"
23
24 #include "Event.h"
25 #include "Frame.h"
26 #include "FrameLoader.h"
27 #include "HTMLDocument.h"
28 #include "kjs_events.h"
29 #include "kjs_window.h"
30 #include "xmlhttprequest.h"
31
32 #include "JSXMLHttpRequest.lut.h"
33
34 using namespace WebCore;
35
36 namespace KJS {
37
38 ////////////////////// JSXMLHttpRequest Object ////////////////////////
39
40 /* Source for JSXMLHttpRequestPrototypeTable.
41 @begin JSXMLHttpRequestPrototypeTable 7
42   abort                 JSXMLHttpRequest::Abort                   DontDelete|Function 0
43   getAllResponseHeaders JSXMLHttpRequest::GetAllResponseHeaders   DontDelete|Function 0
44   getResponseHeader     JSXMLHttpRequest::GetResponseHeader       DontDelete|Function 1
45   open                  JSXMLHttpRequest::Open                    DontDelete|Function 5
46   overrideMimeType      JSXMLHttpRequest::OverrideMIMEType        DontDelete|Function 1
47   send                  JSXMLHttpRequest::Send                    DontDelete|Function 1
48   setRequestHeader      JSXMLHttpRequest::SetRequestHeader        DontDelete|Function 2
49 # from the EventTarget interface
50 # FIXME: add DOM3 EventTarget methods (addEventListenerNS, removeEventListenerNS).
51   addEventListener      JSXMLHttpRequest::AddEventListener        DontDelete|Function 3
52   removeEventListener   JSXMLHttpRequest::RemoveEventListener     DontDelete|Function 3
53   dispatchEvent         JSXMLHttpRequest::DispatchEvent           DontDelete|Function 1
54 @end
55 */
56 KJS_DEFINE_PROTOTYPE(JSXMLHttpRequestPrototype)
57 KJS_IMPLEMENT_PROTOTYPE_FUNCTION(JSXMLHttpRequestPrototypeFunction)
58 KJS_IMPLEMENT_PROTOTYPE("JSXMLHttpRequest", JSXMLHttpRequestPrototype, JSXMLHttpRequestPrototypeFunction)
59
60 JSXMLHttpRequestConstructorImp::JSXMLHttpRequestConstructorImp(ExecState* exec, Document* d)
61     : doc(d)
62 {
63     setPrototype(exec->lexicalInterpreter()->builtinObjectPrototype());
64     putDirect(exec->propertyNames().prototype, JSXMLHttpRequestPrototype::self(exec), None);
65 }
66
67 bool JSXMLHttpRequestConstructorImp::implementsConstruct() const
68 {
69     return true;
70 }
71
72 JSObject* JSXMLHttpRequestConstructorImp::construct(ExecState* exec, const List&)
73 {
74     return new JSXMLHttpRequest(exec, doc.get());
75 }
76
77 const ClassInfo JSXMLHttpRequest::info = { "JSXMLHttpRequest", 0, &JSXMLHttpRequestTable, 0 };
78
79 /* Source for JSXMLHttpRequestTable.
80 @begin JSXMLHttpRequestTable 7
81   readyState            JSXMLHttpRequest::ReadyState              DontDelete|ReadOnly
82   responseText          JSXMLHttpRequest::ResponseText            DontDelete|ReadOnly
83   responseXML           JSXMLHttpRequest::ResponseXML             DontDelete|ReadOnly
84   status                JSXMLHttpRequest::Status                  DontDelete|ReadOnly
85   statusText            JSXMLHttpRequest::StatusText              DontDelete|ReadOnly
86   onreadystatechange    JSXMLHttpRequest::Onreadystatechange      DontDelete
87   onload                JSXMLHttpRequest::Onload                  DontDelete
88 @end
89 */
90
91 bool JSXMLHttpRequest::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
92 {
93     return getStaticValueSlot<JSXMLHttpRequest, DOMObject>(exec, &JSXMLHttpRequestTable, this, propertyName, slot);
94 }
95
96 JSValue* JSXMLHttpRequest::getValueProperty(ExecState* exec, int token) const
97 {
98     ExceptionCode ec = 0;
99
100     switch (token) {
101         case ReadyState:
102             return jsNumber(m_impl->getReadyState());
103         case ResponseText:
104             return jsStringOrNull(m_impl->getResponseText());
105         case ResponseXML:
106             if (Document* responseXML = m_impl->getResponseXML())
107                 return toJS(exec, responseXML);
108             return jsNull();
109         case Status: {
110             JSValue* result = jsNumber(m_impl->getStatus(ec));
111             setDOMException(exec, ec);
112             return result;
113         }
114         case StatusText: {
115             JSValue* result = jsString(m_impl->getStatusText(ec));
116             setDOMException(exec, ec);
117             return result;
118         }
119         case Onreadystatechange:
120             if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onReadyStateChangeListener()))
121                 if (JSObject* listenerObj = listener->listenerObj())
122                     return listenerObj;
123             return jsNull();
124         case Onload:
125             if (JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(m_impl->onLoadListener()))
126                 if (JSObject* listenerObj = listener->listenerObj())
127                     return listenerObj;
128             return jsNull();
129         default:
130             return 0;
131     }
132 }
133
134 void JSXMLHttpRequest::put(ExecState* exec, const Identifier& propertyName, JSValue* value, int attr)
135 {
136     lookupPut<JSXMLHttpRequest,DOMObject>(exec, propertyName, value, attr, &JSXMLHttpRequestTable, this );
137 }
138
139 void JSXMLHttpRequest::putValueProperty(ExecState* exec, int token, JSValue* value, int /*attr*/)
140 {
141     switch (token) {
142         case Onreadystatechange:
143             m_impl->setOnReadyStateChangeListener(Window::retrieveActive(exec)->findOrCreateJSUnprotectedEventListener(value, true));
144             break;
145         case Onload:
146             m_impl->setOnLoadListener(Window::retrieveActive(exec)->findOrCreateJSUnprotectedEventListener(value, true));
147             break;
148     }
149 }
150
151 void JSXMLHttpRequest::mark()
152 {
153     DOMObject::mark();
154
155     JSUnprotectedEventListener* onReadyStateChangeListener = static_cast<JSUnprotectedEventListener*>(m_impl->onReadyStateChangeListener());
156     JSUnprotectedEventListener* onLoadListener = static_cast<JSUnprotectedEventListener*>(m_impl->onLoadListener());
157
158     if (onReadyStateChangeListener)
159         onReadyStateChangeListener->mark();
160
161     if (onLoadListener)
162         onLoadListener->mark();
163     
164     typedef XMLHttpRequest::EventListenersMap EventListenersMap;
165     typedef XMLHttpRequest::ListenerVector ListenerVector;
166     EventListenersMap& eventListeners = m_impl->eventListeners();
167     for (EventListenersMap::iterator mapIter = eventListeners.begin(); mapIter != eventListeners.end(); ++mapIter) {
168         for (ListenerVector::iterator vecIter = mapIter->second.begin(); vecIter != mapIter->second.end(); ++vecIter) {
169             JSUnprotectedEventListener* listener = static_cast<JSUnprotectedEventListener*>(vecIter->get());
170             listener->mark();
171         }
172     }
173 }
174
175
176 JSXMLHttpRequest::JSXMLHttpRequest(ExecState* exec, Document* d)
177   : m_impl(new XMLHttpRequest(d))
178 {
179     setPrototype(JSXMLHttpRequestPrototype::self(exec));
180     ScriptInterpreter::putDOMObject(m_impl.get(), this);
181 }
182
183 JSXMLHttpRequest::~JSXMLHttpRequest()
184 {
185     m_impl->setOnReadyStateChangeListener(0);
186     m_impl->setOnLoadListener(0);
187     m_impl->eventListeners().clear();
188     ScriptInterpreter::forgetDOMObject(m_impl.get());
189 }
190
191 JSValue* JSXMLHttpRequestPrototypeFunction::callAsFunction(ExecState* exec, JSObject* thisObj, const List& args)
192 {
193     if (!thisObj->inherits(&JSXMLHttpRequest::info))
194         return throwError(exec, TypeError);
195
196     JSXMLHttpRequest* request = static_cast<JSXMLHttpRequest*>(thisObj);
197
198     ExceptionCode ec = 0;
199
200     switch (id) {
201         case JSXMLHttpRequest::Abort:
202             request->m_impl->abort();
203             return jsUndefined();
204
205         case JSXMLHttpRequest::GetAllResponseHeaders:
206             return jsStringOrUndefined(request->m_impl->getAllResponseHeaders());
207
208         case JSXMLHttpRequest::GetResponseHeader:
209             if (args.size() < 1)
210                 return throwError(exec, SyntaxError, "Not enough arguments");
211
212             return jsStringOrNull(request->m_impl->getResponseHeader(args[0]->toString(exec)));
213
214         case JSXMLHttpRequest::Open: {
215             if (args.size() < 2)
216                 return throwError(exec, SyntaxError, "Not enough arguments");
217
218             String method = args[0]->toString(exec);
219             KURL url = Window::retrieveActive(exec)->frame()->loader()->completeURL(DeprecatedString(args[1]->toString(exec)));
220
221             bool async = true;
222             if (args.size() >= 3)
223                 async = args[2]->toBoolean(exec);
224
225             String user;
226             String password;
227             if (args.size() >= 4 && !args[3]->isUndefined()) {
228                 user = args[3]->toString(exec);
229
230                 if (args.size() >= 5 && !args[4]->isUndefined())
231                     password = args[4]->toString(exec);
232             }
233
234             request->m_impl->open(method, url, async, user, password, ec);
235             setDOMException(exec, ec);
236
237             return jsUndefined();
238         }
239         case JSXMLHttpRequest::Send: {
240             String body;
241
242             if (args.size() >= 1) {
243                 if (args[0]->toObject(exec)->inherits(&JSDocument::info)) {
244                     Document* doc = static_cast<Document*>(static_cast<JSDocument*>(args[0]->toObject(exec))->impl());
245                     body = doc->toString().deprecatedString();
246                 } else {
247                     // converting certain values (like null) to object can set an exception
248                     if (exec->hadException())
249                         exec->clearException();
250                     else
251                         body = args[0]->toString(exec);
252                 }
253             }
254
255             request->m_impl->send(body, ec);
256             setDOMException(exec, ec);
257
258             return jsUndefined();
259         }
260         case JSXMLHttpRequest::SetRequestHeader:
261             if (args.size() < 2)
262                 return throwError(exec, SyntaxError, "Not enough arguments");
263
264             request->m_impl->setRequestHeader(args[0]->toString(exec), args[1]->toString(exec), ec);
265             setDOMException(exec, ec);
266             return jsUndefined();
267
268         case JSXMLHttpRequest::OverrideMIMEType:
269             if (args.size() < 1)
270                 return throwError(exec, SyntaxError, "Not enough arguments");
271
272             request->m_impl->overrideMIMEType(args[0]->toString(exec));
273             return jsUndefined();
274         
275         case JSXMLHttpRequest::AddEventListener: {
276             JSUnprotectedEventListener* listener = Window::retrieveActive(exec)->findOrCreateJSUnprotectedEventListener(args[1], true);
277             if (listener)
278                 request->m_impl->addEventListener(args[0]->toString(exec), listener, args[2]->toBoolean(exec));
279             return jsUndefined();
280         }
281         case JSXMLHttpRequest::RemoveEventListener: {
282             JSUnprotectedEventListener* listener = Window::retrieveActive(exec)->findJSUnprotectedEventListener(args[1], true);
283             if (listener)
284                 request->m_impl->removeEventListener(args[0]->toString(exec), listener, args[2]->toBoolean(exec));
285             return jsUndefined();
286         }
287         case JSXMLHttpRequest::DispatchEvent: {
288             bool result = request->m_impl->dispatchEvent(toEvent(args[0]), ec);
289             setDOMException(exec, ec);
290             return jsBoolean(result);
291         }
292     }
293
294     return jsUndefined();
295 }
296
297 } // end namespace