Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
6a31e4ac
Commit
6a31e4ac
authored
Feb 27, 2023
by
Gabriel Ivăncescu
Committed by
Alexandre Julliard
Mar 01, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
mshtml: Implement synchronous XMLHttpRequest.
Signed-off-by:
Gabriel Ivăncescu
<
gabrielopcode@gmail.com
>
parent
51a68184
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
691 additions
and
39 deletions
+691
-39
htmlwindow.c
dlls/mshtml/htmlwindow.c
+2
-0
mshtml_private.h
dlls/mshtml/mshtml_private.h
+5
-0
task.c
dlls/mshtml/task.c
+32
-8
events.c
dlls/mshtml/tests/events.c
+200
-1
rsrc.rc
dlls/mshtml/tests/rsrc.rc
+3
-0
script.c
dlls/mshtml/tests/script.c
+22
-2
xhr.js
dlls/mshtml/tests/xhr.js
+140
-0
xhr_iframe.html
dlls/mshtml/tests/xhr_iframe.html
+23
-0
xmlhttprequest.c
dlls/mshtml/tests/xmlhttprequest.c
+6
-12
xmlhttprequest.c
dlls/mshtml/xmlhttprequest.c
+258
-16
No files found.
dlls/mshtml/htmlwindow.c
View file @
6a31e4ac
...
...
@@ -3294,6 +3294,8 @@ static HRESULT WINAPI window_private_postMessage(IWineHTMLWindowPrivate *iface,
return
E_OUTOFMEMORY
;
}
/* Because message events can be sent to different windows, they get blocked by any context */
task
->
header
.
thread_blocked
=
TRUE
;
task
->
event
=
event
;
return
push_event_task
(
&
task
->
header
,
window
,
post_message_proc
,
post_message_destr
,
window
->
task_magic
);
}
...
...
dlls/mshtml/mshtml_private.h
View file @
6a31e4ac
...
...
@@ -607,6 +607,7 @@ struct HTMLInnerWindow {
VARIANT
performance
;
HTMLPerformanceTiming
*
performance_timing
;
unsigned
blocking_depth
;
unsigned
parser_callback_cnt
;
struct
list
script_queue
;
...
...
@@ -1288,6 +1289,7 @@ typedef void (*event_task_proc_t)(event_task_t*);
struct
event_task_t
{
LONG
target_magic
;
BOOL
thread_blocked
;
event_task_proc_t
proc
;
event_task_proc_t
destr
;
struct
list
entry
;
...
...
@@ -1304,11 +1306,14 @@ typedef struct {
struct
list
task_list
;
struct
list
event_task_list
;
struct
list
timer_list
;
struct
list
*
pending_xhr_events_tail
;
struct
wine_rb_tree
session_storage_map
;
void
*
blocking_xhr
;
}
thread_data_t
;
thread_data_t
*
get_thread_data
(
BOOL
)
DECLSPEC_HIDDEN
;
HWND
get_thread_hwnd
(
void
)
DECLSPEC_HIDDEN
;
void
unblock_tasks_and_timers
(
thread_data_t
*
)
DECLSPEC_HIDDEN
;
int
session_storage_map_cmp
(
const
void
*
,
const
struct
wine_rb_entry
*
)
DECLSPEC_HIDDEN
;
void
destroy_session_storage
(
thread_data_t
*
)
DECLSPEC_HIDDEN
;
...
...
dlls/mshtml/task.c
View file @
6a31e4ac
...
...
@@ -96,6 +96,13 @@ HRESULT push_event_task(event_task_t *task, HTMLInnerWindow *window, event_task_
return
S_OK
;
}
static
void
unlink_event_task
(
event_task_t
*
task
,
thread_data_t
*
thread_data
)
{
if
(
thread_data
->
pending_xhr_events_tail
==
&
task
->
entry
)
thread_data
->
pending_xhr_events_tail
=
task
->
entry
.
prev
;
list_remove
(
&
task
->
entry
);
}
static
void
release_task_timer
(
HWND
thread_hwnd
,
task_timer_t
*
timer
)
{
list_remove
(
&
timer
->
entry
);
...
...
@@ -140,7 +147,7 @@ void remove_target_tasks(LONG target)
LIST_FOR_EACH_SAFE
(
liter
,
ltmp
,
&
thread_data
->
event_task_list
)
{
event_task_t
*
task
=
LIST_ENTRY
(
liter
,
event_task_t
,
entry
);
if
(
task
->
target_magic
==
target
)
{
list_remove
(
&
task
->
entry
);
unlink_event_task
(
task
,
thread_data
);
release_event_task
(
task
);
}
}
...
...
@@ -303,7 +310,7 @@ static LRESULT process_timer(void)
thread_data
=
get_thread_data
(
FALSE
);
assert
(
thread_data
!=
NULL
);
if
(
list_empty
(
&
thread_data
->
timer_list
))
{
if
(
list_empty
(
&
thread_data
->
timer_list
)
||
thread_data
->
blocking_xhr
)
{
KillTimer
(
thread_data
->
thread_hwnd
,
TIMER_ID
);
return
0
;
}
...
...
@@ -338,7 +345,7 @@ static LRESULT process_timer(void)
call_timer_disp
(
disp
,
timer_type
);
IDispatch_Release
(
disp
);
}
while
(
!
list_empty
(
&
thread_data
->
timer_list
));
}
while
(
!
list_empty
(
&
thread_data
->
timer_list
)
&&
!
thread_data
->
blocking_xhr
);
KillTimer
(
thread_data
->
thread_hwnd
,
TIMER_ID
);
return
0
;
...
...
@@ -366,15 +373,18 @@ static LRESULT WINAPI hidden_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPa
continue
;
}
head
=
list_head
(
&
thread_data
->
event_task_list
)
;
if
(
head
)
{
head
=
&
thread_data
->
event_task_list
;
while
((
head
=
list_next
(
&
thread_data
->
event_task_list
,
head
))
)
{
event_task_t
*
task
=
LIST_ENTRY
(
head
,
event_task_t
,
entry
);
list_remove
(
&
task
->
entry
);
if
((
!
task
->
thread_blocked
||
!
thread_data
->
blocking_xhr
)
&&
!
task
->
window
->
blocking_depth
)
{
unlink_event_task
(
task
,
thread_data
);
task
->
proc
(
task
);
release_event_task
(
task
);
continue
;
break
;
}
}
if
(
!
head
)
break
;
}
return
0
;
...
...
@@ -451,6 +461,7 @@ thread_data_t *get_thread_data(BOOL create)
list_init
(
&
thread_data
->
task_list
);
list_init
(
&
thread_data
->
event_task_list
);
list_init
(
&
thread_data
->
timer_list
);
thread_data
->
pending_xhr_events_tail
=
&
thread_data
->
event_task_list
;
wine_rb_init
(
&
thread_data
->
session_storage_map
,
session_storage_map_cmp
);
}
...
...
@@ -467,3 +478,16 @@ ULONGLONG get_time_stamp(void)
GetSystemTimeAsFileTime
(
&
time
);
return
(((
ULONGLONG
)
time
.
dwHighDateTime
<<
32
)
+
time
.
dwLowDateTime
)
/
10000
-
time_epoch
;
}
void
unblock_tasks_and_timers
(
thread_data_t
*
thread_data
)
{
if
(
!
list_empty
(
&
thread_data
->
event_task_list
))
PostMessageW
(
thread_data
->
thread_hwnd
,
WM_PROCESSTASK
,
0
,
0
);
if
(
!
thread_data
->
blocking_xhr
&&
!
list_empty
(
&
thread_data
->
timer_list
))
{
task_timer_t
*
timer
=
LIST_ENTRY
(
list_head
(
&
thread_data
->
timer_list
),
task_timer_t
,
entry
);
DWORD
tc
=
GetTickCount
();
SetTimer
(
thread_data
->
thread_hwnd
,
TIMER_ID
,
timer
->
time
>
tc
?
timer
->
time
-
tc
:
0
,
NULL
);
}
}
dlls/mshtml/tests/events.c
View file @
6a31e4ac
...
...
@@ -111,6 +111,8 @@ DEFINE_EXPECT(window1_onstorage);
DEFINE_EXPECT
(
doc2_onstorage
);
DEFINE_EXPECT
(
doc2_onstoragecommit
);
DEFINE_EXPECT
(
window2_onstorage
);
DEFINE_EXPECT
(
async_xhr_done
);
DEFINE_EXPECT
(
sync_xhr_done
);
static
HWND
container_hwnd
=
NULL
;
static
IHTMLWindow2
*
window
;
...
...
@@ -3730,6 +3732,60 @@ static HRESULT WINAPI window2_onstorage(IDispatchEx *iface, DISPID id, LCID lcid
EVENT_HANDLER_FUNC_OBJ
(
window2_onstorage
);
static
HRESULT
WINAPI
async_xhr
(
IDispatchEx
*
iface
,
DISPID
id
,
LCID
lcid
,
WORD
wFlags
,
DISPPARAMS
*
pdp
,
VARIANT
*
pvarRes
,
EXCEPINFO
*
pei
,
IServiceProvider
*
pspCaller
)
{
IHTMLXMLHttpRequest
*
xhr
;
LONG
ready_state
;
HRESULT
hres
;
ok
(
pdp
!=
NULL
,
"pdp == NULL
\n
"
);
ok
(
pdp
->
cArgs
==
(
document_mode
<
9
?
1
:
2
),
"pdp->cArgs = %d
\n
"
,
pdp
->
cArgs
);
ok
(
pdp
->
cNamedArgs
==
1
,
"pdp->cNamedArgs = %d
\n
"
,
pdp
->
cNamedArgs
);
ok
(
pdp
->
rgdispidNamedArgs
[
0
]
==
DISPID_THIS
,
"pdp->rgdispidNamedArgs[0] = %ld
\n
"
,
pdp
->
rgdispidNamedArgs
[
0
]);
ok
(
V_VT
(
pdp
->
rgvarg
)
==
VT_DISPATCH
,
"V_VT(this) = %d
\n
"
,
V_VT
(
pdp
->
rgvarg
));
ok
(
V_DISPATCH
(
pdp
->
rgvarg
)
!=
NULL
,
"V_DISPATCH(this) == NULL
\n
"
);
hres
=
IDispatch_QueryInterface
(
V_DISPATCH
(
pdp
->
rgvarg
),
&
IID_IHTMLXMLHttpRequest
,
(
void
**
)
&
xhr
);
ok
(
hres
==
S_OK
,
"Could not get IHTMLXMLHttpRequest: %08lx
\n
"
,
hres
);
hres
=
IHTMLXMLHttpRequest_get_readyState
(
xhr
,
&
ready_state
);
if
(
SUCCEEDED
(
hres
)
&&
ready_state
==
4
)
CHECK_EXPECT
(
async_xhr_done
);
IHTMLXMLHttpRequest_Release
(
xhr
);
return
S_OK
;
}
EVENT_HANDLER_FUNC_OBJ
(
async_xhr
);
static
HRESULT
WINAPI
sync_xhr
(
IDispatchEx
*
iface
,
DISPID
id
,
LCID
lcid
,
WORD
wFlags
,
DISPPARAMS
*
pdp
,
VARIANT
*
pvarRes
,
EXCEPINFO
*
pei
,
IServiceProvider
*
pspCaller
)
{
IHTMLXMLHttpRequest
*
xhr
;
LONG
ready_state
;
HRESULT
hres
;
ok
(
pdp
!=
NULL
,
"pdp == NULL
\n
"
);
ok
(
pdp
->
cArgs
==
(
document_mode
<
9
?
1
:
2
),
"pdp->cArgs = %d
\n
"
,
pdp
->
cArgs
);
ok
(
pdp
->
cNamedArgs
==
1
,
"pdp->cNamedArgs = %d
\n
"
,
pdp
->
cNamedArgs
);
ok
(
pdp
->
rgdispidNamedArgs
[
0
]
==
DISPID_THIS
,
"pdp->rgdispidNamedArgs[0] = %ld
\n
"
,
pdp
->
rgdispidNamedArgs
[
0
]);
ok
(
V_VT
(
pdp
->
rgvarg
)
==
VT_DISPATCH
,
"V_VT(this) = %d
\n
"
,
V_VT
(
pdp
->
rgvarg
));
ok
(
V_DISPATCH
(
pdp
->
rgvarg
)
!=
NULL
,
"V_DISPATCH(this) == NULL
\n
"
);
hres
=
IDispatch_QueryInterface
(
V_DISPATCH
(
pdp
->
rgvarg
),
&
IID_IHTMLXMLHttpRequest
,
(
void
**
)
&
xhr
);
ok
(
hres
==
S_OK
,
"Could not get IHTMLXMLHttpRequest: %08lx
\n
"
,
hres
);
hres
=
IHTMLXMLHttpRequest_get_readyState
(
xhr
,
&
ready_state
);
if
(
SUCCEEDED
(
hres
)
&&
ready_state
==
4
)
CHECK_EXPECT
(
sync_xhr_done
);
IHTMLXMLHttpRequest_Release
(
xhr
);
return
S_OK
;
}
EVENT_HANDLER_FUNC_OBJ
(
sync_xhr
);
static
HRESULT
QueryInterface
(
REFIID
,
void
**
);
static
HRESULT
browserservice_qi
(
REFIID
,
void
**
);
static
HRESULT
wb_qi
(
REFIID
,
void
**
);
...
...
@@ -5130,11 +5186,31 @@ typedef struct {
IInternetProtocolSink
*
sink
;
IUri
*
uri
;
BOOL
replied
;
ULONG
size
;
const
char
*
data
;
const
char
*
ptr
;
HANDLE
delay_event
;
ULONG
delay
;
}
ProtocolHandler
;
static
ProtocolHandler
*
delay_with_signal_handler
;
static
DWORD
WINAPI
delay_proc
(
void
*
arg
)
{
PROTOCOLDATA
protocol_data
=
{
PI_FORCE_ASYNC
};
ProtocolHandler
*
protocol_handler
=
arg
;
if
(
protocol_handler
->
delay_event
)
WaitForSingleObject
(
protocol_handler
->
delay_event
,
INFINITE
);
else
Sleep
(
protocol_handler
->
delay
);
protocol_handler
->
delay
=
0
;
IInternetProtocolSink_Switch
(
protocol_handler
->
sink
,
&
protocol_data
);
return
0
;
}
static
DWORD
WINAPI
async_switch_proc
(
void
*
arg
)
{
PROTOCOLDATA
protocol_data
=
{
PI_FORCE_ASYNC
};
...
...
@@ -5177,6 +5253,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface)
LONG
ref
=
InterlockedDecrement
(
&
This
->
ref
);
if
(
!
ref
)
{
if
(
This
->
delay_event
)
CloseHandle
(
This
->
delay_event
);
if
(
This
->
sink
)
IInternetProtocolSink_Release
(
This
->
sink
);
if
(
This
->
uri
)
...
...
@@ -5203,6 +5281,7 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
HRESULT
hres
;
BSTR
bstr
;
if
(
!
This
->
replied
)
{
hres
=
IInternetProtocolSink_QueryInterface
(
This
->
sink
,
&
IID_IServiceProvider
,
(
void
**
)
&
service_provider
);
ok
(
hres
==
S_OK
,
"Could not get IServiceProvider iface: %08lx
\n
"
,
hres
);
...
...
@@ -5223,6 +5302,15 @@ static HRESULT WINAPI Protocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA
IHttpNegotiate_Release
(
http_negotiate
);
SysFreeString
(
bstr
);
This
->
replied
=
TRUE
;
if
(
This
->
delay
||
This
->
delay_event
)
{
IInternetProtocolEx_AddRef
(
&
This
->
IInternetProtocolEx_iface
);
QueueUserWorkItem
(
delay_proc
,
This
,
0
);
return
S_OK
;
}
}
hres
=
IInternetProtocolSink_ReportData
(
This
->
sink
,
BSCF_FIRSTDATANOTIFICATION
|
BSCF_LASTDATANOTIFICATION
,
This
->
size
,
This
->
size
);
ok
(
hres
==
S_OK
,
"ReportData failed: %08lx
\n
"
,
hres
);
...
...
@@ -5298,6 +5386,8 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
IInternetBindInfo
*
pOIBindInfo
,
DWORD
grfPI
,
HANDLE
*
dwReserved
)
{
ProtocolHandler
*
This
=
impl_from_IInternetProtocolEx
(
iface
);
HRESULT
hres
;
BSTR
query
;
This
->
data
=
protocol_doc_str
;
This
->
size
=
strlen
(
This
->
data
);
...
...
@@ -5306,6 +5396,23 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
IUri_AddRef
(
This
->
uri
=
uri
);
This
->
ptr
=
This
->
data
;
hres
=
IUri_GetQuery
(
uri
,
&
query
);
if
(
hres
==
S_OK
)
{
if
(
!
wcscmp
(
query
,
L"?delay_with_signal"
))
{
if
(
delay_with_signal_handler
)
{
ProtocolHandler
*
delayed
=
delay_with_signal_handler
;
delay_with_signal_handler
=
NULL
;
SetEvent
(
delayed
->
delay_event
);
This
->
delay
=
30
;
}
else
{
delay_with_signal_handler
=
This
;
This
->
delay_event
=
CreateEventW
(
NULL
,
FALSE
,
FALSE
,
NULL
);
ok
(
This
->
delay_event
!=
NULL
,
"CreateEvent failed: %08lx
\n
"
,
GetLastError
());
}
}
SysFreeString
(
query
);
}
IInternetProtocolEx_AddRef
(
&
This
->
IInternetProtocolEx_iface
);
QueueUserWorkItem
(
async_switch_proc
,
This
,
0
);
return
E_PENDING
;
...
...
@@ -6008,6 +6115,95 @@ done:
IHTMLDocument2_Release
(
doc
[
1
]);
}
static
void
test_sync_xhr_events
(
const
char
*
doc_str
)
{
IHTMLXMLHttpRequest
*
xhr
[
2
];
IHTMLDocument2
*
doc
[
2
];
IHTMLDocument6
*
doc6
;
VARIANT
var
,
vempty
;
HRESULT
hres
;
unsigned
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
doc
);
i
++
)
doc
[
i
]
=
create_document_with_origin
(
doc_str
);
document_mode
=
0
;
V_VT
(
&
vempty
)
=
VT_EMPTY
;
hres
=
IHTMLDocument2_QueryInterface
(
doc
[
0
],
&
IID_IHTMLDocument6
,
(
void
**
)
&
doc6
);
if
(
SUCCEEDED
(
hres
))
{
hres
=
IHTMLDocument6_get_documentMode
(
doc6
,
&
var
);
ok
(
hres
==
S_OK
,
"get_documentMode failed: %08lx
\n
"
,
hres
);
ok
(
V_VT
(
&
var
)
==
VT_R4
,
"V_VT(documentMode) = %u
\n
"
,
V_VT
(
&
var
));
document_mode
=
V_R4
(
&
var
);
IHTMLDocument6_Release
(
doc6
);
}
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
doc
);
i
++
)
{
IHTMLXMLHttpRequestFactory
*
ctor
;
IHTMLWindow5
*
window5
;
IHTMLWindow2
*
window
;
BSTR
bstr
,
method
;
hres
=
IHTMLDocument2_get_parentWindow
(
doc
[
i
],
&
window
);
ok
(
hres
==
S_OK
,
"[%u] get_parentWindow failed: %08lx
\n
"
,
i
,
hres
);
ok
(
window
!=
NULL
,
"[%u] window == NULL
\n
"
,
i
);
hres
=
IHTMLWindow2_QueryInterface
(
window
,
&
IID_IHTMLWindow5
,
(
void
**
)
&
window5
);
ok
(
hres
==
S_OK
,
"[%u] Could not get IHTMLWindow5: %08lx
\n
"
,
i
,
hres
);
IHTMLWindow2_Release
(
window
);
hres
=
IHTMLWindow5_get_XMLHttpRequest
(
window5
,
&
var
);
ok
(
hres
==
S_OK
,
"[%u] get_XMLHttpRequest failed: %08lx
\n
"
,
i
,
hres
);
ok
(
V_VT
(
&
var
)
==
VT_DISPATCH
,
"[%u] V_VT(XMLHttpRequest) == %d
\n
"
,
i
,
V_VT
(
&
var
));
ok
(
V_DISPATCH
(
&
var
)
!=
NULL
,
"[%u] V_DISPATCH(XMLHttpRequest) == NULL
\n
"
,
i
);
IHTMLWindow5_Release
(
window5
);
hres
=
IDispatch_QueryInterface
(
V_DISPATCH
(
&
var
),
&
IID_IHTMLXMLHttpRequestFactory
,
(
void
**
)
&
ctor
);
ok
(
hres
==
S_OK
,
"[%u] Could not get IHTMLXMLHttpRequestFactory: %08lx
\n
"
,
i
,
hres
);
IDispatch_Release
(
V_DISPATCH
(
&
var
));
hres
=
IHTMLXMLHttpRequestFactory_create
(
ctor
,
&
xhr
[
i
]);
ok
(
hres
==
S_OK
,
"[%u] create failed: %08lx
\n
"
,
i
,
hres
);
IHTMLXMLHttpRequestFactory_Release
(
ctor
);
V_VT
(
&
var
)
=
VT_BOOL
;
V_BOOL
(
&
var
)
=
i
?
VARIANT_FALSE
:
VARIANT_TRUE
;
method
=
SysAllocString
(
L"GET"
);
bstr
=
SysAllocString
(
L"blank.html?delay_with_signal"
);
hres
=
IHTMLXMLHttpRequest_open
(
xhr
[
i
],
method
,
bstr
,
var
,
vempty
,
vempty
);
ok
(
hres
==
S_OK
,
"[%u] open failed: %08lx
\n
"
,
i
,
hres
);
SysFreeString
(
method
);
SysFreeString
(
bstr
);
V_VT
(
&
var
)
=
VT_DISPATCH
;
V_DISPATCH
(
&
var
)
=
(
IDispatch
*
)(
i
?
&
sync_xhr_obj
:
&
async_xhr_obj
);
hres
=
IHTMLXMLHttpRequest_put_onreadystatechange
(
xhr
[
i
],
var
);
ok
(
hres
==
S_OK
,
"[%u] put_onreadystatechange failed: %08lx
\n
"
,
i
,
hres
);
}
/* async xhr */
hres
=
IHTMLXMLHttpRequest_send
(
xhr
[
0
],
vempty
);
ok
(
hres
==
S_OK
,
"async xhr send failed: %08lx
\n
"
,
hres
);
/* sync xhr */
SET_EXPECT
(
sync_xhr_done
);
hres
=
IHTMLXMLHttpRequest_send
(
xhr
[
1
],
vempty
);
ok
(
hres
==
S_OK
,
"sync xhr send failed: %08lx
\n
"
,
hres
);
CHECK_CALLED
(
sync_xhr_done
);
SET_EXPECT
(
async_xhr_done
);
pump_msgs
(
&
called_async_xhr_done
);
CHECK_CALLED
(
async_xhr_done
);
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
xhr
);
i
++
)
IHTMLXMLHttpRequest_Release
(
xhr
[
i
]);
set_client_site
(
doc
[
0
],
FALSE
);
set_client_site
(
doc
[
1
],
FALSE
);
IHTMLDocument2_Release
(
doc
[
0
]);
IHTMLDocument2_Release
(
doc
[
1
]);
}
static
BOOL
check_ie
(
void
)
{
IHTMLDocument2
*
doc
;
...
...
@@ -6069,8 +6265,11 @@ START_TEST(events)
test_empty_document
();
test_storage_events
(
empty_doc_str
);
if
(
is_ie9plus
)
test_sync_xhr_events
(
empty_doc_str
);
if
(
is_ie9plus
)
{
test_storage_events
(
empty_doc_ie9_str
);
test_sync_xhr_events
(
empty_doc_ie9_str
);
}
DestroyWindow
(
container_hwnd
);
}
else
{
...
...
dlls/mshtml/tests/rsrc.rc
View file @
6a31e4ac
...
...
@@ -88,6 +88,9 @@ doc_with_prop_ie9.html HTML "doc_with_prop_ie9.html"
/* @makedep: iframe.html */
iframe.html HTML "iframe.html"
/* @makedep: xhr_iframe.html */
xhr_iframe.html HTML "xhr_iframe.html"
/* For res: protocol test: */
/* @makedep: jstest.html */
...
...
dlls/mshtml/tests/script.c
View file @
6a31e4ac
...
...
@@ -3675,6 +3675,7 @@ typedef struct {
IInternetProtocolSink
*
sink
;
BINDINFO
bind_info
;
HANDLE
delay_event
;
BSTR
content_type
;
IStream
*
stream
;
char
*
data
;
...
...
@@ -3685,11 +3686,16 @@ typedef struct {
IUri
*
uri
;
}
ProtocolHandler
;
static
ProtocolHandler
*
delay_with_signal_handler
;
static
DWORD
WINAPI
delay_proc
(
void
*
arg
)
{
PROTOCOLDATA
protocol_data
=
{
PI_FORCE_ASYNC
};
ProtocolHandler
*
protocol_handler
=
arg
;
if
(
protocol_handler
->
delay_event
)
WaitForSingleObject
(
protocol_handler
->
delay_event
,
INFINITE
);
else
Sleep
(
protocol_handler
->
delay
);
protocol_handler
->
delay
=
-
1
;
IInternetProtocolSink_Switch
(
protocol_handler
->
sink
,
&
protocol_data
);
...
...
@@ -3735,7 +3741,7 @@ static void report_data(ProtocolHandler *This)
IHttpNegotiate_Release
(
http_negotiate
);
if
(
This
->
delay
)
{
if
(
This
->
delay
||
This
->
delay_event
)
{
IInternetProtocolEx_AddRef
(
&
This
->
IInternetProtocolEx_iface
);
QueueUserWorkItem
(
delay_proc
,
This
,
0
);
return
;
...
...
@@ -3880,6 +3886,8 @@ static ULONG WINAPI Protocol_Release(IInternetProtocolEx *iface)
LONG
ref
=
InterlockedDecrement
(
&
This
->
ref
);
if
(
!
ref
)
{
if
(
This
->
delay_event
)
CloseHandle
(
This
->
delay_event
);
if
(
This
->
sink
)
IInternetProtocolSink_Release
(
This
->
sink
);
if
(
This
->
stream
)
...
...
@@ -4061,8 +4069,20 @@ static HRESULT WINAPI ProtocolEx_StartEx(IInternetProtocolEx *iface, IUri *uri,
hres
=
IUri_GetQuery
(
uri
,
&
query
);
if
(
SUCCEEDED
(
hres
))
{
if
(
!
lstrcmpW
(
query
,
L"?delay"
))
if
(
!
wcscmp
(
query
,
L"?delay"
))
This
->
delay
=
1000
;
else
if
(
!
wcscmp
(
query
,
L"?delay_with_signal"
))
{
if
(
delay_with_signal_handler
)
{
ProtocolHandler
*
delayed
=
delay_with_signal_handler
;
delay_with_signal_handler
=
NULL
;
SetEvent
(
delayed
->
delay_event
);
This
->
delay
=
30
;
}
else
{
delay_with_signal_handler
=
This
;
This
->
delay_event
=
CreateEventW
(
NULL
,
FALSE
,
FALSE
,
NULL
);
ok
(
This
->
delay_event
!=
NULL
,
"CreateEvent failed: %08lx
\n
"
,
GetLastError
());
}
}
else
if
(
!
wcsncmp
(
query
,
L"?content-type="
,
sizeof
(
"?content-type="
)
-
1
))
This
->
content_type
=
SysAllocString
(
query
+
sizeof
(
"?content-type="
)
-
1
);
SysFreeString
(
query
);
...
...
dlls/mshtml/tests/xhr.js
View file @
6a31e4ac
...
...
@@ -76,6 +76,145 @@ function test_xhr() {
xhr
.
send
(
xml
);
}
function
test_sync_xhr
()
{
var
async_xhr
,
async_xhr2
,
sync_xhr
,
sync_xhr_in_async
,
sync_xhr_nested
,
a
=
[
0
];
var
async_xhr_clicked
=
false
,
doc_dblclicked
=
false
;
function
onmsg
(
e
)
{
a
.
push
(
"msg"
+
e
.
data
);
}
document
.
ondblclick
=
function
()
{
doc_dblclicked
=
true
;
};
window
.
addEventListener
(
"message"
,
onmsg
);
window
.
postMessage
(
"1"
,
"*"
);
window
.
setTimeout
(
function
()
{
a
.
push
(
"timeout"
);
},
0
);
window
.
postMessage
(
"2"
,
"*"
);
a
.
push
(
1
);
async_xhr
=
new
XMLHttpRequest
();
async_xhr
.
open
(
"POST"
,
"echo.php"
,
true
);
async_xhr
.
onreadystatechange
=
function
()
{
if
(
async_xhr
.
readyState
<
3
)
return
;
a
.
push
(
"async_xhr("
+
async_xhr
.
readyState
+
")"
);
ok
(
async_xhr2
.
readyState
===
1
,
"async_xhr2.readyState = "
+
async_xhr2
.
readyState
);
if
(
async_xhr
.
readyState
==
4
)
{
window
.
postMessage
(
"_async"
,
"*"
);
sync_xhr_in_async
=
new
XMLHttpRequest
();
sync_xhr_in_async
.
open
(
"POST"
,
"echo.php"
,
false
);
sync_xhr_in_async
.
onreadystatechange
=
function
()
{
if
(
sync_xhr_in_async
.
readyState
==
4
)
a
.
push
(
"sync_xhr_in_async"
);
};
sync_xhr_in_async
.
setRequestHeader
(
"X-Test"
,
"True"
);
sync_xhr_in_async
.
send
(
"sync_in_async"
);
}
};
async_xhr
.
addEventListener
(
"click"
,
function
()
{
async_xhr_clicked
=
true
;
});
async_xhr
.
setRequestHeader
(
"X-Test"
,
"True"
);
async_xhr
.
send
(
"1234"
);
a
.
push
(
2
);
async_xhr2
=
new
XMLHttpRequest
();
async_xhr2
.
open
(
"POST"
,
"echo.php?delay_with_signal"
,
true
);
async_xhr2
.
onreadystatechange
=
function
()
{
if
(
async_xhr2
.
readyState
<
3
)
return
;
a
.
push
(
"async_xhr2("
+
async_xhr2
.
readyState
+
")"
);
ok
(
async_xhr
.
readyState
===
4
,
"async_xhr.readyState = "
+
async_xhr
.
readyState
);
};
async_xhr2
.
setRequestHeader
(
"X-Test"
,
"True"
);
async_xhr2
.
send
(
"foobar"
);
a
.
push
(
3
);
sync_xhr
=
new
XMLHttpRequest
();
sync_xhr
.
open
(
"POST"
,
"echo.php?delay_with_signal"
,
false
);
sync_xhr
.
onreadystatechange
=
function
()
{
a
.
push
(
"sync_xhr("
+
sync_xhr
.
readyState
+
")"
);
ok
(
async_xhr
.
readyState
===
1
,
"async_xhr.readyState in sync_xhr handler = "
+
async_xhr
.
readyState
);
ok
(
async_xhr2
.
readyState
===
1
,
"async_xhr2.readyState in sync_xhr handler = "
+
async_xhr2
.
readyState
);
if
(
sync_xhr
.
readyState
<
4
)
return
;
window
.
setTimeout
(
function
()
{
a
.
push
(
"timeout_sync"
);
},
0
);
window
.
postMessage
(
"_sync"
,
"*"
);
sync_xhr_nested
=
new
XMLHttpRequest
();
sync_xhr_nested
.
open
(
"POST"
,
"echo.php"
,
false
);
sync_xhr_nested
.
onreadystatechange
=
function
()
{
a
.
push
(
"nested("
+
sync_xhr_nested
.
readyState
+
")"
);
if
(
sync_xhr_nested
.
readyState
==
4
)
{
window
.
setTimeout
(
function
()
{
a
.
push
(
"timeout_nested"
);
},
0
);
window
.
postMessage
(
"_nested"
,
"*"
);
var
e
=
document
.
createEvent
(
"Event"
);
e
.
initEvent
(
"click"
,
true
,
false
);
ok
(
async_xhr_clicked
===
false
,
"async_xhr click fired before dispatch"
);
async_xhr
.
dispatchEvent
(
e
);
ok
(
async_xhr_clicked
===
true
,
"async_xhr click not fired immediately"
);
if
(
document
.
fireEvent
)
{
ok
(
doc_dblclicked
===
false
,
"document dblclick fired before dispatch"
);
document
.
fireEvent
(
"ondblclick"
,
document
.
createEventObject
());
ok
(
doc_dblclicked
===
true
,
"document dblclick not fired immediately"
);
}
}
};
sync_xhr_nested
.
setRequestHeader
(
"X-Test"
,
"True"
);
sync_xhr_nested
.
send
(
"nested"
);
};
sync_xhr
.
setRequestHeader
(
"X-Test"
,
"True"
);
sync_xhr
.
send
(
"abcd"
);
a
.
push
(
4
);
window
.
setTimeout
(
function
()
{
var
r
=
a
.
join
(
","
);
todo_wine_if
(
document
.
documentMode
<
10
).
ok
(
r
===
"0,1,2,3,"
+
(
document
.
documentMode
<
10
?
"sync_xhr(1),sync_xhr(2),sync_xhr(3),"
:
""
)
+
"sync_xhr(4),"
+
(
document
.
documentMode
<
10
?
"nested(1),nested(2),nested(3),"
:
""
)
+
"nested(4),4,async_xhr(3),async_xhr(4),sync_xhr_in_async,async_xhr2(3),async_xhr2(4),"
+
"msg1,msg2,msg_sync,msg_nested,msg_async,timeout,timeout_sync,timeout_nested"
,
"unexpected order: "
+
r
);
window
.
removeEventListener
(
"message"
,
onmsg
);
document
.
ondblclick
=
null
;
a
=
[
0
];
// Events dispatched to other iframes are not blocked by a send() in another context,
// except for async XHR events (which are a special case again), messages, and timeouts.
var
iframe
=
document
.
createElement
(
"iframe"
),
iframe2
=
document
.
createElement
(
"iframe"
);
iframe
.
onload
=
function
()
{
iframe2
.
onload
=
function
()
{
function
onmsg
(
e
)
{
a
.
push
(
e
.
data
);
if
(
e
.
data
===
"echo"
)
iframe2
.
contentWindow
.
postMessage
(
"sync_xhr"
,
"*"
);
};
window
.
setTimeout
(
function
()
{
var
r
=
a
.
join
(
","
);
ok
(
r
===
"0,1,async_xhr,echo,sync_xhr(pre-send),sync_xhr(DONE),sync_xhr,async_xhr(DONE)"
,
"[iframes 1] unexpected order: "
+
r
);
a
=
[
0
];
window
.
setTimeout
(
function
()
{
var
r
=
a
.
join
(
","
);
ok
(
r
===
"0,1,echo,blank(DONE),sync_xhr(pre-send),sync_xhr(DONE),sync_xhr"
,
"[iframes 2] unexpected order: "
+
r
);
window
.
removeEventListener
(
"message"
,
onmsg
);
next_test
();
},
0
);
iframe
.
onload
=
function
()
{
a
.
push
(
"blank(DONE)"
);
};
iframe
.
src
=
"blank.html?delay_with_signal"
;
iframe2
.
contentWindow
.
postMessage
(
"echo"
,
"*"
);
a
.
push
(
1
);
},
0
);
window
.
addEventListener
(
"message"
,
onmsg
);
iframe
.
contentWindow
.
postMessage
(
"async_xhr"
,
"*"
);
iframe2
.
contentWindow
.
postMessage
(
"echo"
,
"*"
);
a
.
push
(
1
);
};
iframe2
.
src
=
"xhr_iframe.html"
;
document
.
body
.
appendChild
(
iframe2
);
};
iframe
.
src
=
"xhr_iframe.html"
;
document
.
body
.
appendChild
(
iframe
);
},
0
);
}
function
test_content_types
()
{
var
xhr
=
new
XMLHttpRequest
(),
types
,
i
=
0
,
override
=
false
;
var
v
=
document
.
documentMode
;
...
...
@@ -291,6 +430,7 @@ function test_response() {
var
tests
=
[
test_xhr
,
test_sync_xhr
,
test_content_types
,
test_abort
,
test_timeout
,
...
...
dlls/mshtml/tests/xhr_iframe.html
0 → 100644
View file @
6a31e4ac
<html><head><script
type=
"text/javascript"
>
window
.
onmessage
=
function
(
e
)
{
if
(
e
.
data
===
"echo"
)
parent
.
postMessage
(
"echo"
,
"*"
);
else
if
(
e
.
data
===
"async_xhr"
)
{
var
async_xhr
=
new
XMLHttpRequest
();
async_xhr
.
open
(
"POST"
,
"echo.php?delay_with_signal"
,
true
);
async_xhr
.
onreadystatechange
=
function
()
{
if
(
async_xhr
.
readyState
==
4
)
parent
.
postMessage
(
"async_xhr(DONE)"
,
"*"
);
};
async_xhr
.
setRequestHeader
(
"X-Test"
,
"True"
);
async_xhr
.
send
(
"foo"
);
parent
.
postMessage
(
"async_xhr"
,
"*"
);
}
else
if
(
e
.
data
===
"sync_xhr"
)
{
var
sync_xhr
=
new
XMLHttpRequest
();
sync_xhr
.
open
(
"POST"
,
"echo.php?delay_with_signal"
,
false
);
sync_xhr
.
onreadystatechange
=
function
()
{
if
(
sync_xhr
.
readyState
==
4
)
parent
.
postMessage
(
"sync_xhr(DONE)"
,
"*"
);
};
sync_xhr
.
setRequestHeader
(
"X-Test"
,
"True"
);
parent
.
postMessage
(
"sync_xhr(pre-send)"
,
"*"
);
sync_xhr
.
send
(
"bar"
);
parent
.
postMessage
(
"sync_xhr"
,
"*"
);
}
}
</script><body></body></html>
dlls/mshtml/tests/xmlhttprequest.c
View file @
6a31e4ac
...
...
@@ -673,18 +673,12 @@ static void test_sync_xhr(IHTMLDocument2 *doc, const WCHAR *xml_url, const WCHAR
SET_EXPECT
(
xmlhttprequest_onreadystatechange_opened
);
hres
=
IHTMLXMLHttpRequest_open
(
xhr
,
method
,
url
,
vbool
,
vempty
,
vempty
);
todo_wine
ok
(
hres
==
S_OK
,
"open failed: %08lx
\n
"
,
hres
);
/* Gecko 30+ only supports async */
todo_wine
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_opened
);
ok
(
hres
==
S_OK
,
"open failed: %08lx
\n
"
,
hres
);
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_opened
);
SysFreeString
(
method
);
SysFreeString
(
url
);
if
(
FAILED
(
hres
))
{
IHTMLXMLHttpRequest_Release
(
xhr
);
xhr
=
NULL
;
return
;
}
text
=
(
BSTR
)
0xdeadbeef
;
hres
=
IHTMLXMLHttpRequest_getAllResponseHeaders
(
xhr
,
&
text
);
ok
(
hres
==
E_FAIL
,
"got %08lx
\n
"
,
hres
);
...
...
@@ -718,11 +712,11 @@ static void test_sync_xhr(IHTMLDocument2 *doc, const WCHAR *xml_url, const WCHAR
loading_cnt
=
0
;
hres
=
IHTMLXMLHttpRequest_send
(
xhr
,
vempty
);
ok
(
hres
==
S_OK
,
"send failed: %08lx
\n
"
,
hres
);
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_opened
);
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_headers_received
);
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_loading
);
todo_wine
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_opened
);
todo_wine
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_headers_received
);
todo_wine
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_loading
);
CHECK_CALLED
(
xmlhttprequest_onreadystatechange_done
);
ok
(
loading_cnt
==
1
,
"loading_cnt = %d
\n
"
,
loading_cnt
);
todo_wine
ok
(
loading_cnt
==
1
,
"loading_cnt = %d
\n
"
,
loading_cnt
);
text
=
NULL
;
hres
=
IHTMLXMLHttpRequest_getResponseHeader
(
xhr
,
content_type
,
&
text
);
...
...
dlls/mshtml/xmlhttprequest.c
View file @
6a31e4ac
...
...
@@ -137,10 +137,16 @@ struct HTMLXMLHttpRequest {
IWineXMLHttpRequestPrivate
IWineXMLHttpRequestPrivate_iface
;
IProvideClassInfo2
IProvideClassInfo2_iface
;
LONG
ref
;
LONG
task_magic
;
LONG
ready_state
;
response_type_t
response_type
;
BOOLEAN
synchronous
;
DWORD
magic
;
DWORD
pending_events_magic
;
HTMLInnerWindow
*
window
;
nsIXMLHttpRequest
*
nsxhr
;
XMLHttpReqEventListener
*
event_listener
;
DOMEvent
*
pending_progress_event
;
};
static
void
detach_xhr_event_listener
(
XMLHttpReqEventListener
*
event_listener
)
...
...
@@ -166,6 +172,157 @@ static void detach_xhr_event_listener(XMLHttpReqEventListener *event_listener)
nsIDOMEventListener_Release
(
&
event_listener
->
nsIDOMEventListener_iface
);
}
static
void
synthesize_pending_events
(
HTMLXMLHttpRequest
*
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
);
}
IHTMLXMLHttpRequest_AddRef
(
&
xhr
->
IHTMLXMLHttpRequest_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:
IHTMLXMLHttpRequest_Release
(
&
xhr
->
IHTMLXMLHttpRequest_iface
);
}
static
nsresult
sync_xhr_send
(
HTMLXMLHttpRequest
*
xhr
,
nsIVariant
*
nsbody
)
{
thread_data_t
*
thread_data
=
get_thread_data
(
TRUE
);
HTMLXMLHttpRequest
*
prev_blocking_xhr
;
HTMLInnerWindow
*
window
=
xhr
->
window
;
nsresult
nsres
;
if
(
!
thread_data
)
return
NS_ERROR_OUT_OF_MEMORY
;
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
*/
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
;
window
->
base
.
outer_window
->
readystate_locked
--
;
if
(
!--
window
->
blocking_depth
)
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
;
HTMLXMLHttpRequest
*
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
)
{
...
...
@@ -221,23 +378,92 @@ static nsrefcnt NSAPI XMLHttpReqEventListener_Release(nsIDOMEventListener *iface
static
nsresult
NSAPI
XMLHttpReqEventListener_HandleEvent
(
nsIDOMEventListener
*
iface
,
nsIDOMEvent
*
nsevent
)
{
XMLHttpReqEventListener
*
This
=
impl_from_nsIDOMEventListener
(
iface
);
UINT16
ready_state
;
HTMLXMLHttpRequest
*
blocking_xhr
=
NULL
;
thread_data_t
*
thread_data
;
LONG
ready_state
;
DOMEvent
*
event
;
HRESULT
hres
;
UINT16
val
;
TRACE
(
"(%p)
\n
"
,
This
);
if
(
!
This
->
xhr
)
return
NS_OK
;
if
(
NS_SUCCEEDED
(
nsIXMLHttpRequest_GetReadyState
(
This
->
xhr
->
nsxhr
,
&
ready_state
)))
This
->
xhr
->
ready_state
=
ready_state
;
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
;
hres
=
create_event_from_nsevent
(
nsevent
,
dispex_compat_mode
(
&
This
->
xhr
->
event_target
.
dispex
),
&
event
);
if
(
SUCCEEDED
(
hres
)
){
dispatch_event
(
&
This
->
xhr
->
event_target
,
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
;
}
}
This
->
xhr
->
ready_state
=
ready_state
;
dispatch_event
(
&
This
->
xhr
->
event_target
,
event
);
IDOMEvent_Release
(
&
event
->
IDOMEvent_iface
);
return
NS_OK
;
}
...
...
@@ -299,7 +525,11 @@ static ULONG WINAPI HTMLXMLHttpRequest_Release(IHTMLXMLHttpRequest *iface)
TRACE
(
"(%p) ref=%ld
\n
"
,
This
,
ref
);
if
(
!
ref
)
{
remove_target_tasks
(
This
->
task_magic
);
detach_xhr_event_listener
(
This
->
event_listener
);
if
(
This
->
pending_progress_event
)
IDOMEvent_Release
(
&
This
->
pending_progress_event
->
IDOMEvent_iface
);
IHTMLWindow2_Release
(
&
This
->
window
->
base
.
IHTMLWindow2_iface
);
release_event_target
(
&
This
->
event_target
);
release_dispex
(
&
This
->
event_target
.
dispex
);
nsIXMLHttpRequest_Release
(
This
->
nsxhr
);
...
...
@@ -510,14 +740,17 @@ static HRESULT WINAPI HTMLXMLHttpRequest_get_onreadystatechange(IHTMLXMLHttpRequ
static
HRESULT
WINAPI
HTMLXMLHttpRequest_abort
(
IHTMLXMLHttpRequest
*
iface
)
{
HTMLXMLHttpRequest
*
This
=
impl_from_IHTMLXMLHttpRequest
(
iface
);
DWORD
prev_magic
=
This
->
magic
;
UINT16
ready_state
;
nsresult
nsres
;
TRACE
(
"(%p)->()
\n
"
,
This
);
This
->
magic
++
;
nsres
=
nsIXMLHttpRequest_SlowAbort
(
This
->
nsxhr
);
if
(
NS_FAILED
(
nsres
))
{
ERR
(
"nsIXMLHttpRequest_SlowAbort failed: %08lx
\n
"
,
nsres
);
This
->
magic
=
prev_magic
;
return
E_FAIL
;
}
...
...
@@ -553,9 +786,11 @@ static HRESULT HTMLXMLHttpRequest_open_hook(DispatchEx *dispex, WORD flags,
static
HRESULT
WINAPI
HTMLXMLHttpRequest_open
(
IHTMLXMLHttpRequest
*
iface
,
BSTR
bstrMethod
,
BSTR
bstrUrl
,
VARIANT
varAsync
,
VARIANT
varUser
,
VARIANT
varPassword
)
{
HTMLXMLHttpRequest
*
This
=
impl_from_IHTMLXMLHttpRequest
(
iface
);
BOOLEAN
prev_synchronous
;
nsAString
user
,
password
;
nsACString
method
,
url
;
unsigned
opt_argc
=
1
;
DWORD
prev_magic
;
nsresult
nsres
;
HRESULT
hres
;
...
...
@@ -570,15 +805,6 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
}
}
/* 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.
*/
if
(
!
V_BOOL
(
&
varAsync
))
{
FIXME
(
"Synchronous request is not supported yet
\n
"
);
return
E_FAIL
;
}
hres
=
variant_to_nsastr
(
varUser
,
&
user
);
if
(
FAILED
(
hres
))
return
hres
;
...
...
@@ -602,6 +828,12 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
return
hres
;
}
/* Set this here, Gecko dispatches nested sync XHR readyState changes for OPENED (see HandleEvent) */
prev_magic
=
This
->
magic
;
prev_synchronous
=
This
->
synchronous
;
This
->
synchronous
=
!
V_BOOL
(
&
varAsync
);
This
->
magic
++
;
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
)
...
...
@@ -615,6 +847,8 @@ static HRESULT WINAPI HTMLXMLHttpRequest_open(IHTMLXMLHttpRequest *iface, BSTR b
if
(
NS_FAILED
(
nsres
))
{
ERR
(
"nsIXMLHttpRequest_Open failed: %08lx
\n
"
,
nsres
);
This
->
magic
=
prev_magic
;
This
->
synchronous
=
prev_synchronous
;
return
E_FAIL
;
}
...
...
@@ -651,13 +885,18 @@ static HRESULT WINAPI HTMLXMLHttpRequest_send(IHTMLXMLHttpRequest *iface, VARIAN
return
E_NOTIMPL
;
}
if
(
NS_SUCCEEDED
(
nsres
))
if
(
NS_SUCCEEDED
(
nsres
))
{
if
(
This
->
synchronous
)
nsres
=
sync_xhr_send
(
This
,
(
nsIVariant
*
)
nsbody
);
else
nsres
=
nsIXMLHttpRequest_Send
(
This
->
nsxhr
,
(
nsIVariant
*
)
nsbody
);
}
if
(
nsbody
)
nsIWritableVariant_Release
(
nsbody
);
if
(
NS_FAILED
(
nsres
))
{
ERR
(
"nsIXMLHttpRequest_Send failed: %08lx
\n
"
,
nsres
);
return
E_FAIL
;
return
map_nsresult
(
nsres
)
;
}
return
S_OK
;
...
...
@@ -1445,6 +1684,9 @@ static HRESULT WINAPI HTMLXMLHttpRequestFactory_create(IHTMLXMLHttpRequestFactor
}
ret
->
nsxhr
=
nsxhr
;
ret
->
window
=
This
->
window
;
ret
->
task_magic
=
get_task_target_magic
();
IHTMLWindow2_AddRef
(
&
This
->
window
->
base
.
IHTMLWindow2_iface
);
ret
->
IHTMLXMLHttpRequest_iface
.
lpVtbl
=
&
HTMLXMLHttpRequestVtbl
;
ret
->
IHTMLXMLHttpRequest2_iface
.
lpVtbl
=
&
HTMLXMLHttpRequest2Vtbl
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment