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