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