b98068032f160032011acf0378171a3aedc6caa8
[WebKit-https.git] / WebCore / bindings / v8 / custom / V8XMLHttpRequestCustom.cpp
1 /*
2  * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "config.h"
32 #include "XMLHttpRequest.h"
33
34 #include "Frame.h"
35 #include "V8Binding.h"
36 #include "V8Document.h"
37 #include "V8CustomBinding.h"
38 #include "V8HTMLDocument.h"
39 #include "V8ObjectEventListener.h"
40 #include "V8Proxy.h"
41 #include "V8Utilities.h"
42 #include "WorkerContext.h"
43 #include "WorkerContextExecutionProxy.h"
44
45 namespace WebCore {
46
47 static PassRefPtr<EventListener> getEventListener(XMLHttpRequest* xmlHttpRequest, v8::Local<v8::Value> value, bool findOnly)
48 {
49 #if ENABLE(WORKERS)
50     WorkerContextExecutionProxy* workerContextProxy = WorkerContextExecutionProxy::retrieve();
51     if (workerContextProxy)
52         return workerContextProxy->findOrCreateObjectEventListener(value, false, findOnly);
53 #endif
54
55     V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
56     if (proxy) {
57         V8EventListenerList* list = proxy->objectListeners();
58         return findOnly ? list->findWrapper(value, false) : list->findOrCreateWrapper<V8ObjectEventListener>(proxy->frame(), value, false);
59     }
60
61     return PassRefPtr<EventListener>();
62 }
63
64 ACCESSOR_GETTER(XMLHttpRequestOnabort)
65 {
66     INC_STATS("DOM.XMLHttpRequest.onabort._get");
67     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
68     if (xmlHttpRequest->onabort()) {
69         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onabort());
70         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
71         return v8Listener;
72     }
73     return v8::Null();
74 }
75
76 ACCESSOR_SETTER(XMLHttpRequestOnabort)
77 {
78     INC_STATS("DOM.XMLHttpRequest.onabort._set");
79     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
80     if (value->IsNull()) {
81         if (xmlHttpRequest->onabort()) {
82             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onabort());
83             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
84             removeHiddenDependency(info.Holder(), v8Listener, V8Custom::kXMLHttpRequestCacheIndex);
85         }
86
87         // Clear the listener.
88         xmlHttpRequest->setOnabort(0);
89     } else {
90         RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, value, false);
91         if (listener) {
92             xmlHttpRequest->setOnabort(listener);
93             createHiddenDependency(info.Holder(), value, V8Custom::kXMLHttpRequestCacheIndex);
94         }
95     }
96 }
97
98 ACCESSOR_GETTER(XMLHttpRequestOnerror)
99 {
100     INC_STATS("DOM.XMLHttpRequest.onerror._get");
101     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
102     if (xmlHttpRequest->onerror()) {
103         RefPtr<V8ObjectEventListener> listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onerror());
104         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
105         return v8Listener;
106     }
107     return v8::Null();
108 }
109
110 ACCESSOR_SETTER(XMLHttpRequestOnerror)
111 {
112     INC_STATS("DOM.XMLHttpRequest.onerror._set");
113     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
114     if (value->IsNull()) {
115         if (xmlHttpRequest->onerror()) {
116             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onerror());
117             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
118             removeHiddenDependency(info.Holder(), v8Listener, V8Custom::kXMLHttpRequestCacheIndex);
119         }
120
121         // Clear the listener.
122         xmlHttpRequest->setOnerror(0);
123     } else {
124         RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, value, false);
125         if (listener) {
126             xmlHttpRequest->setOnerror(listener);
127             createHiddenDependency(info.Holder(), value, V8Custom::kXMLHttpRequestCacheIndex);
128         }
129     }
130 }
131
132 ACCESSOR_GETTER(XMLHttpRequestOnload)
133 {
134     INC_STATS("DOM.XMLHttpRequest.onload._get");
135     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
136     if (xmlHttpRequest->onload()) {
137         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onload());
138         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
139         return v8Listener;
140     }
141     return v8::Null();
142 }
143
144 ACCESSOR_SETTER(XMLHttpRequestOnload)
145 {
146     INC_STATS("DOM.XMLHttpRequest.onload._set");
147     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
148     if (value->IsNull()) {
149         if (xmlHttpRequest->onload()) {
150             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onload());
151             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
152             removeHiddenDependency(info.Holder(), v8Listener, V8Custom::kXMLHttpRequestCacheIndex);
153         }
154
155         xmlHttpRequest->setOnload(0);
156
157     } else {
158         RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, value, false);
159         if (listener) {
160             xmlHttpRequest->setOnload(listener.get());
161             createHiddenDependency(info.Holder(), value, V8Custom::kXMLHttpRequestCacheIndex);
162         }
163     }
164 }
165
166 ACCESSOR_GETTER(XMLHttpRequestOnloadstart)
167 {
168     INC_STATS("DOM.XMLHttpRequest.onloadstart._get");
169     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
170     if (xmlHttpRequest->onloadstart()) {
171         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onloadstart());
172         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
173         return v8Listener;
174     }
175     return v8::Null();
176 }
177
178 ACCESSOR_SETTER(XMLHttpRequestOnloadstart)
179 {
180     INC_STATS("DOM.XMLHttpRequest.onloadstart._set");
181     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
182     if (value->IsNull()) {
183         if (xmlHttpRequest->onloadstart()) {
184             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onloadstart());
185             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
186             removeHiddenDependency(info.Holder(), v8Listener, V8Custom::kXMLHttpRequestCacheIndex);
187         }
188
189         // Clear the listener.
190         xmlHttpRequest->setOnloadstart(0);
191     } else {
192         RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, value, false);
193         if (listener) {
194             xmlHttpRequest->setOnloadstart(listener);
195             createHiddenDependency(info.Holder(), value, V8Custom::kXMLHttpRequestCacheIndex);
196         }
197     }
198 }
199
200 ACCESSOR_GETTER(XMLHttpRequestOnprogress)
201 {
202     INC_STATS("DOM.XMLHttpRequest.onprogress._get");
203     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
204     if (xmlHttpRequest->onprogress()) {
205         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onprogress());
206         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
207         return v8Listener;
208     }
209     return v8::Null();
210 }
211
212 ACCESSOR_SETTER(XMLHttpRequestOnprogress)
213 {
214     INC_STATS("DOM.XMLHttpRequest.onprogress._set");
215     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
216     if (value->IsNull()) {
217         if (xmlHttpRequest->onprogress()) {
218             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onprogress());
219             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
220             removeHiddenDependency(info.Holder(), v8Listener, V8Custom::kXMLHttpRequestCacheIndex);
221         }
222
223         // Clear the listener.
224         xmlHttpRequest->setOnprogress(0);
225     } else {
226         RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, value, false);
227         if (listener) {
228             xmlHttpRequest->setOnprogress(listener);
229             createHiddenDependency(info.Holder(), value, V8Custom::kXMLHttpRequestCacheIndex);
230         }
231     }
232 }
233
234 ACCESSOR_GETTER(XMLHttpRequestOnreadystatechange)
235 {
236     INC_STATS("DOM.XMLHttpRequest.onreadystatechange._get");
237     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
238     if (xmlHttpRequest->onreadystatechange()) {
239         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onreadystatechange());
240         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
241         return v8Listener;
242     }
243     return v8::Null();
244 }
245
246 ACCESSOR_SETTER(XMLHttpRequestOnreadystatechange)
247 {
248     INC_STATS("DOM.XMLHttpRequest.onreadystatechange._set");
249     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
250     if (value->IsNull()) {
251         if (xmlHttpRequest->onreadystatechange()) {
252             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onreadystatechange());
253             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
254             removeHiddenDependency(info.Holder(), v8Listener, V8Custom::kXMLHttpRequestCacheIndex);
255         }
256
257         // Clear the listener.
258         xmlHttpRequest->setOnreadystatechange(0);
259     } else {
260         RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, value, false);
261         if (listener) {
262             xmlHttpRequest->setOnreadystatechange(listener.get());
263             createHiddenDependency(info.Holder(), value, V8Custom::kXMLHttpRequestCacheIndex);
264         }
265     }
266 }
267
268 ACCESSOR_GETTER(XMLHttpRequestResponseText)
269 {
270     // FIXME: This is only needed because webkit set this getter as custom.
271     // So we need a custom method to avoid forking the IDL file.
272     INC_STATS("DOM.XMLHttpRequest.responsetext._get");
273     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
274     return v8StringOrNull(xmlHttpRequest->responseText());
275 }
276
277 CALLBACK_FUNC_DECL(XMLHttpRequestAddEventListener)
278 {
279     INC_STATS("DOM.XMLHttpRequest.addEventListener()");
280     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
281
282     RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, args[1], false);
283     if (listener) {
284         String type = toWebCoreString(args[0]);
285         bool useCapture = args[2]->BooleanValue();
286         xmlHttpRequest->addEventListener(type, listener, useCapture);
287
288         createHiddenDependency(args.Holder(), args[1], V8Custom::kXMLHttpRequestCacheIndex);
289     }
290     return v8::Undefined();
291 }
292
293 CALLBACK_FUNC_DECL(XMLHttpRequestRemoveEventListener)
294 {
295     INC_STATS("DOM.XMLHttpRequest.removeEventListener()");
296     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
297
298     RefPtr<EventListener> listener = getEventListener(xmlHttpRequest, args[1], true);
299     if (listener) {
300         String type = toWebCoreString(args[0]);
301         bool useCapture = args[2]->BooleanValue();
302         xmlHttpRequest->removeEventListener(type, listener.get(), useCapture);
303
304         removeHiddenDependency(args.Holder(), args[1], V8Custom::kXMLHttpRequestCacheIndex);
305     }
306
307     return v8::Undefined();
308 }
309
310 CALLBACK_FUNC_DECL(XMLHttpRequestOpen)
311 {
312     INC_STATS("DOM.XMLHttpRequest.open()");
313     // Four cases:
314     // open(method, url)
315     // open(method, url, async)
316     // open(method, url, async, user)
317     // open(method, url, async, user, passwd)
318
319     if (args.Length() < 2)
320         return throwError("Not enough arguments", V8Proxy::SyntaxError);
321
322     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
323
324     String method = toWebCoreString(args[0]);
325     String urlstring = toWebCoreString(args[1]);
326     ScriptExecutionContext* context = getScriptExecutionContext();
327     if (!context)
328         return v8::Undefined();
329
330     KURL url = context->completeURL(urlstring);
331
332     bool async = (args.Length() < 3) ? true : args[2]->BooleanValue();
333
334     ExceptionCode ec = 0;
335     String user, passwd;
336     if (args.Length() >= 4 && !args[3]->IsUndefined()) {
337         user = toWebCoreStringWithNullCheck(args[3]);
338
339         if (args.Length() >= 5 && !args[4]->IsUndefined()) {
340             passwd = toWebCoreStringWithNullCheck(args[4]);
341             xmlHttpRequest->open(method, url, async, user, passwd, ec);
342         } else
343             xmlHttpRequest->open(method, url, async, user, ec);
344     } else
345         xmlHttpRequest->open(method, url, async, ec);
346
347     if (ec)
348         return throwError(ec);
349
350     return v8::Undefined();
351 }
352
353 static bool IsDocumentType(v8::Handle<v8::Value> value)
354 {
355     // FIXME: add other document types.
356     return V8Document::HasInstance(value) || V8HTMLDocument::HasInstance(value);
357 }
358
359 CALLBACK_FUNC_DECL(XMLHttpRequestSend)
360 {
361     INC_STATS("DOM.XMLHttpRequest.send()");
362     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
363
364     ExceptionCode ec = 0;
365     if (args.Length() < 1)
366         xmlHttpRequest->send(ec);
367     else {
368         v8::Handle<v8::Value> arg = args[0];
369         // FIXME: upstream handles "File" objects too.
370         if (IsDocumentType(arg)) {
371             v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(arg);
372             Document* document = V8DOMWrapper::convertDOMWrapperToNode<Document>(object);
373             ASSERT(document);
374             xmlHttpRequest->send(document, ec);
375         } else
376             xmlHttpRequest->send(toWebCoreStringWithNullCheck(arg), ec);
377     }
378
379     if (ec)
380         return throwError(ec);
381
382     return v8::Undefined();
383 }
384
385 CALLBACK_FUNC_DECL(XMLHttpRequestSetRequestHeader) {
386     INC_STATS("DOM.XMLHttpRequest.setRequestHeader()");
387     if (args.Length() < 2)
388         return throwError("Not enough arguments", V8Proxy::SyntaxError);
389
390     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
391     ExceptionCode ec = 0;
392     String header = toWebCoreString(args[0]);
393     String value = toWebCoreString(args[1]);
394     xmlHttpRequest->setRequestHeader(header, value, ec);
395     if (ec)
396         return throwError(ec);
397     return v8::Undefined();
398 }
399
400 CALLBACK_FUNC_DECL(XMLHttpRequestGetResponseHeader)
401 {
402     INC_STATS("DOM.XMLHttpRequest.getResponseHeader()");
403     if (args.Length() < 1)
404         return throwError("Not enough arguments", V8Proxy::SyntaxError);
405
406     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
407     ExceptionCode ec = 0;
408     String header = toWebCoreString(args[0]);
409     String result = xmlHttpRequest->getResponseHeader(header, ec);
410     if (ec)
411         return throwError(ec);
412     return v8StringOrNull(result);
413 }
414
415 CALLBACK_FUNC_DECL(XMLHttpRequestOverrideMimeType)
416 {
417     INC_STATS("DOM.XMLHttpRequest.overrideMimeType()");
418     if (args.Length() < 1)
419         return throwError("Not enough arguments", V8Proxy::SyntaxError);
420
421     XMLHttpRequest* xmlHttpRequest = V8DOMWrapper::convertToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
422     String value = toWebCoreString(args[0]);
423     xmlHttpRequest->overrideMimeType(value);
424     return v8::Undefined();
425 }
426
427 CALLBACK_FUNC_DECL(XMLHttpRequestDispatchEvent)
428 {
429     INC_STATS("DOM.XMLHttpRequest.dispatchEvent()");
430     return v8::Undefined();
431 }
432
433 } // namespace WebCore