ea69f0b889f278cbcfffb1a54ce92215bcc8a397
[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 "V8XMLHttpRequestUtilities.h"
42
43 namespace WebCore {
44
45 ACCESSOR_GETTER(XMLHttpRequestOnabort)
46 {
47     INC_STATS("DOM.XMLHttpRequest.onabort._get");
48     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
49     if (xmlHttpRequest->onabort()) {
50         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onabort());
51         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
52         return v8Listener;
53     }
54     return v8::Undefined();
55 }
56
57 ACCESSOR_SETTER(XMLHttpRequestOnabort)
58 {
59     INC_STATS("DOM.XMLHttpRequest.onabort._set");
60     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
61     if (value->IsNull()) {
62         if (xmlHttpRequest->onabort()) {
63             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onabort());
64             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
65             removeHiddenXHRDependency(info.Holder(), v8Listener);
66         }
67
68         // Clear the listener.
69         xmlHttpRequest->setOnabort(0);
70     } else {
71         V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
72         if (!proxy)
73             return;
74
75         RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(value, false);
76         if (listener) {
77             xmlHttpRequest->setOnabort(listener);
78             createHiddenXHRDependency(info.Holder(), value);
79         }
80     }
81 }
82
83 ACCESSOR_GETTER(XMLHttpRequestOnerror)
84 {
85     INC_STATS("DOM.XMLHttpRequest.onerror._get");
86     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
87     if (xmlHttpRequest->onerror()) {
88         RefPtr<V8ObjectEventListener> listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onerror());
89         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
90         return v8Listener;
91     }
92     return v8::Undefined();
93 }
94
95 ACCESSOR_SETTER(XMLHttpRequestOnerror)
96 {
97     INC_STATS("DOM.XMLHttpRequest.onerror._set");
98     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
99     if (value->IsNull()) {
100         if (xmlHttpRequest->onerror()) {
101             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onerror());
102             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
103             removeHiddenXHRDependency(info.Holder(), v8Listener);
104         }
105
106         // Clear the listener.
107         xmlHttpRequest->setOnerror(0);
108     } else {
109         V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
110         if (!proxy)
111             return;
112
113         RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(value, false);
114         if (listener) {
115             xmlHttpRequest->setOnerror(listener);
116             createHiddenXHRDependency(info.Holder(), value);
117         }
118     }
119 }
120
121 ACCESSOR_GETTER(XMLHttpRequestOnload)
122 {
123     INC_STATS("DOM.XMLHttpRequest.onload._get");
124     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
125     if (xmlHttpRequest->onload()) {
126         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onload());
127         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
128         return v8Listener;
129     }
130     return v8::Undefined();
131 }
132
133 ACCESSOR_SETTER(XMLHttpRequestOnload)
134 {
135     INC_STATS("DOM.XMLHttpRequest.onload._set");
136     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
137     if (value->IsNull()) {
138         if (xmlHttpRequest->onload()) {
139             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onload());
140             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
141             removeHiddenXHRDependency(info.Holder(), v8Listener);
142         }
143
144         xmlHttpRequest->setOnload(0);
145
146     } else {
147         V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
148         if (!proxy)
149             return;
150
151         RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(value, false);
152         if (listener) {
153             xmlHttpRequest->setOnload(listener.get());
154             createHiddenXHRDependency(info.Holder(), value);
155         }
156     }
157 }
158
159 ACCESSOR_GETTER(XMLHttpRequestOnloadstart)
160 {
161     INC_STATS("DOM.XMLHttpRequest.onloadstart._get");
162     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
163     if (xmlHttpRequest->onloadstart()) {
164         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onloadstart());
165         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
166         return v8Listener;
167     }
168     return v8::Undefined();
169 }
170
171 ACCESSOR_SETTER(XMLHttpRequestOnloadstart)
172 {
173     INC_STATS("DOM.XMLHttpRequest.onloadstart._set");
174     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
175     if (value->IsNull()) {
176         if (xmlHttpRequest->onloadstart()) {
177             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onloadstart());
178             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
179             removeHiddenXHRDependency(info.Holder(), v8Listener);
180         }
181
182         // Clear the listener.
183         xmlHttpRequest->setOnloadstart(0);
184     } else {
185         V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
186         if (!proxy)
187             return;
188
189         RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(value, false);
190         if (listener) {
191             xmlHttpRequest->setOnloadstart(listener);
192             createHiddenXHRDependency(info.Holder(), value);
193         }
194     }
195 }
196
197 ACCESSOR_GETTER(XMLHttpRequestOnprogress)
198 {
199     INC_STATS("DOM.XMLHttpRequest.onprogress._get");
200     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
201     if (xmlHttpRequest->onprogress()) {
202         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onprogress());
203         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
204         return v8Listener;
205     }
206     return v8::Undefined();
207 }
208
209 ACCESSOR_SETTER(XMLHttpRequestOnprogress)
210 {
211     INC_STATS("DOM.XMLHttpRequest.onprogress._set");
212     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
213     if (value->IsNull()) {
214         if (xmlHttpRequest->onprogress()) {
215             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onprogress());
216             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
217             removeHiddenXHRDependency(info.Holder(), v8Listener);
218         }
219
220         // Clear the listener.
221         xmlHttpRequest->setOnprogress(0);
222     } else {
223         V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
224         if (!proxy)
225             return;
226
227         RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(value, false);
228         if (listener) {
229             xmlHttpRequest->setOnprogress(listener);
230             createHiddenXHRDependency(info.Holder(), value);
231         }
232     }
233 }
234
235 ACCESSOR_GETTER(XMLHttpRequestOnreadystatechange)
236 {
237     INC_STATS("DOM.XMLHttpRequest.onreadystatechange._get");
238     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
239     if (xmlHttpRequest->onreadystatechange()) {
240         V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onreadystatechange());
241         v8::Local<v8::Object> v8Listener = listener->getListenerObject();
242         return v8Listener;
243     }
244     return v8::Undefined();
245 }
246
247 ACCESSOR_SETTER(XMLHttpRequestOnreadystatechange)
248 {
249     INC_STATS("DOM.XMLHttpRequest.onreadystatechange._set");
250     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
251     if (value->IsNull()) {
252         if (xmlHttpRequest->onreadystatechange()) {
253             V8ObjectEventListener* listener = static_cast<V8ObjectEventListener*>(xmlHttpRequest->onreadystatechange());
254             v8::Local<v8::Object> v8Listener = listener->getListenerObject();
255             removeHiddenXHRDependency(info.Holder(), v8Listener);
256         }
257
258         // Clear the listener.
259         xmlHttpRequest->setOnreadystatechange(0);
260     } else {
261         V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
262         if (!proxy)
263             return;
264
265         RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(value, false);
266         if (listener) {
267             xmlHttpRequest->setOnreadystatechange(listener.get());
268             createHiddenXHRDependency(info.Holder(), value);
269         }
270     }
271 }
272
273 ACCESSOR_GETTER(XMLHttpRequestResponseText)
274 {
275     // FIXME: This is only needed because webkit set this getter as custom.
276     // So we need a custom method to avoid forking the IDL file.
277     INC_STATS("DOM.XMLHttpRequest.responsetext._get");
278     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, info.Holder());
279     return v8StringOrNull(xmlHttpRequest->responseText());
280 }
281
282 CALLBACK_FUNC_DECL(XMLHttpRequestAddEventListener)
283 {
284     INC_STATS("DOM.XMLHttpRequest.addEventListener()");
285     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
286
287     V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
288     if (!proxy)
289         return v8::Undefined();
290
291     RefPtr<EventListener> listener = proxy->FindOrCreateObjectEventListener(args[1], false);
292     if (listener) {
293         String type = toWebCoreString(args[0]);
294         bool useCapture = args[2]->BooleanValue();
295         xmlHttpRequest->addEventListener(type, listener, useCapture);
296
297         createHiddenXHRDependency(args.Holder(), args[1]);
298     }
299     return v8::Undefined();
300 }
301
302 CALLBACK_FUNC_DECL(XMLHttpRequestRemoveEventListener)
303 {
304     INC_STATS("DOM.XMLHttpRequest.removeEventListener()");
305     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
306
307     V8Proxy* proxy = V8Proxy::retrieve(xmlHttpRequest->scriptExecutionContext());
308     if (!proxy)
309         return v8::Undefined(); // Probably leaked.
310
311     RefPtr<EventListener> listener = proxy->FindObjectEventListener(args[1], false);
312
313     if (listener) {
314         String type = toWebCoreString(args[0]);
315         bool useCapture = args[2]->BooleanValue();
316         xmlHttpRequest->removeEventListener(type, listener.get(), useCapture);
317
318         removeHiddenXHRDependency(args.Holder(), args[1]);
319     }
320
321     return v8::Undefined();
322 }
323
324 CALLBACK_FUNC_DECL(XMLHttpRequestOpen)
325 {
326     INC_STATS("DOM.XMLHttpRequest.open()");
327     // Four cases:
328     // open(method, url)
329     // open(method, url, async)
330     // open(method, url, async, user)
331     // open(method, url, async, user, passwd)
332
333     if (args.Length() < 2)
334         return throwError("Not enough arguments", V8Proxy::SYNTAX_ERROR);
335
336     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
337
338     String method = toWebCoreString(args[0]);
339     String urlstring = toWebCoreString(args[1]);
340     V8Proxy* proxy = V8Proxy::retrieve();
341     KURL url = proxy->frame()->document()->completeURL(urlstring);
342
343     bool async = (args.Length() < 3) ? true : args[2]->BooleanValue();
344
345     ExceptionCode ec = 0;
346     String user, passwd;
347     if (args.Length() >= 4 && !args[3]->IsUndefined()) {
348         user = valueToStringWithNullCheck(args[3]);
349
350         if (args.Length() >= 5 && !args[4]->IsUndefined()) {
351             passwd = valueToStringWithNullCheck(args[4]);
352             xmlHttpRequest->open(method, url, async, user, passwd, ec);
353         } else
354             xmlHttpRequest->open(method, url, async, user, ec);
355     } else
356         xmlHttpRequest->open(method, url, async, ec);
357
358     if (ec)
359         return throwError(ec);
360
361     return v8::Undefined();
362 }
363
364 static bool IsDocumentType(v8::Handle<v8::Value> value)
365 {
366     // FIXME: add other document types.
367     return V8Document::HasInstance(value) || V8HTMLDocument::HasInstance(value);
368 }
369
370 CALLBACK_FUNC_DECL(XMLHttpRequestSend)
371 {
372     INC_STATS("DOM.XMLHttpRequest.send()");
373     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
374
375     ExceptionCode ec = 0;
376     if (args.Length() < 1)
377         xmlHttpRequest->send(ec);
378     else {
379         v8::Handle<v8::Value> arg = args[0];
380         // FIXME: upstream handles "File" objects too.
381         if (IsDocumentType(arg)) {
382             v8::Handle<v8::Object> object = v8::Handle<v8::Object>::Cast(arg);
383             Document* document = V8Proxy::DOMWrapperToNode<Document>(object);
384             ASSERT(document);
385             xmlHttpRequest->send(document, ec);
386         } else
387             xmlHttpRequest->send(valueToStringWithNullCheck(arg), ec);
388     }
389
390     if (ec)
391         return throwError(ec);
392
393     return v8::Undefined();
394 }
395
396 CALLBACK_FUNC_DECL(XMLHttpRequestSetRequestHeader) {
397     INC_STATS("DOM.XMLHttpRequest.setRequestHeader()");
398     if (args.Length() < 2)
399         return throwError("Not enough arguments", V8Proxy::SYNTAX_ERROR);
400
401     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
402     ExceptionCode ec = 0;
403     String header = toWebCoreString(args[0]);
404     String value = toWebCoreString(args[1]);
405     xmlHttpRequest->setRequestHeader(header, value, ec);
406     if (ec)
407         return throwError(ec);
408     return v8::Undefined();
409 }
410
411 CALLBACK_FUNC_DECL(XMLHttpRequestGetResponseHeader)
412 {
413     INC_STATS("DOM.XMLHttpRequest.getResponseHeader()");
414     if (args.Length() < 1)
415         return throwError("Not enough arguments", V8Proxy::SYNTAX_ERROR);
416
417     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
418     ExceptionCode ec = 0;
419     String header = toWebCoreString(args[0]);
420     String result = xmlHttpRequest->getResponseHeader(header, ec);
421     if (ec)
422         return throwError(ec);
423     return v8StringOrNull(result);
424 }
425
426 CALLBACK_FUNC_DECL(XMLHttpRequestOverrideMimeType)
427 {
428     INC_STATS("DOM.XMLHttpRequest.overrideMimeType()");
429     if (args.Length() < 1)
430         return throwError("Not enough arguments", V8Proxy::SYNTAX_ERROR);
431
432     XMLHttpRequest* xmlHttpRequest = V8Proxy::ToNativeObject<XMLHttpRequest>(V8ClassIndex::XMLHTTPREQUEST, args.Holder());
433     String value = toWebCoreString(args[0]);
434     xmlHttpRequest->overrideMimeType(value);
435     return v8::Undefined();
436 }
437
438 CALLBACK_FUNC_DECL(XMLHttpRequestDispatchEvent)
439 {
440     INC_STATS("DOM.XMLHttpRequest.dispatchEvent()");
441     return v8::Undefined();
442 }
443
444 } // namespace WebCore