| /* | |
| * Copyright 2015 Zhenbo Li | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdarg.h> | |
| #define COBJMACROS | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "ole2.h" | |
| #include "wine/debug.h" | |
| #include "mshtml_private.h" | |
| #include "htmlevent.h" | |
| #include "binding.h" | |
| #include "mshtmdid.h" | |
| #include "initguid.h" | |
| #include "msxml6.h" | |
| #include "objsafe.h" | |
| #include "shlwapi.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(mshtml); | |
| static HRESULT bstr_to_nsacstr(BSTR bstr, nsACString *str) | |
| { | |
| char *cstr = strdupWtoU(bstr); | |
| if(!cstr) | |
| return E_OUTOFMEMORY; | |
| nsACString_Init(str, cstr); | |
| free(cstr); | |
| return S_OK; | |
| } | |
| static HRESULT variant_to_nsastr(VARIANT var, nsAString *ret) | |
| { | |
| switch(V_VT(&var)) { | |
| case VT_NULL: | |
| case VT_ERROR: | |
| case VT_EMPTY: | |
| nsAString_Init(ret, NULL); | |
| return S_OK; | |
| case VT_BSTR: | |
| nsAString_InitDepend(ret, V_BSTR(&var)); | |
| return S_OK; | |
| default: | |
| FIXME("Unsupported VARIANT: %s\n", debugstr_variant(&var)); | |
| return E_INVALIDARG; | |
| } | |
| } | |
| static HRESULT return_nscstr(nsresult nsres, nsACString *nscstr, BSTR *p) | |
| { | |
| const char *str; | |
| int len; | |
| if(NS_FAILED(nsres)) { | |
| ERR("failed: %08lx\n", nsres); | |
| nsACString_Finish(nscstr); | |
| return E_FAIL; | |
| } | |
| nsACString_GetData(nscstr, &str); | |
| if(*str) { | |
| len = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); | |
| *p = SysAllocStringLen(NULL, len - 1); | |
| if(!*p) { | |
| nsACString_Finish(nscstr); | |
| return E_OUTOFMEMORY; | |
| } | |
| MultiByteToWideChar(CP_UTF8, 0, str, -1, *p, len); | |
| }else { | |
| *p = NULL; | |
| } | |
| nsACString_Finish(nscstr); | |
| return S_OK; | |
| } | |
| static const eventid_t events[] = { | |
| EVENTID_READYSTATECHANGE, | |
| EVENTID_LOAD, | |
| EVENTID_LOADSTART, | |
| EVENTID_LOADEND, | |
| EVENTID_PROGRESS, | |
| EVENTID_ABORT, | |
| EVENTID_ERROR, | |
| EVENTID_TIMEOUT, | |
| }; | |
| typedef enum { | |
| response_type_empty, | |
| response_type_text, | |
| response_type_doc, | |
| response_type_arraybuf, | |
| response_type_blob, | |
| response_type_stream | |
| } response_type_t; | |
| static const struct { | |
| const WCHAR *str; | |
| const WCHAR *nsxhr_str; | |
| } response_type_desc[] = { | |
| [response_type_empty] = { L"", L"" }, | |
| [response_type_text] = { L"text", L"" }, | |
| [response_type_doc] = { L"document", L"" }, /* FIXME */ | |
| [response_type_arraybuf] = { L"arraybuffer", L"arraybuffer" }, | |
| [response_type_blob] = { L"blob", L"arraybuffer" }, | |
| [response_type_stream] = { L"ms-stream", L"arraybuffer" } /* FIXME */ | |
| }; | |
| typedef struct { | |
| nsIDOMEventListener nsIDOMEventListener_iface; | |
| LONG ref; | |
| struct xhr *xhr; | |
| } XMLHttpReqEventListener; | |
| struct xhr { | |
| EventTarget event_target; | |
| LONG task_magic; | |
| LONG ready_state; | |
| response_type_t response_type; | |
| BOOLEAN synchronous; | |
| DWORD magic; | |
| DWORD pending_events_magic; | |
| IDispatch *response_obj; | |
| HTMLInnerWindow *window; | |
| nsIXMLHttpRequest *nsxhr; | |
| XMLHttpReqEventListener *event_listener; | |
| DOMEvent *pending_progress_event; | |
| }; | |
| struct HTMLXMLHttpRequest { | |
| struct xhr xhr; | |
| IHTMLXMLHttpRequest IHTMLXMLHttpRequest_iface; | |
| IHTMLXMLHttpRequest2 IHTMLXMLHttpRequest2_iface; | |
| IWineXMLHttpRequestPrivate IWineXMLHttpRequestPrivate_iface; | |
| IProvideClassInfo2 IProvideClassInfo2_iface; | |
| }; | |
| typedef struct { | |
| struct xhr xhr; | |
| IHTMLXDomainRequest IHTMLXDomainRequest_iface; | |
| } HTMLXDomainRequest; | |
| static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener) | |
| { | |
| nsIDOMEventTarget *event_target; | |
| nsresult nsres; | |
| nsAString str; | |
| unsigned i; | |
| nsres = nsIXMLHttpRequest_QueryInterface(event_listener->xhr->nsxhr, &IID_nsIDOMEventTarget, (void**)&event_target); | |
| assert(nsres == NS_OK); | |
| for(i = 0; i < ARRAY_SIZE(events) ; i++) { | |
| nsAString_InitDepend(&str, get_event_name(events[i])); | |
| nsres = nsIDOMEventTarget_RemoveEventListener(event_target, &str, &event_listener->nsIDOMEventListener_iface, FALSE); | |
| nsAString_Finish(&str); | |
| assert(nsres == NS_OK); | |
| } | |
| nsIDOMEventTarget_Release(event_target); | |
| event_listener->xhr = NULL; | |
| nsIDOMEventListener_Release(&event_listener->nsIDOMEventListener_iface); | |
| } | |
| static void synthesize_pending_events(struct xhr *xhr) | |
| { | |
| DWORD magic = xhr->pending_events_magic; | |
| UINT16 ready_state = xhr->ready_state; | |
| BOOLEAN send_load, send_loadend; | |
| DOMEvent *event; | |
| HRESULT hres; | |
| if(xhr->magic != magic) | |
| return; | |
| /* Make sure further events are synthesized with a new task */ | |
| xhr->pending_events_magic = magic - 1; | |
| /* Synthesize the necessary events that led us to this current state */ | |
| nsIXMLHttpRequest_GetReadyState(xhr->nsxhr, &ready_state); | |
| if(ready_state == READYSTATE_UNINITIALIZED) | |
| return; | |
| /* Synchronous XHRs only send readyState changes before DONE in IE9 and below */ | |
| if(xhr->synchronous && dispex_compat_mode(&xhr->event_target.dispex) > COMPAT_MODE_IE9) { | |
| if(ready_state < READYSTATE_INTERACTIVE) { | |
| xhr->ready_state = ready_state; | |
| return; | |
| } | |
| xhr->ready_state = max(xhr->ready_state, READYSTATE_INTERACTIVE); | |
| } | |
| IWineJSDispatchHost_AddRef(&xhr->event_target.dispex.IWineJSDispatchHost_iface); | |
| send_loadend = send_load = (xhr->ready_state != ready_state && ready_state == READYSTATE_COMPLETE); | |
| for(;;) { | |
| if(xhr->pending_progress_event && | |
| xhr->ready_state == (xhr->pending_progress_event->event_id == EVENTID_PROGRESS ? READYSTATE_INTERACTIVE : READYSTATE_COMPLETE)) | |
| { | |
| DOMEvent *pending_progress_event = xhr->pending_progress_event; | |
| xhr->pending_progress_event = NULL; | |
| if(pending_progress_event->event_id != EVENTID_PROGRESS) { | |
| send_load = FALSE; | |
| send_loadend = TRUE; | |
| } | |
| dispatch_event(&xhr->event_target, pending_progress_event); | |
| IDOMEvent_Release(&pending_progress_event->IDOMEvent_iface); | |
| if(xhr->magic != magic) | |
| goto ret; | |
| } | |
| if(xhr->ready_state >= ready_state) | |
| break; | |
| xhr->ready_state++; | |
| hres = create_document_event(xhr->window->doc, EVENTID_READYSTATECHANGE, &event); | |
| if(SUCCEEDED(hres)) { | |
| dispatch_event(&xhr->event_target, event); | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| if(xhr->magic != magic) | |
| goto ret; | |
| } | |
| } | |
| if(send_load) { | |
| hres = create_document_event(xhr->window->doc, EVENTID_LOAD, &event); | |
| if(SUCCEEDED(hres)) { | |
| dispatch_event(&xhr->event_target, event); | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| if(xhr->magic != magic) | |
| goto ret; | |
| } | |
| } | |
| if(send_loadend) { | |
| hres = create_document_event(xhr->window->doc, EVENTID_LOADEND, &event); | |
| if(SUCCEEDED(hres)) { | |
| dispatch_event(&xhr->event_target, event); | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| if(xhr->magic != magic) | |
| goto ret; | |
| } | |
| } | |
| ret: | |
| IWineJSDispatchHost_Release(&xhr->event_target.dispex.IWineJSDispatchHost_iface); | |
| } | |
| static nsresult sync_xhr_send(struct xhr *xhr, nsIVariant *nsbody) | |
| { | |
| thread_data_t *thread_data = get_thread_data(TRUE); | |
| HTMLInnerWindow *window = xhr->window; | |
| struct xhr *prev_blocking_xhr; | |
| unsigned prev_tasks_locked; | |
| nsresult nsres; | |
| if(!thread_data) | |
| return NS_ERROR_OUT_OF_MEMORY; | |
| prev_tasks_locked = thread_data->tasks_locked; | |
| prev_blocking_xhr = thread_data->blocking_xhr; | |
| /* Note: Starting with Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), | |
| * synchronous requests on the main thread have been deprecated due to the negative | |
| * effects to the user experience. However, they still work. The larger issue is that | |
| * it is broken because it still dispatches async XHR and some other events, while all | |
| * other major browsers don't, including IE, so we have to filter them out during Send. | |
| * | |
| * They will need to be queued and dispatched later, after Send returns, otherwise it | |
| * breaks JavaScript single-threaded expectations (JS code will switch from blocking in | |
| * Send to executing some event handler, then returning back to Send, messing its state). | |
| * | |
| * Of course we can't just delay dispatching the events, because the state won't match | |
| * for each event later on, to what it's supposed to be (most notably, XHR's readyState). | |
| * We'll keep snapshots and synthesize them when unblocked for async XHR events. | |
| * | |
| * Note that while queuing an event this way would not work correctly with their default | |
| * behavior in Gecko (preventDefault() can't be called because we need to *delay* the | |
| * default, rather than prevent it completely), Gecko does suppress events reaching the | |
| * document during the sync XHR event loop, so those we do not handle manually. If we | |
| * find an event that has defaults on Gecko's side and isn't delayed by Gecko, we need | |
| * to figure out a way to handle it... | |
| * | |
| * For details (and bunch of problems to consider) see: https://bugzil.la/697151 | |
| * | |
| * FIXME: Since Gecko uses a message loop to implement sync XHR, and it requires that | |
| * all the async tasks are executed (or else it hangs indefinitely waiting for them), | |
| * we have to enable processing of all tasks, even if we're coming from a nested loop | |
| * that wouldn't otherwise process tasks. This isn't correct but it's niche enough. | |
| */ | |
| if(thread_data->tasks_locked) { | |
| thread_data->tasks_locked = 0; | |
| unblock_tasks_and_timers(thread_data); | |
| } | |
| window->base.outer_window->readystate_locked++; | |
| window->blocking_depth++; | |
| thread_data->blocking_xhr = xhr; | |
| nsres = nsIXMLHttpRequest_Send(xhr->nsxhr, nsbody); | |
| thread_data->blocking_xhr = prev_blocking_xhr; | |
| thread_data->tasks_locked = prev_tasks_locked; | |
| window->base.outer_window->readystate_locked--; | |
| if(!--window->blocking_depth && !thread_data->tasks_locked) | |
| unblock_tasks_and_timers(thread_data); | |
| /* Process any pending events now since they were part of the blocked send() above */ | |
| synthesize_pending_events(xhr); | |
| return nsres; | |
| } | |
| struct pending_xhr_events_task { | |
| event_task_t header; | |
| struct xhr *xhr; | |
| }; | |
| static void pending_xhr_events_proc(event_task_t *_task) | |
| { | |
| struct pending_xhr_events_task *task = (struct pending_xhr_events_task*)_task; | |
| synthesize_pending_events(task->xhr); | |
| } | |
| static void pending_xhr_events_destr(event_task_t *_task) | |
| { | |
| } | |
| static inline XMLHttpReqEventListener *impl_from_nsIDOMEventListener(nsIDOMEventListener *iface) | |
| { | |
| return CONTAINING_RECORD(iface, XMLHttpReqEventListener, nsIDOMEventListener_iface); | |
| } | |
| static nsresult NSAPI XMLHttpReqEventListener_QueryInterface(nsIDOMEventListener *iface, | |
| nsIIDRef riid, void **result) | |
| { | |
| XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); | |
| if(IsEqualGUID(&IID_nsISupports, riid)) { | |
| TRACE("(%p)->(IID_nsISupports, %p)\n", This, result); | |
| *result = &This->nsIDOMEventListener_iface; | |
| }else if(IsEqualGUID(&IID_nsIDOMEventListener, riid)) { | |
| TRACE("(%p)->(IID_nsIDOMEventListener %p)\n", This, result); | |
| *result = &This->nsIDOMEventListener_iface; | |
| }else { | |
| *result = NULL; | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), result); | |
| return NS_NOINTERFACE; | |
| } | |
| nsIDOMEventListener_AddRef(&This->nsIDOMEventListener_iface); | |
| return NS_OK; | |
| } | |
| static nsrefcnt NSAPI XMLHttpReqEventListener_AddRef(nsIDOMEventListener *iface) | |
| { | |
| XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); | |
| LONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p) ref=%ld\n", This, ref); | |
| return ref; | |
| } | |
| static nsrefcnt NSAPI XMLHttpReqEventListener_Release(nsIDOMEventListener *iface) | |
| { | |
| XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); | |
| LONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p) ref=%ld\n", This, ref); | |
| if(!ref) { | |
| assert(!This->xhr); | |
| free(This); | |
| } | |
| return ref; | |
| } | |
| static nsresult NSAPI XMLHttpReqEventListener_HandleEvent(nsIDOMEventListener *iface, nsIDOMEvent *nsevent) | |
| { | |
| XMLHttpReqEventListener *This = impl_from_nsIDOMEventListener(iface); | |
| struct xhr *blocking_xhr = NULL; | |
| thread_data_t *thread_data; | |
| compat_mode_t compat_mode; | |
| LONG ready_state; | |
| DOMEvent *event; | |
| HRESULT hres; | |
| UINT16 val; | |
| TRACE("(%p)\n", This); | |
| if(!This->xhr) | |
| return NS_OK; | |
| ready_state = This->xhr->ready_state; | |
| if(NS_SUCCEEDED(nsIXMLHttpRequest_GetReadyState(This->xhr->nsxhr, &val))) | |
| ready_state = val; | |
| if((thread_data = get_thread_data(FALSE))) | |
| blocking_xhr = thread_data->blocking_xhr; | |
| compat_mode = dispex_compat_mode(&This->xhr->event_target.dispex); | |
| hres = create_event_from_nsevent(nsevent, This->xhr->window, compat_mode, &event); | |
| if(FAILED(hres)) { | |
| if(!blocking_xhr || This->xhr == blocking_xhr) | |
| This->xhr->ready_state = ready_state; | |
| return NS_ERROR_OUT_OF_MEMORY; | |
| } | |
| if(blocking_xhr) { | |
| BOOL has_pending_events = (This->xhr->magic == This->xhr->pending_events_magic); | |
| if(has_pending_events || This->xhr != blocking_xhr) { | |
| switch(event->event_id) { | |
| case EVENTID_PROGRESS: | |
| case EVENTID_ABORT: | |
| case EVENTID_ERROR: | |
| case EVENTID_TIMEOUT: | |
| if(This->xhr->pending_progress_event) | |
| IDOMEvent_Release(&This->xhr->pending_progress_event->IDOMEvent_iface); | |
| This->xhr->pending_progress_event = event; | |
| break; | |
| default: | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| break; | |
| } | |
| if(!has_pending_events) { | |
| if(!This->xhr->synchronous) { | |
| struct pending_xhr_events_task *task; | |
| remove_target_tasks(This->xhr->task_magic); | |
| if(!(task = malloc(sizeof(*task)))) | |
| return NS_ERROR_OUT_OF_MEMORY; | |
| task->header.target_magic = This->xhr->task_magic; | |
| task->header.thread_blocked = TRUE; | |
| task->header.proc = pending_xhr_events_proc; | |
| task->header.destr = pending_xhr_events_destr; | |
| task->header.window = This->xhr->window; | |
| task->xhr = This->xhr; | |
| IHTMLWindow2_AddRef(&This->xhr->window->base.IHTMLWindow2_iface); | |
| list_add_after(thread_data->pending_xhr_events_tail, &task->header.entry); | |
| thread_data->pending_xhr_events_tail = &task->header.entry; | |
| } | |
| This->xhr->pending_events_magic = This->xhr->magic; | |
| return NS_OK; | |
| } | |
| /* Synthesize pending events that a nested sync XHR might have blocked us on */ | |
| if(This->xhr == blocking_xhr) | |
| synthesize_pending_events(This->xhr); | |
| return NS_OK; | |
| } | |
| /* Workaround weird Gecko behavior with nested sync XHRs, where it sends readyState changes | |
| for OPENED (or possibly other states than DONE), unlike IE10+ and non-nested sync XHRs... */ | |
| if(ready_state < READYSTATE_COMPLETE && event->event_id == EVENTID_READYSTATECHANGE) { | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| This->xhr->ready_state = ready_state; | |
| return NS_OK; | |
| } | |
| /* IE10+ only send readystatechange event when it is DONE for sync XHRs, but older modes | |
| send all the others here, including OPENED state change (even if it was opened earlier). */ | |
| if(compat_mode < COMPAT_MODE_IE10 && This->xhr->ready_state < READYSTATE_COMPLETE && ( | |
| event->event_id == EVENTID_READYSTATECHANGE || event->event_id == EVENTID_PROGRESS || event->event_id == EVENTID_LOADSTART)) { | |
| DOMEvent *readystatechange_event; | |
| DWORD magic = This->xhr->magic; | |
| unsigned i; | |
| for(i = READYSTATE_LOADING; i < READYSTATE_COMPLETE; i++) { | |
| hres = create_document_event(This->xhr->window->doc, EVENTID_READYSTATECHANGE, &readystatechange_event); | |
| if(FAILED(hres)) | |
| break; | |
| This->xhr->ready_state = i; | |
| dispatch_event(&This->xhr->event_target, readystatechange_event); | |
| IDOMEvent_Release(&readystatechange_event->IDOMEvent_iface); | |
| if(This->xhr->magic != magic) { | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| return NS_OK; | |
| } | |
| } | |
| } | |
| } | |
| This->xhr->ready_state = ready_state; | |
| dispatch_event(&This->xhr->event_target, event); | |
| IDOMEvent_Release(&event->IDOMEvent_iface); | |
| return NS_OK; | |
| } | |
| static const nsIDOMEventListenerVtbl XMLHttpReqEventListenerVtbl = { | |
| XMLHttpReqEventListener_QueryInterface, | |
| XMLHttpReqEventListener_AddRef, | |
| XMLHttpReqEventListener_Release, | |
| XMLHttpReqEventListener_HandleEvent | |
| }; | |
| static inline HTMLXMLHttpRequest *impl_from_IHTMLXMLHttpRequest(IHTMLXMLHttpRequest *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXMLHttpRequest, IHTMLXMLHttpRequest_iface); | |
| } | |
| DISPEX_IDISPATCH_IMPL(HTMLXMLHttpRequest, IHTMLXMLHttpRequest, | |
| impl_from_IHTMLXMLHttpRequest(iface)->xhr.event_target.dispex) | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_readyState(IHTMLXMLHttpRequest *iface, LONG *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_POINTER; | |
| *p = This->xhr.ready_state; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_responseBody(IHTMLXMLHttpRequest *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| FIXME("(%p)->(%p)\n", This, p); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI get_response_text(struct xhr *xhr, BSTR *p) | |
| { | |
| nsAString nsstr; | |
| nsresult nsres; | |
| if(!p) | |
| return E_POINTER; | |
| if(xhr->ready_state < READYSTATE_INTERACTIVE) { | |
| *p = NULL; | |
| return S_OK; | |
| } | |
| nsAString_Init(&nsstr, NULL); | |
| nsres = nsIXMLHttpRequest_GetResponseText(xhr->nsxhr, &nsstr); | |
| return return_nsstr(nsres, &nsstr, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_responseText(IHTMLXMLHttpRequest *iface, BSTR *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_response_text(&This->xhr, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_responseXML(IHTMLXMLHttpRequest *iface, IDispatch **p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| IXMLDOMDocument *xmldoc = NULL; | |
| BSTR str; | |
| HRESULT hres; | |
| VARIANT_BOOL vbool; | |
| IObjectSafety *safety; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(This->xhr.ready_state < READYSTATE_COMPLETE) { | |
| *p = NULL; | |
| return S_OK; | |
| } | |
| if(dispex_compat_mode(&This->xhr.event_target.dispex) >= COMPAT_MODE_IE10) { | |
| HTMLDocumentNode *doc; | |
| nsIDOMDocument *nsdoc; | |
| nsresult nsres; | |
| nsres = nsIXMLHttpRequest_GetResponseXML(This->xhr.nsxhr, &nsdoc); | |
| if(NS_FAILED(nsres)) | |
| return map_nsresult(nsres); | |
| if(!nsdoc) { | |
| *p = NULL; | |
| return S_OK; | |
| } | |
| if(!This->xhr.window->base.outer_window || !This->xhr.window->base.outer_window->browser) | |
| hres = E_UNEXPECTED; | |
| else | |
| hres = create_document_node(nsdoc, This->xhr.window->base.outer_window->browser, NULL, This->xhr.window, | |
| dispex_compat_mode(&This->xhr.window->event_target.dispex), &doc); | |
| nsIDOMDocument_Release(nsdoc); | |
| if(FAILED(hres)) | |
| return hres; | |
| *p = (IDispatch*)&doc->IHTMLDocument2_iface; | |
| return S_OK; | |
| } | |
| hres = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&xmldoc); | |
| if(FAILED(hres)) { | |
| ERR("CoCreateInstance failed: %08lx\n", hres); | |
| return hres; | |
| } | |
| hres = IHTMLXMLHttpRequest_get_responseText(iface, &str); | |
| if(FAILED(hres)) { | |
| IXMLDOMDocument_Release(xmldoc); | |
| ERR("get_responseText failed: %08lx\n", hres); | |
| return hres; | |
| } | |
| hres = IXMLDOMDocument_loadXML(xmldoc, str, &vbool); | |
| SysFreeString(str); | |
| if(hres != S_OK || vbool != VARIANT_TRUE) | |
| WARN("loadXML failed: %08lx, returning an empty xmldoc\n", hres); | |
| hres = IXMLDOMDocument_QueryInterface(xmldoc, &IID_IObjectSafety, (void**)&safety); | |
| assert(SUCCEEDED(hres)); | |
| hres = IObjectSafety_SetInterfaceSafetyOptions(safety, NULL, | |
| INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA | INTERFACE_USES_SECURITY_MANAGER, | |
| INTERFACESAFE_FOR_UNTRUSTED_CALLER | INTERFACESAFE_FOR_UNTRUSTED_DATA | INTERFACE_USES_SECURITY_MANAGER); | |
| assert(SUCCEEDED(hres)); | |
| IObjectSafety_Release(safety); | |
| *p = (IDispatch*)xmldoc; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_status(IHTMLXMLHttpRequest *iface, LONG *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| UINT32 val; | |
| nsresult nsres; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_POINTER; | |
| if(This->xhr.ready_state < READYSTATE_LOADED) { | |
| *p = 0; | |
| return E_FAIL; | |
| } | |
| nsres = nsIXMLHttpRequest_GetStatus(This->xhr.nsxhr, &val); | |
| if(NS_FAILED(nsres)) { | |
| ERR("nsIXMLHttpRequest_GetStatus failed: %08lx\n", nsres); | |
| return E_FAIL; | |
| } | |
| *p = val; | |
| if(val == 0) | |
| return E_FAIL; /* WinAPI thinks this is an error */ | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_statusText(IHTMLXMLHttpRequest *iface, BSTR *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| nsACString nscstr; | |
| nsresult nsres; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_POINTER; | |
| if(This->xhr.ready_state < READYSTATE_LOADED) { | |
| *p = NULL; | |
| return E_FAIL; | |
| } | |
| nsACString_Init(&nscstr, NULL); | |
| nsres = nsIXMLHttpRequest_GetStatusText(This->xhr.nsxhr, &nscstr); | |
| return return_nscstr(nsres, &nscstr, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_put_onreadystatechange(IHTMLXMLHttpRequest *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_READYSTATECHANGE, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_get_onreadystatechange(IHTMLXMLHttpRequest *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_READYSTATECHANGE, p); | |
| } | |
| static HRESULT WINAPI xhr_abort(struct xhr *xhr) | |
| { | |
| DWORD prev_magic = xhr->magic; | |
| UINT16 ready_state; | |
| nsresult nsres; | |
| xhr->magic++; | |
| nsres = nsIXMLHttpRequest_SlowAbort(xhr->nsxhr); | |
| if(NS_FAILED(nsres)) { | |
| ERR("nsIXMLHttpRequest_SlowAbort failed: %08lx\n", nsres); | |
| xhr->magic = prev_magic; | |
| return E_FAIL; | |
| } | |
| /* Gecko changed to READYSTATE_UNINITIALIZED if it did abort */ | |
| nsres = nsIXMLHttpRequest_GetReadyState(xhr->nsxhr, &ready_state); | |
| if(NS_SUCCEEDED(nsres)) | |
| xhr->ready_state = ready_state; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_abort(IHTMLXMLHttpRequest *iface) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| TRACE("(%p)->()\n", This); | |
| return xhr_abort(&This->xhr); | |
| } | |
| static HRESULT WINAPI xhr_open(struct xhr *xhr, BSTR bstrMethod, BSTR bstrUrl, BOOL async, nsAString *user, nsAString *password, unsigned opt_argc) | |
| { | |
| BOOLEAN prev_synchronous; | |
| nsACString method, url; | |
| DWORD prev_magic; | |
| nsresult nsres; | |
| HRESULT hres; | |
| hres = bstr_to_nsacstr(bstrMethod, &method); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = bstr_to_nsacstr(bstrUrl, &url); | |
| if(FAILED(hres)) { | |
| nsACString_Finish(&method); | |
| return hres; | |
| } | |
| /* Set this here, Gecko dispatches nested sync XHR readyState changes for OPENED (see HandleEvent) */ | |
| prev_magic = xhr->magic; | |
| prev_synchronous = xhr->synchronous; | |
| xhr->synchronous = !async; | |
| xhr->magic++; | |
| nsres = nsIXMLHttpRequest_Open(xhr->nsxhr, &method, &url, async, user, password, opt_argc); | |
| nsACString_Finish(&method); | |
| nsACString_Finish(&url); | |
| if(NS_FAILED(nsres)) { | |
| ERR("nsIXMLHttpRequest_Open failed: %08lx\n", nsres); | |
| xhr->magic = prev_magic; | |
| xhr->synchronous = prev_synchronous; | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT HTMLXMLHttpRequest_open_hook(DispatchEx *dispex, WORD flags, | |
| DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) | |
| { | |
| /* If only two arguments were given, implicitly set async to false */ | |
| if((flags & DISPATCH_METHOD) && dp->cArgs == 2 && !dp->cNamedArgs) { | |
| VARIANT args[5]; | |
| DISPPARAMS new_dp = {args, NULL, ARRAY_SIZE(args), 0}; | |
| V_VT(args) = VT_EMPTY; | |
| V_VT(args+1) = VT_EMPTY; | |
| V_VT(args+2) = VT_BOOL; | |
| V_BOOL(args+2) = VARIANT_TRUE; | |
| args[3] = dp->rgvarg[0]; | |
| args[4] = dp->rgvarg[1]; | |
| TRACE("implicit async\n"); | |
| return dispex_call_builtin(dispex, DISPID_IHTMLXMLHTTPREQUEST_OPEN, &new_dp, res, ei, caller); | |
| } | |
| return S_FALSE; /* fallback to default */ | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR bstrMethod, BSTR bstrUrl, VARIANT varAsync, VARIANT varUser, VARIANT varPassword) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| nsAString user, password; | |
| unsigned opt_argc = 1; | |
| HRESULT hres; | |
| TRACE("(%p)->(%s %s %s %s %s)\n", This, debugstr_w(bstrMethod), debugstr_w(bstrUrl), debugstr_variant(&varAsync), debugstr_variant(&varUser), debugstr_variant(&varPassword)); | |
| if(V_VT(&varAsync) != VT_BOOL) { | |
| LCID lcid = MAKELCID(MAKELANGID(LANG_ENGLISH,SUBLANG_ENGLISH_US),SORT_DEFAULT); | |
| hres = VariantChangeTypeEx(&varAsync, &varAsync, lcid, 0, VT_BOOL); | |
| if(FAILED(hres)) { | |
| WARN("Failed to convert varAsync to BOOL: %#lx\n", hres); | |
| return hres; | |
| } | |
| } | |
| hres = variant_to_nsastr(varUser, &user); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = variant_to_nsastr(varPassword, &password); | |
| if(FAILED(hres)) { | |
| nsAString_Finish(&user); | |
| return hres; | |
| } | |
| if(V_VT(&varPassword) != VT_EMPTY && V_VT(&varPassword) != VT_ERROR) | |
| opt_argc += 2; | |
| else if(V_VT(&varUser) != VT_EMPTY && V_VT(&varUser) != VT_ERROR) | |
| opt_argc += 1; | |
| hres = xhr_open(&This->xhr, bstrMethod, bstrUrl, !!V_BOOL(&varAsync), &user, &password, opt_argc); | |
| nsAString_Finish(&user); | |
| nsAString_Finish(&password); | |
| return hres; | |
| } | |
| static HRESULT WINAPI xhr_send(struct xhr *xhr, VARIANT *varBody) | |
| { | |
| nsIWritableVariant *nsbody = NULL; | |
| nsresult nsres = NS_OK; | |
| switch(V_VT(varBody)) { | |
| case VT_NULL: | |
| case VT_EMPTY: | |
| case VT_ERROR: | |
| break; | |
| case VT_BSTR: { | |
| nsAString nsstr; | |
| nsbody = create_nsvariant(); | |
| if(!nsbody) | |
| return E_OUTOFMEMORY; | |
| nsAString_InitDepend(&nsstr, V_BSTR(varBody)); | |
| nsres = nsIWritableVariant_SetAsAString(nsbody, &nsstr); | |
| nsAString_Finish(&nsstr); | |
| break; | |
| } | |
| default: | |
| FIXME("unsupported body type %s\n", debugstr_variant(varBody)); | |
| return E_NOTIMPL; | |
| } | |
| if(NS_SUCCEEDED(nsres)) { | |
| if(xhr->synchronous) | |
| nsres = sync_xhr_send(xhr, (nsIVariant*)nsbody); | |
| else | |
| nsres = nsIXMLHttpRequest_Send(xhr->nsxhr, (nsIVariant*)nsbody); | |
| } | |
| if(nsbody) | |
| nsIWritableVariant_Release(nsbody); | |
| if(NS_FAILED(nsres)) { | |
| ERR("nsIXMLHttpRequest_Send failed: %08lx\n", nsres); | |
| return map_nsresult(nsres); | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_send(IHTMLXMLHttpRequest *iface, VARIANT varBody) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&varBody)); | |
| return xhr_send(&This->xhr, &varBody); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_getAllResponseHeaders(IHTMLXMLHttpRequest *iface, BSTR *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| nsACString nscstr; | |
| nsresult nsres; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_POINTER; | |
| if(This->xhr.ready_state < READYSTATE_LOADED) { | |
| *p = NULL; | |
| return E_FAIL; | |
| } | |
| nsACString_Init(&nscstr, NULL); | |
| nsres = nsIXMLHttpRequest_GetAllResponseHeaders(This->xhr.nsxhr, &nscstr); | |
| return return_nscstr(nsres, &nscstr, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_getResponseHeader(IHTMLXMLHttpRequest *iface, BSTR bstrHeader, BSTR *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| nsACString header, ret; | |
| char *cstr; | |
| nsresult nsres; | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_w(bstrHeader), p); | |
| if(!p) | |
| return E_POINTER; | |
| if(!bstrHeader) | |
| return E_INVALIDARG; | |
| if(This->xhr.ready_state < READYSTATE_LOADED) { | |
| *p = NULL; | |
| return E_FAIL; | |
| } | |
| cstr = strdupWtoU(bstrHeader); | |
| nsACString_InitDepend(&header, cstr); | |
| nsACString_Init(&ret, NULL); | |
| nsres = nsIXMLHttpRequest_GetResponseHeader(This->xhr.nsxhr, &header, &ret); | |
| nsACString_Finish(&header); | |
| free(cstr); | |
| return return_nscstr(nsres, &ret, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_setRequestHeader(IHTMLXMLHttpRequest *iface, BSTR bstrHeader, BSTR bstrValue) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest(iface); | |
| char *header_u, *value_u; | |
| nsACString header, value; | |
| nsresult nsres; | |
| TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrHeader), debugstr_w(bstrValue)); | |
| header_u = strdupWtoU(bstrHeader); | |
| if(bstrHeader && !header_u) | |
| return E_OUTOFMEMORY; | |
| value_u = strdupWtoU(bstrValue); | |
| if(bstrValue && !value_u) { | |
| free(header_u); | |
| return E_OUTOFMEMORY; | |
| } | |
| nsACString_InitDepend(&header, header_u); | |
| nsACString_InitDepend(&value, value_u); | |
| nsres = nsIXMLHttpRequest_SetRequestHeader(This->xhr.nsxhr, &header, &value); | |
| nsACString_Finish(&header); | |
| nsACString_Finish(&value); | |
| free(header_u); | |
| free(value_u); | |
| if(NS_FAILED(nsres)) { | |
| ERR("SetRequestHeader failed: %08lx\n", nsres); | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| static const IHTMLXMLHttpRequestVtbl HTMLXMLHttpRequestVtbl = { | |
| HTMLXMLHttpRequest_QueryInterface, | |
| HTMLXMLHttpRequest_AddRef, | |
| HTMLXMLHttpRequest_Release, | |
| HTMLXMLHttpRequest_GetTypeInfoCount, | |
| HTMLXMLHttpRequest_GetTypeInfo, | |
| HTMLXMLHttpRequest_GetIDsOfNames, | |
| HTMLXMLHttpRequest_Invoke, | |
| HTMLXMLHttpRequest_get_readyState, | |
| HTMLXMLHttpRequest_get_responseBody, | |
| HTMLXMLHttpRequest_get_responseText, | |
| HTMLXMLHttpRequest_get_responseXML, | |
| HTMLXMLHttpRequest_get_status, | |
| HTMLXMLHttpRequest_get_statusText, | |
| HTMLXMLHttpRequest_put_onreadystatechange, | |
| HTMLXMLHttpRequest_get_onreadystatechange, | |
| HTMLXMLHttpRequest_abort, | |
| HTMLXMLHttpRequest_open, | |
| HTMLXMLHttpRequest_send, | |
| HTMLXMLHttpRequest_getAllResponseHeaders, | |
| HTMLXMLHttpRequest_getResponseHeader, | |
| HTMLXMLHttpRequest_setRequestHeader | |
| }; | |
| static inline HTMLXMLHttpRequest *impl_from_IHTMLXMLHttpRequest2(IHTMLXMLHttpRequest2 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXMLHttpRequest, IHTMLXMLHttpRequest2_iface); | |
| } | |
| DISPEX_IDISPATCH_IMPL(HTMLXMLHttpRequest2, IHTMLXMLHttpRequest2, | |
| impl_from_IHTMLXMLHttpRequest2(iface)->xhr.event_target.dispex) | |
| static HRESULT WINAPI HTMLXMLHttpRequest2_put_timeout(IHTMLXMLHttpRequest2 *iface, LONG v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest2(iface); | |
| TRACE("(%p)->(%ld)\n", This, v); | |
| if(v < 0) | |
| return E_INVALIDARG; | |
| return map_nsresult(nsIXMLHttpRequest_SetTimeout(This->xhr.nsxhr, v)); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest2_get_timeout(IHTMLXMLHttpRequest2 *iface, LONG *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest2(iface); | |
| nsresult nsres; | |
| UINT32 timeout; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_POINTER; | |
| nsres = nsIXMLHttpRequest_GetTimeout(This->xhr.nsxhr, &timeout); | |
| *p = timeout; | |
| return map_nsresult(nsres); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest2_put_ontimeout(IHTMLXMLHttpRequest2 *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest2(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_TIMEOUT, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest2_get_ontimeout(IHTMLXMLHttpRequest2 *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IHTMLXMLHttpRequest2(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_TIMEOUT, p); | |
| } | |
| static const IHTMLXMLHttpRequest2Vtbl HTMLXMLHttpRequest2Vtbl = { | |
| HTMLXMLHttpRequest2_QueryInterface, | |
| HTMLXMLHttpRequest2_AddRef, | |
| HTMLXMLHttpRequest2_Release, | |
| HTMLXMLHttpRequest2_GetTypeInfoCount, | |
| HTMLXMLHttpRequest2_GetTypeInfo, | |
| HTMLXMLHttpRequest2_GetIDsOfNames, | |
| HTMLXMLHttpRequest2_Invoke, | |
| HTMLXMLHttpRequest2_put_timeout, | |
| HTMLXMLHttpRequest2_get_timeout, | |
| HTMLXMLHttpRequest2_put_ontimeout, | |
| HTMLXMLHttpRequest2_get_ontimeout | |
| }; | |
| static inline HTMLXMLHttpRequest *impl_from_IWineXMLHttpRequestPrivate(IWineXMLHttpRequestPrivate *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXMLHttpRequest, IWineXMLHttpRequestPrivate_iface); | |
| } | |
| DISPEX_IDISPATCH_IMPL(HTMLXMLHttpRequest_private, IWineXMLHttpRequestPrivate, | |
| impl_from_IWineXMLHttpRequestPrivate(iface)->xhr.event_target.dispex) | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_response(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| HRESULT hres = S_OK; | |
| UINT32 buf_size; | |
| nsresult nsres; | |
| void *buf; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(This->xhr.response_obj) { | |
| V_VT(p) = VT_DISPATCH; | |
| V_DISPATCH(p) = This->xhr.response_obj; | |
| IDispatch_AddRef(This->xhr.response_obj); | |
| return S_OK; | |
| } | |
| switch(This->xhr.response_type) { | |
| case response_type_empty: | |
| case response_type_text: | |
| hres = IHTMLXMLHttpRequest_get_responseText(&This->IHTMLXMLHttpRequest_iface, &V_BSTR(p)); | |
| if(SUCCEEDED(hres)) | |
| V_VT(p) = VT_BSTR; | |
| break; | |
| case response_type_doc: | |
| FIXME("response_type_doc\n"); | |
| return E_NOTIMPL; | |
| case response_type_arraybuf: | |
| case response_type_blob: | |
| if(This->xhr.ready_state < READYSTATE_COMPLETE) { | |
| V_VT(p) = VT_EMPTY; | |
| break; | |
| } | |
| nsres = nsIXMLHttpRequest_GetResponseBuffer(This->xhr.nsxhr, NULL, 0, &buf_size); | |
| assert(nsres == NS_OK); | |
| if(This->xhr.response_type == response_type_arraybuf) { | |
| hres = IWineJScript_CreateArrayBuffer(This->xhr.window->jscript, buf_size, (IWineJSDispatch**)&This->xhr.response_obj, &buf); | |
| if(SUCCEEDED(hres)) { | |
| nsres = nsIXMLHttpRequest_GetResponseBuffer(This->xhr.nsxhr, buf, buf_size, &buf_size); | |
| assert(nsres == NS_OK); | |
| } | |
| break; | |
| } | |
| FIXME("response_type_blob\n"); | |
| return E_NOTIMPL; | |
| case response_type_stream: | |
| FIXME("response_type_stream\n"); | |
| return E_NOTIMPL; | |
| default: | |
| assert(0); | |
| } | |
| if(SUCCEEDED(hres) && This->xhr.response_obj) { | |
| V_VT(p) = VT_DISPATCH; | |
| V_DISPATCH(p) = This->xhr.response_obj; | |
| IDispatch_AddRef(This->xhr.response_obj); | |
| } | |
| return hres; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_responseType(IWineXMLHttpRequestPrivate *iface, BSTR v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| nsAString nsstr; | |
| nsresult nsres; | |
| unsigned i; | |
| TRACE("(%p)->(%s)\n", This, debugstr_w(v)); | |
| if(This->xhr.ready_state < READYSTATE_LOADING || This->xhr.ready_state > READYSTATE_INTERACTIVE) { | |
| /* FIXME: Return InvalidStateError */ | |
| return E_FAIL; | |
| } | |
| for(i = 0; i < ARRAY_SIZE(response_type_desc); i++) | |
| if(!wcscmp(v, response_type_desc[i].str)) | |
| break; | |
| if(i >= ARRAY_SIZE(response_type_desc)) | |
| return S_OK; | |
| nsAString_InitDepend(&nsstr, response_type_desc[i].nsxhr_str); | |
| nsres = nsIXMLHttpRequest_SetResponseType(This->xhr.nsxhr, &nsstr); | |
| nsAString_Finish(&nsstr); | |
| if(NS_FAILED(nsres)) | |
| return map_nsresult(nsres); | |
| This->xhr.response_type = i; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_responseType(IWineXMLHttpRequestPrivate *iface, BSTR *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| *p = SysAllocString(response_type_desc[This->xhr.response_type].str); | |
| return *p ? S_OK : E_OUTOFMEMORY; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_upload(IWineXMLHttpRequestPrivate *iface, IDispatch **p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| FIXME("(%p)->(%p)\n", This, p); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_withCredentials(IWineXMLHttpRequestPrivate *iface, VARIANT_BOOL v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%x)\n", This, v); | |
| return map_nsresult(nsIXMLHttpRequest_SetWithCredentials(This->xhr.nsxhr, !!v)); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_withCredentials(IWineXMLHttpRequestPrivate *iface, VARIANT_BOOL *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| nsresult nsres; | |
| cpp_bool b; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| nsres = nsIXMLHttpRequest_GetWithCredentials(This->xhr.nsxhr, &b); | |
| if(NS_FAILED(nsres)) | |
| return map_nsresult(nsres); | |
| *p = b ? VARIANT_TRUE : VARIANT_FALSE; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_overrideMimeType(IWineXMLHttpRequestPrivate *iface, BSTR mimeType) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| static const WCHAR generic_type[] = L"application/octet-stream"; | |
| const WCHAR *type = NULL; | |
| WCHAR *lowercase = NULL; | |
| nsAString nsstr; | |
| nsresult nsres; | |
| TRACE("(%p)->(%s)\n", This, debugstr_w(mimeType)); | |
| if(mimeType) { | |
| if(mimeType[0]) { | |
| if(!(lowercase = wcsdup(mimeType))) | |
| return E_OUTOFMEMORY; | |
| _wcslwr(lowercase); | |
| type = lowercase; | |
| }else | |
| type = generic_type; | |
| } | |
| nsAString_InitDepend(&nsstr, type); | |
| nsres = nsIXMLHttpRequest_SlowOverrideMimeType(This->xhr.nsxhr, &nsstr); | |
| nsAString_Finish(&nsstr); | |
| free(lowercase); | |
| return map_nsresult(nsres); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_onerror(IWineXMLHttpRequestPrivate *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_ERROR, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_onerror(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_ERROR, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_onabort(IWineXMLHttpRequestPrivate *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_ABORT, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_onabort(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_ABORT, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_onprogress(IWineXMLHttpRequestPrivate *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_PROGRESS, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_onprogress(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_PROGRESS, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_onloadstart(IWineXMLHttpRequestPrivate *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_LOADSTART, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_onloadstart(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_LOADSTART, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_onloadend(IWineXMLHttpRequestPrivate *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_LOADEND, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_onloadend(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_LOADEND, p); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_put_onload(IWineXMLHttpRequestPrivate *iface, VARIANT v) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_LOAD, &v); | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequest_private_get_onload(IWineXMLHttpRequestPrivate *iface, VARIANT *p) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IWineXMLHttpRequestPrivate(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_LOAD, p); | |
| } | |
| static const IWineXMLHttpRequestPrivateVtbl WineXMLHttpRequestPrivateVtbl = { | |
| HTMLXMLHttpRequest_private_QueryInterface, | |
| HTMLXMLHttpRequest_private_AddRef, | |
| HTMLXMLHttpRequest_private_Release, | |
| HTMLXMLHttpRequest_private_GetTypeInfoCount, | |
| HTMLXMLHttpRequest_private_GetTypeInfo, | |
| HTMLXMLHttpRequest_private_GetIDsOfNames, | |
| HTMLXMLHttpRequest_private_Invoke, | |
| HTMLXMLHttpRequest_private_get_response, | |
| HTMLXMLHttpRequest_private_put_responseType, | |
| HTMLXMLHttpRequest_private_get_responseType, | |
| HTMLXMLHttpRequest_private_get_upload, | |
| HTMLXMLHttpRequest_private_put_withCredentials, | |
| HTMLXMLHttpRequest_private_get_withCredentials, | |
| HTMLXMLHttpRequest_private_overrideMimeType, | |
| HTMLXMLHttpRequest_private_put_onerror, | |
| HTMLXMLHttpRequest_private_get_onerror, | |
| HTMLXMLHttpRequest_private_put_onabort, | |
| HTMLXMLHttpRequest_private_get_onabort, | |
| HTMLXMLHttpRequest_private_put_onprogress, | |
| HTMLXMLHttpRequest_private_get_onprogress, | |
| HTMLXMLHttpRequest_private_put_onloadstart, | |
| HTMLXMLHttpRequest_private_get_onloadstart, | |
| HTMLXMLHttpRequest_private_put_onloadend, | |
| HTMLXMLHttpRequest_private_get_onloadend, | |
| HTMLXMLHttpRequest_private_put_onload, | |
| HTMLXMLHttpRequest_private_get_onload | |
| }; | |
| static inline HTMLXMLHttpRequest *impl_from_IProvideClassInfo2(IProvideClassInfo2 *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXMLHttpRequest, IProvideClassInfo2_iface); | |
| } | |
| static HRESULT WINAPI ProvideClassInfo_QueryInterface(IProvideClassInfo2 *iface, REFIID riid, void **ppv) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IProvideClassInfo2(iface); | |
| return IHTMLXMLHttpRequest_QueryInterface(&This->IHTMLXMLHttpRequest_iface, riid, ppv); | |
| } | |
| static ULONG WINAPI ProvideClassInfo_AddRef(IProvideClassInfo2 *iface) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IProvideClassInfo2(iface); | |
| return IHTMLXMLHttpRequest_AddRef(&This->IHTMLXMLHttpRequest_iface); | |
| } | |
| static ULONG WINAPI ProvideClassInfo_Release(IProvideClassInfo2 *iface) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IProvideClassInfo2(iface); | |
| return IHTMLXMLHttpRequest_Release(&This->IHTMLXMLHttpRequest_iface); | |
| } | |
| static HRESULT WINAPI ProvideClassInfo_GetClassInfo(IProvideClassInfo2 *iface, ITypeInfo **ppTI) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IProvideClassInfo2(iface); | |
| TRACE("(%p)->(%p)\n", This, ppTI); | |
| return get_class_typeinfo(&CLSID_HTMLXMLHttpRequest, ppTI); | |
| } | |
| static HRESULT WINAPI ProvideClassInfo2_GetGUID(IProvideClassInfo2 *iface, DWORD dwGuidKind, GUID *pGUID) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_IProvideClassInfo2(iface); | |
| FIXME("(%p)->(%lu %p)\n", This, dwGuidKind, pGUID); | |
| return E_NOTIMPL; | |
| } | |
| static const IProvideClassInfo2Vtbl ProvideClassInfo2Vtbl = { | |
| ProvideClassInfo_QueryInterface, | |
| ProvideClassInfo_AddRef, | |
| ProvideClassInfo_Release, | |
| ProvideClassInfo_GetClassInfo, | |
| ProvideClassInfo2_GetGUID, | |
| }; | |
| static inline HTMLXMLHttpRequest *impl_from_DispatchEx(DispatchEx *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXMLHttpRequest, xhr.event_target.dispex); | |
| } | |
| static inline struct xhr *xhr_from_DispatchEx(DispatchEx *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct xhr, event_target.dispex); | |
| } | |
| static void *HTMLXMLHttpRequest_query_interface(DispatchEx *dispex, REFIID riid) | |
| { | |
| HTMLXMLHttpRequest *This = impl_from_DispatchEx(dispex); | |
| if(IsEqualGUID(&IID_IHTMLXMLHttpRequest, riid)) | |
| return &This->IHTMLXMLHttpRequest_iface; | |
| if(IsEqualGUID(&IID_IHTMLXMLHttpRequest2, riid)) | |
| return &This->IHTMLXMLHttpRequest2_iface; | |
| if(IsEqualGUID(&IID_IWineXMLHttpRequestPrivate, riid)) | |
| return &This->IWineXMLHttpRequestPrivate_iface; | |
| if(IsEqualGUID(&IID_IProvideClassInfo, riid)) | |
| return &This->IProvideClassInfo2_iface; | |
| if(IsEqualGUID(&IID_IProvideClassInfo2, riid)) | |
| return &This->IProvideClassInfo2_iface; | |
| return EventTarget_query_interface(&This->xhr.event_target, riid); | |
| } | |
| static void xhr_traverse(DispatchEx *dispex, nsCycleCollectionTraversalCallback *cb) | |
| { | |
| struct xhr *xhr = xhr_from_DispatchEx(dispex); | |
| if(xhr->window) | |
| note_cc_edge((nsISupports*)&xhr->window->base.IHTMLWindow2_iface, "window", cb); | |
| if(xhr->pending_progress_event) | |
| note_cc_edge((nsISupports*)&xhr->pending_progress_event->IDOMEvent_iface, "pending_progress_event", cb); | |
| if(xhr->response_obj) | |
| note_cc_edge((nsISupports*)xhr->response_obj, "response_obj", cb); | |
| if(xhr->nsxhr) | |
| note_cc_edge((nsISupports*)xhr->nsxhr, "nsxhr", cb); | |
| traverse_event_target(&xhr->event_target, cb); | |
| } | |
| static void xhr_unlink(DispatchEx *dispex) | |
| { | |
| struct xhr *xhr = xhr_from_DispatchEx(dispex); | |
| if(xhr->event_listener) { | |
| XMLHttpReqEventListener *event_listener = xhr->event_listener; | |
| xhr->event_listener = NULL; | |
| detach_xhr_event_listener(event_listener); | |
| } | |
| if(xhr->window) { | |
| HTMLInnerWindow *window = xhr->window; | |
| xhr->window = NULL; | |
| IHTMLWindow2_Release(&window->base.IHTMLWindow2_iface); | |
| } | |
| if(xhr->pending_progress_event) { | |
| DOMEvent *pending_progress_event = xhr->pending_progress_event; | |
| xhr->pending_progress_event = NULL; | |
| IDOMEvent_Release(&pending_progress_event->IDOMEvent_iface); | |
| } | |
| unlink_ref(&xhr->response_obj); | |
| unlink_ref(&xhr->nsxhr); | |
| release_event_target(&xhr->event_target); | |
| } | |
| static void xhr_destructor(DispatchEx *dispex) | |
| { | |
| struct xhr *xhr = xhr_from_DispatchEx(dispex); | |
| free(xhr); | |
| } | |
| static void xhr_last_release(DispatchEx *dispex) | |
| { | |
| struct xhr *xhr = xhr_from_DispatchEx(dispex); | |
| remove_target_tasks(xhr->task_magic); | |
| } | |
| static nsISupports *xhr_get_gecko_target(DispatchEx *dispex) | |
| { | |
| struct xhr *xhr = xhr_from_DispatchEx(dispex); | |
| return (nsISupports*)xhr->nsxhr; | |
| } | |
| static void xhr_bind_event(DispatchEx *dispex, eventid_t eid) | |
| { | |
| /* Do nothing. To be able to track state and queue events manually, when blocked | |
| * by sync XHRs in their send() event loop, we always register the handlers. */ | |
| } | |
| static void HTMLXMLHttpRequest_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) | |
| { | |
| static const dispex_hook_t xhr_hooks[] = { | |
| {DISPID_IHTMLXMLHTTPREQUEST_OPEN, HTMLXMLHttpRequest_open_hook}, | |
| {DISPID_UNKNOWN} | |
| }; | |
| static const dispex_hook_t private_hooks[] = { | |
| {DISPID_IWINEXMLHTTPREQUESTPRIVATE_RESPONSE}, | |
| {DISPID_IWINEXMLHTTPREQUESTPRIVATE_RESPONSETYPE}, | |
| {DISPID_IWINEXMLHTTPREQUESTPRIVATE_UPLOAD}, | |
| {DISPID_IWINEXMLHTTPREQUESTPRIVATE_WITHCREDENTIALS}, | |
| {DISPID_EVPROP_ONERROR}, | |
| {DISPID_EVPROP_ONABORT}, | |
| {DISPID_EVPROP_PROGRESS}, | |
| {DISPID_EVPROP_LOADSTART}, | |
| {DISPID_EVPROP_LOADEND}, | |
| /* IE10 only */ | |
| {DISPID_IWINEXMLHTTPREQUESTPRIVATE_OVERRIDEMIMETYPE}, | |
| {DISPID_UNKNOWN} | |
| }; | |
| const dispex_hook_t *const private_ie10_hooks = private_hooks + ARRAY_SIZE(private_hooks) - 2; | |
| EventTarget_init_dispex_info(info, compat_mode); | |
| dispex_info_add_interface(info, IHTMLXMLHttpRequest_tid, compat_mode >= COMPAT_MODE_IE10 ? xhr_hooks : NULL); | |
| dispex_info_add_interface(info, IWineXMLHttpRequestPrivate_tid, | |
| compat_mode < COMPAT_MODE_IE10 ? private_hooks : | |
| compat_mode < COMPAT_MODE_IE11 ? private_ie10_hooks : NULL); | |
| } | |
| static const event_target_vtbl_t HTMLXMLHttpRequest_event_target_vtbl = { | |
| { | |
| .query_interface = HTMLXMLHttpRequest_query_interface, | |
| .destructor = xhr_destructor, | |
| .traverse = xhr_traverse, | |
| .unlink = xhr_unlink, | |
| .last_release = xhr_last_release | |
| }, | |
| .get_gecko_target = xhr_get_gecko_target, | |
| .bind_event = xhr_bind_event | |
| }; | |
| /* IHTMLXMLHttpRequestFactory */ | |
| static inline struct constructor *impl_from_IHTMLXMLHttpRequestFactory(IHTMLXMLHttpRequestFactory *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct constructor, iface); | |
| } | |
| DISPEX_IDISPATCH_IMPL(HTMLXMLHttpRequestFactory, IHTMLXMLHttpRequestFactory, | |
| impl_from_IHTMLXMLHttpRequestFactory(iface)->dispex) | |
| static HRESULT alloc_xhr(HTMLInnerWindow *window, dispex_static_data_t *dispex, unsigned size, void **p) | |
| { | |
| XMLHttpReqEventListener *event_listener; | |
| nsIDOMEventTarget *nstarget; | |
| nsIXMLHttpRequest *nsxhr; | |
| struct xhr *ret; | |
| nsresult nsres; | |
| unsigned i; | |
| nsxhr = create_nsxhr(window->dom_window); | |
| if(!nsxhr) | |
| return E_FAIL; | |
| ret = calloc(1, size); | |
| if(!ret) { | |
| nsIXMLHttpRequest_Release(nsxhr); | |
| return E_OUTOFMEMORY; | |
| } | |
| event_listener = malloc(sizeof(*event_listener)); | |
| if(!event_listener) { | |
| free(ret); | |
| nsIXMLHttpRequest_Release(nsxhr); | |
| return E_OUTOFMEMORY; | |
| } | |
| ret->nsxhr = nsxhr; | |
| ret->window = window; | |
| ret->task_magic = get_task_target_magic(); | |
| IHTMLWindow2_AddRef(&window->base.IHTMLWindow2_iface); | |
| init_event_target(&ret->event_target, dispex, window); | |
| /* Always register the handlers because we need them to track state */ | |
| event_listener->nsIDOMEventListener_iface.lpVtbl = &XMLHttpReqEventListenerVtbl; | |
| event_listener->ref = 1; | |
| event_listener->xhr = ret; | |
| ret->event_listener = event_listener; | |
| nsres = nsIXMLHttpRequest_QueryInterface(nsxhr, &IID_nsIDOMEventTarget, (void**)&nstarget); | |
| assert(nsres == NS_OK); | |
| for(i = 0; i < ARRAY_SIZE(events); i++) { | |
| const WCHAR *name = get_event_name(events[i]); | |
| nsAString type_str; | |
| nsAString_InitDepend(&type_str, name); | |
| nsres = nsIDOMEventTarget_AddEventListener(nstarget, &type_str, &event_listener->nsIDOMEventListener_iface, FALSE, TRUE, 2); | |
| nsAString_Finish(&type_str); | |
| if(NS_FAILED(nsres)) { | |
| WARN("AddEventListener(%s) failed: %08lx\n", debugstr_w(name), nsres); | |
| IWineJSDispatchHost_Release(&ret->event_target.dispex.IWineJSDispatchHost_iface); | |
| return map_nsresult(nsres); | |
| } | |
| } | |
| nsIDOMEventTarget_Release(nstarget); | |
| *p = ret; | |
| return S_OK; | |
| } | |
| static HRESULT create_xhr(HTMLInnerWindow *window, HTMLXMLHttpRequest **p) | |
| { | |
| HTMLXMLHttpRequest *ret; | |
| HRESULT hres; | |
| hres = alloc_xhr(window, &XMLHttpRequest_dispex, sizeof(*ret), (void**)&ret); | |
| if(SUCCEEDED(hres)) { | |
| ret->IHTMLXMLHttpRequest_iface.lpVtbl = &HTMLXMLHttpRequestVtbl; | |
| ret->IHTMLXMLHttpRequest2_iface.lpVtbl = &HTMLXMLHttpRequest2Vtbl; | |
| ret->IWineXMLHttpRequestPrivate_iface.lpVtbl = &WineXMLHttpRequestPrivateVtbl; | |
| ret->IProvideClassInfo2_iface.lpVtbl = &ProvideClassInfo2Vtbl; | |
| *p = ret; | |
| } | |
| return hres; | |
| } | |
| static HRESULT WINAPI HTMLXMLHttpRequestFactory_create(IHTMLXMLHttpRequestFactory *iface, IHTMLXMLHttpRequest **p) | |
| { | |
| struct constructor *This = impl_from_IHTMLXMLHttpRequestFactory(iface); | |
| HTMLXMLHttpRequest *xhr; | |
| HRESULT hres; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| hres = create_xhr(This->window, &xhr); | |
| if(SUCCEEDED(hres)) | |
| *p = &xhr->IHTMLXMLHttpRequest_iface; | |
| return hres; | |
| } | |
| static const IHTMLXMLHttpRequestFactoryVtbl HTMLXMLHttpRequestFactoryVtbl = { | |
| HTMLXMLHttpRequestFactory_QueryInterface, | |
| HTMLXMLHttpRequestFactory_AddRef, | |
| HTMLXMLHttpRequestFactory_Release, | |
| HTMLXMLHttpRequestFactory_GetTypeInfoCount, | |
| HTMLXMLHttpRequestFactory_GetTypeInfo, | |
| HTMLXMLHttpRequestFactory_GetIDsOfNames, | |
| HTMLXMLHttpRequestFactory_Invoke, | |
| HTMLXMLHttpRequestFactory_create | |
| }; | |
| static void *HTMLXMLHttpRequestFactory_query_interface(DispatchEx *dispex, REFIID riid) | |
| { | |
| struct constructor *This = constructor_from_DispatchEx(dispex); | |
| if(IsEqualGUID(&IID_IHTMLXMLHttpRequestFactory, riid)) | |
| return &This->iface; | |
| return NULL; | |
| } | |
| static HRESULT HTMLXMLHttpRequestFactory_value(DispatchEx *iface, LCID lcid, WORD flags, DISPPARAMS *params, | |
| VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) | |
| { | |
| struct constructor *This = constructor_from_DispatchEx(iface); | |
| HTMLXMLHttpRequest *xhr; | |
| HRESULT hres; | |
| TRACE("\n"); | |
| if(flags != DISPATCH_CONSTRUCT) { | |
| FIXME("flags %x not supported\n", flags); | |
| return E_NOTIMPL; | |
| } | |
| hres = create_xhr(This->window, &xhr); | |
| if(FAILED(hres)) | |
| return hres; | |
| V_VT(res) = VT_DISPATCH; | |
| V_DISPATCH(res) = (IDispatch*)&xhr->IHTMLXMLHttpRequest_iface; | |
| return S_OK; | |
| } | |
| static const dispex_static_data_vtbl_t HTMLXMLHttpRequestFactory_dispex_vtbl = { | |
| .query_interface = HTMLXMLHttpRequestFactory_query_interface, | |
| .destructor = constructor_destructor, | |
| .traverse = constructor_traverse, | |
| .unlink = constructor_unlink, | |
| .value = HTMLXMLHttpRequestFactory_value | |
| }; | |
| static const tid_t HTMLXMLHttpRequestFactory_iface_tids[] = { | |
| IHTMLXMLHttpRequestFactory_tid, | |
| 0 | |
| }; | |
| static dispex_static_data_t HTMLXMLHttpRequestFactory_dispex = { | |
| .name = "XMLHttpRequest", | |
| .constructor_id = OBJID_XMLHttpRequest, | |
| .vtbl = &HTMLXMLHttpRequestFactory_dispex_vtbl, | |
| .disp_tid = IHTMLXMLHttpRequestFactory_tid, | |
| .iface_tids = HTMLXMLHttpRequestFactory_iface_tids, | |
| }; | |
| static HRESULT init_constructor(struct constructor *constr, dispex_static_data_t *dispex, const void *vtbl) | |
| { | |
| struct constructor *create; | |
| HRESULT hres; | |
| constr->iface.lpVtbl = vtbl; | |
| init_dispatch(&constr->dispex, dispex, constr->window, dispex_compat_mode(&constr->window->event_target.dispex)); | |
| if(!constr->window->jscript) | |
| return S_OK; | |
| if(!(create = malloc(sizeof(*create)))) | |
| return E_OUTOFMEMORY; | |
| create->iface = constr->iface; | |
| create->window = constr->window; | |
| IHTMLWindow2_AddRef(&constr->window->base.IHTMLWindow2_iface); | |
| init_dispatch_from_desc(&create->dispex, constr->dispex.info, NULL, NULL); | |
| hres = IWineJScript_InitHostConstructor(create->window->jscript, &create->dispex.IWineJSDispatchHost_iface, | |
| L"create", &create->dispex.jsdisp); | |
| if(SUCCEEDED(hres)) { | |
| VARIANT v; | |
| V_VT(&v) = VT_DISPATCH; | |
| V_DISPATCH(&v) = (IDispatch*)&create->dispex.IWineJSDispatchHost_iface; | |
| hres = IWineJSDispatch_DefineProperty(constr->dispex.jsdisp, L"create", PROPF_WRITABLE | PROPF_CONFIGURABLE, &v); | |
| } | |
| IWineJSDispatchHost_Release(&create->dispex.IWineJSDispatchHost_iface); | |
| return hres; | |
| } | |
| static HRESULT HTMLXMLHttpRequestFactory_init(struct constructor *constr) | |
| { | |
| return init_constructor(constr, &HTMLXMLHttpRequestFactory_dispex, &HTMLXMLHttpRequestFactoryVtbl); | |
| } | |
| static const tid_t HTMLXMLHttpRequest_iface_tids[] = { | |
| IHTMLXMLHttpRequest2_tid, | |
| 0 | |
| }; | |
| dispex_static_data_t XMLHttpRequest_dispex = { | |
| .id = OBJID_XMLHttpRequest, | |
| .init_constructor = HTMLXMLHttpRequestFactory_init, | |
| .vtbl = &HTMLXMLHttpRequest_event_target_vtbl.dispex_vtbl, | |
| .disp_tid = DispHTMLXMLHttpRequest_tid, | |
| .iface_tids = HTMLXMLHttpRequest_iface_tids, | |
| .init_info = HTMLXMLHttpRequest_init_dispex_info, | |
| }; | |
| /* IHTMLXDomainRequest */ | |
| static inline HTMLXDomainRequest *impl_from_IHTMLXDomainRequest(IHTMLXDomainRequest *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXDomainRequest, IHTMLXDomainRequest_iface); | |
| } | |
| DISPEX_IDISPATCH_IMPL(HTMLXDomainRequest, IHTMLXDomainRequest, | |
| impl_from_IHTMLXDomainRequest(iface)->xhr.event_target.dispex) | |
| static HRESULT WINAPI HTMLXDomainRequest_get_responseText(IHTMLXDomainRequest *iface, BSTR *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| return get_response_text(&This->xhr, p); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_put_timeout(IHTMLXDomainRequest *iface, LONG v) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%ld)\n", This, v); | |
| if(v < 0) | |
| return E_INVALIDARG; | |
| return map_nsresult(nsIXMLHttpRequest_SetTimeout(This->xhr.nsxhr, v)); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_get_timeout(IHTMLXDomainRequest *iface, LONG *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| nsresult nsres; | |
| UINT32 timeout; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_INVALIDARG; | |
| nsres = nsIXMLHttpRequest_GetTimeout(This->xhr.nsxhr, &timeout); | |
| *p = timeout ? timeout : -1; | |
| return map_nsresult(nsres); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_get_contentType(IHTMLXDomainRequest *iface, BSTR *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| const char *content_type = "text/plain"; | |
| nsIChannel *nschannel; | |
| HRESULT hres = S_OK; | |
| nsACString nsstr; | |
| nsresult nsres; | |
| int len; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| if(!p) | |
| return E_POINTER; | |
| if(This->xhr.ready_state < READYSTATE_LOADED) { | |
| *p = NULL; | |
| return S_OK; | |
| } | |
| nsACString_Init(&nsstr, NULL); | |
| nsres = nsIXMLHttpRequest_GetChannel(This->xhr.nsxhr, &nschannel); | |
| if(NS_SUCCEEDED(nsres)) { | |
| nsres = nsIChannel_GetContentType(nschannel, &nsstr); | |
| if(NS_SUCCEEDED(nsres)) | |
| nsACString_GetData(&nsstr, &content_type); | |
| nsIChannel_Release(nschannel); | |
| } | |
| len = MultiByteToWideChar(CP_UTF8, 0, content_type, -1, NULL, 0); | |
| if(!(*p = SysAllocStringLen(NULL, len - 1))) | |
| hres = E_OUTOFMEMORY; | |
| else | |
| MultiByteToWideChar(CP_UTF8, 0, content_type, -1, *p, len); | |
| nsACString_Finish(&nsstr); | |
| return hres; | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_put_onprogress(IHTMLXDomainRequest *iface, VARIANT v) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_PROGRESS, &v); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_get_onprogress(IHTMLXDomainRequest *iface, VARIANT *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_PROGRESS, p); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_put_onerror(IHTMLXDomainRequest *iface, VARIANT v) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_ERROR, &v); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_get_onerror(IHTMLXDomainRequest *iface, VARIANT *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_ERROR, p); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_put_ontimeout(IHTMLXDomainRequest *iface, VARIANT v) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_TIMEOUT, &v); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_get_ontimeout(IHTMLXDomainRequest *iface, VARIANT *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_TIMEOUT, p); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_put_onload(IHTMLXDomainRequest *iface, VARIANT v) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%s)\n", This, debugstr_variant(&v)); | |
| return set_event_handler(&This->xhr.event_target, EVENTID_LOAD, &v); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_get_onload(IHTMLXDomainRequest *iface, VARIANT *p) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| TRACE("(%p)->(%p)\n", This, p); | |
| return get_event_handler(&This->xhr.event_target, EVENTID_LOAD, p); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_abort(IHTMLXDomainRequest *iface) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| return xhr_abort(&This->xhr); | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_open(IHTMLXDomainRequest *iface, BSTR bstrMethod, BSTR bstrUrl) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| HTMLOuterWindow *outer_window = This->xhr.window->base.outer_window; | |
| nsLoadFlags load_flags; | |
| DWORD scheme, scheme2; | |
| nsIChannel *nschannel; | |
| nsAString nsstr; | |
| nsresult nsres; | |
| HRESULT hres; | |
| IUri *uri; | |
| TRACE("(%p)->(%s %s)\n", This, debugstr_w(bstrMethod), debugstr_w(bstrUrl)); | |
| if(!outer_window || !outer_window->uri) | |
| return E_ACCESSDENIED; | |
| hres = create_relative_uri(outer_window, bstrUrl, &uri); | |
| if(FAILED(hres)) | |
| return hres; | |
| hres = IUri_GetScheme(uri, &scheme); | |
| if(hres == S_OK) { | |
| if(scheme != URL_SCHEME_HTTP && scheme != URL_SCHEME_HTTPS) | |
| hres = E_ACCESSDENIED; | |
| else { | |
| hres = IUri_GetScheme(outer_window->uri, &scheme2); | |
| if(hres == S_OK && scheme != scheme2) | |
| hres = E_ACCESSDENIED; | |
| } | |
| } | |
| IUri_Release(uri); | |
| if(hres != S_OK) | |
| return E_ACCESSDENIED; | |
| hres = xhr_open(&This->xhr, bstrMethod, bstrUrl, TRUE, NULL, NULL, 1); | |
| if(FAILED(hres)) | |
| return hres; | |
| /* Prevent Gecko from parsing responseXML for no reason */ | |
| nsAString_InitDepend(&nsstr, L"text"); | |
| nsIXMLHttpRequest_SetResponseType(This->xhr.nsxhr, &nsstr); | |
| nsAString_Finish(&nsstr); | |
| /* Always sniff the content type even there's a Content-Type response header, to match native */ | |
| nsres = nsIXMLHttpRequest_GetChannel(This->xhr.nsxhr, &nschannel); | |
| if(NS_SUCCEEDED(nsres)) { | |
| nsres = nsIChannel_GetLoadFlags(nschannel, &load_flags); | |
| if(NS_SUCCEEDED(nsres)) { | |
| load_flags |= LOAD_CALL_CONTENT_SNIFFERS; | |
| nsIChannel_SetLoadFlags(nschannel, load_flags); | |
| } | |
| nsIChannel_Release(nschannel); | |
| } | |
| /* IE always adds Origin header, even from same origin, but Gecko doesn't allow us to alter it. */ | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI HTMLXDomainRequest_send(IHTMLXDomainRequest *iface, VARIANT varBody) | |
| { | |
| HTMLXDomainRequest *This = impl_from_IHTMLXDomainRequest(iface); | |
| return xhr_send(&This->xhr, &varBody); | |
| } | |
| static const IHTMLXDomainRequestVtbl HTMLXDomainRequestVtbl = { | |
| HTMLXDomainRequest_QueryInterface, | |
| HTMLXDomainRequest_AddRef, | |
| HTMLXDomainRequest_Release, | |
| HTMLXDomainRequest_GetTypeInfoCount, | |
| HTMLXDomainRequest_GetTypeInfo, | |
| HTMLXDomainRequest_GetIDsOfNames, | |
| HTMLXDomainRequest_Invoke, | |
| HTMLXDomainRequest_get_responseText, | |
| HTMLXDomainRequest_put_timeout, | |
| HTMLXDomainRequest_get_timeout, | |
| HTMLXDomainRequest_get_contentType, | |
| HTMLXDomainRequest_put_onprogress, | |
| HTMLXDomainRequest_get_onprogress, | |
| HTMLXDomainRequest_put_onerror, | |
| HTMLXDomainRequest_get_onerror, | |
| HTMLXDomainRequest_put_ontimeout, | |
| HTMLXDomainRequest_get_ontimeout, | |
| HTMLXDomainRequest_put_onload, | |
| HTMLXDomainRequest_get_onload, | |
| HTMLXDomainRequest_abort, | |
| HTMLXDomainRequest_open, | |
| HTMLXDomainRequest_send | |
| }; | |
| static inline HTMLXDomainRequest *HTMLXDomainRequest_from_DispatchEx(DispatchEx *iface) | |
| { | |
| return CONTAINING_RECORD(iface, HTMLXDomainRequest, xhr.event_target.dispex); | |
| } | |
| static void *HTMLXDomainRequest_query_interface(DispatchEx *dispex, REFIID riid) | |
| { | |
| HTMLXDomainRequest *This = HTMLXDomainRequest_from_DispatchEx(dispex); | |
| if(IsEqualGUID(&IID_IHTMLXDomainRequest, riid)) | |
| return &This->IHTMLXDomainRequest_iface; | |
| return EventTarget_query_interface(&This->xhr.event_target, riid); | |
| } | |
| static const event_target_vtbl_t HTMLXDomainRequest_event_target_vtbl = { | |
| { | |
| .query_interface = HTMLXDomainRequest_query_interface, | |
| .destructor = xhr_destructor, | |
| .traverse = xhr_traverse, | |
| .unlink = xhr_unlink, | |
| .last_release = xhr_last_release | |
| }, | |
| .get_gecko_target = xhr_get_gecko_target, | |
| .bind_event = xhr_bind_event | |
| }; | |
| static void HTMLXDomainRequest_init_dispex_info(dispex_data_t *info, compat_mode_t compat_mode) | |
| { | |
| dispex_info_add_interface(info, IHTMLXDomainRequest_tid, NULL); | |
| } | |
| /* IHTMLXDomainRequestFactory */ | |
| static inline struct constructor *impl_from_IHTMLXDomainRequestFactory(IHTMLXDomainRequestFactory *iface) | |
| { | |
| return CONTAINING_RECORD(iface, struct constructor, iface); | |
| } | |
| DISPEX_IDISPATCH_IMPL(HTMLXDomainRequestFactory, IHTMLXDomainRequestFactory, | |
| impl_from_IHTMLXDomainRequestFactory(iface)->dispex) | |
| static HRESULT WINAPI HTMLXDomainRequestFactory_create(IHTMLXDomainRequestFactory *iface, IHTMLXDomainRequest **p) | |
| { | |
| struct constructor *This = impl_from_IHTMLXDomainRequestFactory(iface); | |
| HTMLXDomainRequest *xdr; | |
| HRESULT hres; | |
| TRACE("(%p)->(%p)\n", This, p); | |
| hres = alloc_xhr(This->window, &XDomainRequest_dispex, sizeof(*xdr), (void**)&xdr); | |
| if(FAILED(hres)) | |
| return hres; | |
| xdr->IHTMLXDomainRequest_iface.lpVtbl = &HTMLXDomainRequestVtbl; | |
| *p = &xdr->IHTMLXDomainRequest_iface; | |
| return hres; | |
| } | |
| static const IHTMLXDomainRequestFactoryVtbl HTMLXDomainRequestFactoryVtbl = { | |
| HTMLXDomainRequestFactory_QueryInterface, | |
| HTMLXDomainRequestFactory_AddRef, | |
| HTMLXDomainRequestFactory_Release, | |
| HTMLXDomainRequestFactory_GetTypeInfoCount, | |
| HTMLXDomainRequestFactory_GetTypeInfo, | |
| HTMLXDomainRequestFactory_GetIDsOfNames, | |
| HTMLXDomainRequestFactory_Invoke, | |
| HTMLXDomainRequestFactory_create | |
| }; | |
| static void *HTMLXDomainRequestFactory_query_interface(DispatchEx *dispex, REFIID riid) | |
| { | |
| struct constructor *This = constructor_from_DispatchEx(dispex); | |
| if(IsEqualGUID(&IID_IHTMLXDomainRequestFactory, riid)) | |
| return &This->iface; | |
| return NULL; | |
| } | |
| static HRESULT HTMLXDomainRequestFactory_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *params, | |
| VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) | |
| { | |
| struct constructor *This = constructor_from_DispatchEx(dispex); | |
| IHTMLXDomainRequest *xdr; | |
| HRESULT hres; | |
| TRACE("\n"); | |
| if(flags != DISPATCH_CONSTRUCT) { | |
| FIXME("flags %x not supported\n", flags); | |
| return E_NOTIMPL; | |
| } | |
| hres = HTMLXDomainRequestFactory_create((IHTMLXDomainRequestFactory*)&This->iface, &xdr); | |
| if(FAILED(hres)) | |
| return hres; | |
| V_VT(res) = VT_DISPATCH; | |
| V_DISPATCH(res) = (IDispatch*)xdr; | |
| return hres; | |
| } | |
| static const dispex_static_data_vtbl_t HTMLXDomainRequestFactory_dispex_vtbl = { | |
| .query_interface = HTMLXDomainRequestFactory_query_interface, | |
| .destructor = constructor_destructor, | |
| .traverse = constructor_traverse, | |
| .unlink = constructor_unlink, | |
| .value = HTMLXDomainRequestFactory_value, | |
| }; | |
| static const tid_t HTMLXDomainRequestFactory_iface_tids[] = { | |
| IHTMLXDomainRequestFactory_tid, | |
| 0 | |
| }; | |
| static dispex_static_data_t HTMLXDomainRequestFactory_dispex = { | |
| .name = "XDomainRequest", | |
| .constructor_id = OBJID_XDomainRequest, | |
| .vtbl = &HTMLXDomainRequestFactory_dispex_vtbl, | |
| .disp_tid = IHTMLXDomainRequestFactory_tid, | |
| .iface_tids = HTMLXDomainRequestFactory_iface_tids, | |
| }; | |
| static HRESULT HTMLXDomainRequestFactory_init(struct constructor *constr) | |
| { | |
| return init_constructor(constr, &HTMLXDomainRequestFactory_dispex, &HTMLXDomainRequestFactoryVtbl); | |
| } | |
| dispex_static_data_t XDomainRequest_dispex = { | |
| .id = OBJID_XDomainRequest, | |
| .init_constructor = &HTMLXDomainRequestFactory_init, | |
| .vtbl = &HTMLXDomainRequest_event_target_vtbl.dispex_vtbl, | |
| .disp_tid = DispXDomainRequest_tid, | |
| .init_info = HTMLXDomainRequest_init_dispex_info, | |
| .max_compat_mode = COMPAT_MODE_IE10 | |
| }; |