1/*
2 * Copyright (C) 2008, 2009 Apple 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
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "JSXMLHttpRequest.h"
31
32#include "Blob.h"
33#include "DOMFormData.h"
34#include "DOMWindow.h"
35#include "Document.h"
36#include "Event.h"
37#include "Frame.h"
38#include "FrameLoader.h"
39#include "HTMLDocument.h"
40#include "InspectorInstrumentation.h"
41#include "JSBlob.h"
42#include "JSDOMFormData.h"
43#include "JSDOMWindowCustom.h"
44#include "JSDocument.h"
45#include "JSEvent.h"
46#include "JSEventListener.h"
47#include "XMLHttpRequest.h"
48#include <interpreter/StackVisitor.h>
49#include <runtime/ArrayBuffer.h>
50#include <runtime/Error.h>
51#include <runtime/JSArrayBuffer.h>
52#include <runtime/JSArrayBufferView.h>
53#include <runtime/JSONObject.h>
54
55using namespace JSC;
56
57namespace WebCore {
58
59void JSXMLHttpRequest::visitAdditionalChildren(SlotVisitor& visitor)
60{
61    if (XMLHttpRequestUpload* upload = impl().optionalUpload())
62        visitor.addOpaqueRoot(upload);
63
64    if (Document* responseDocument = impl().optionalResponseXML())
65        visitor.addOpaqueRoot(responseDocument);
66
67    if (ArrayBuffer* responseArrayBuffer = impl().optionalResponseArrayBuffer())
68        visitor.addOpaqueRoot(responseArrayBuffer);
69
70    if (Blob* responseBlob = impl().optionalResponseBlob())
71        visitor.addOpaqueRoot(responseBlob);
72
73    if (m_response)
74        visitor.append(&m_response);
75}
76
77// Custom functions
78JSValue JSXMLHttpRequest::open(ExecState* exec)
79{
80    if (exec->argumentCount() < 2)
81        return exec->vm().throwException(exec, createNotEnoughArgumentsError(exec));
82
83    const URL& url = impl().scriptExecutionContext()->completeURL(exec->uncheckedArgument(1).toString(exec)->value(exec));
84    String method = exec->uncheckedArgument(0).toString(exec)->value(exec);
85
86    ExceptionCode ec = 0;
87    if (exec->argumentCount() >= 3) {
88        bool async = exec->uncheckedArgument(2).toBoolean(exec);
89        if (!exec->argument(3).isUndefined()) {
90            String user = valueToStringWithNullCheck(exec, exec->uncheckedArgument(3));
91
92            if (!exec->argument(4).isUndefined()) {
93                String password = valueToStringWithNullCheck(exec, exec->uncheckedArgument(4));
94                impl().open(method, url, async, user, password, ec);
95            } else
96                impl().open(method, url, async, user, ec);
97        } else
98            impl().open(method, url, async, ec);
99    } else
100        impl().open(method, url, ec);
101
102    setDOMException(exec, ec);
103    return jsUndefined();
104}
105
106class SendFunctor {
107public:
108    SendFunctor()
109        : m_hasSkippedFirstFrame(false)
110        , m_line(0)
111        , m_column(0)
112    {
113    }
114
115    unsigned line() const { return m_line; }
116    unsigned column() const { return m_column; }
117    String url() const { return m_url; }
118
119    StackVisitor::Status operator()(StackVisitor& visitor)
120    {
121        if (!m_hasSkippedFirstFrame) {
122            m_hasSkippedFirstFrame = true;
123            return StackVisitor::Continue;
124        }
125
126        unsigned line = 0;
127        unsigned column = 0;
128        visitor->computeLineAndColumn(line, column);
129        m_line = line;
130        m_column = column;
131        m_url = visitor->sourceURL();
132        return StackVisitor::Done;
133    }
134
135private:
136    bool m_hasSkippedFirstFrame;
137    unsigned m_line;
138    unsigned m_column;
139    String m_url;
140};
141
142JSValue JSXMLHttpRequest::send(ExecState* exec)
143{
144    InspectorInstrumentation::willSendXMLHttpRequest(impl().scriptExecutionContext(), impl().url());
145
146    ExceptionCode ec = 0;
147    JSValue val = exec->argument(0);
148    if (val.isUndefinedOrNull())
149        impl().send(ec);
150    else if (val.inherits(JSDocument::info()))
151        impl().send(toDocument(val), ec);
152    else if (val.inherits(JSBlob::info()))
153        impl().send(toBlob(val), ec);
154    else if (val.inherits(JSDOMFormData::info()))
155        impl().send(toDOMFormData(val), ec);
156    else if (val.inherits(JSArrayBuffer::info()))
157        impl().send(toArrayBuffer(val), ec);
158    else if (val.inherits(JSArrayBufferView::info())) {
159        RefPtr<ArrayBufferView> view = toArrayBufferView(val);
160        impl().send(view.get(), ec);
161    } else
162        impl().send(val.toString(exec)->value(exec), ec);
163
164    SendFunctor functor;
165    exec->iterate(functor);
166    impl().setLastSendLineAndColumnNumber(functor.line(), functor.column());
167    impl().setLastSendURL(functor.url());
168    setDOMException(exec, ec);
169    return jsUndefined();
170}
171
172JSValue JSXMLHttpRequest::responseText(ExecState* exec) const
173{
174    ExceptionCode ec = 0;
175    String text = impl().responseText(ec);
176    if (ec) {
177        setDOMException(exec, ec);
178        return jsUndefined();
179    }
180    return jsOwnedStringOrNull(exec, text);
181}
182
183JSValue JSXMLHttpRequest::response(ExecState* exec) const
184{
185    // FIXME: Use CachedAttribute for other types than JSON as well.
186    if (m_response && impl().responseCacheIsValid())
187        return m_response.get();
188
189    if (!impl().doneWithoutErrors() && impl().responseTypeCode() > XMLHttpRequest::ResponseTypeText)
190        return jsNull();
191
192    switch (impl().responseTypeCode()) {
193    case XMLHttpRequest::ResponseTypeDefault:
194    case XMLHttpRequest::ResponseTypeText:
195        return responseText(exec);
196
197    case XMLHttpRequest::ResponseTypeJSON:
198        {
199            JSValue value = JSONParse(exec, impl().responseTextIgnoringResponseType());
200            if (!value)
201                value = jsNull();
202            JSXMLHttpRequest* jsRequest = const_cast<JSXMLHttpRequest*>(this);
203            jsRequest->m_response.set(exec->vm(), jsRequest, value);
204
205            impl().didCacheResponseJSON();
206
207            return value;
208        }
209
210    case XMLHttpRequest::ResponseTypeDocument:
211        {
212            ExceptionCode ec = 0;
213            Document* document = impl().responseXML(ec);
214            if (ec) {
215                setDOMException(exec, ec);
216                return jsUndefined();
217            }
218            return toJS(exec, globalObject(), document);
219        }
220
221    case XMLHttpRequest::ResponseTypeBlob:
222        return toJS(exec, globalObject(), impl().responseBlob());
223
224    case XMLHttpRequest::ResponseTypeArrayBuffer:
225        return toJS(exec, globalObject(), impl().responseArrayBuffer());
226    }
227
228    return jsUndefined();
229}
230
231} // namespace WebCore
232