#define CONST_VTABLE
#include <wine/test.h>
+#include <wine/heap.h>
#include <stdarg.h>
#include <stdio.h>
DEFINE_EXPECT(Stream_Seek);
DEFINE_EXPECT(Stream_Read);
DEFINE_EXPECT(Redirect);
+DEFINE_EXPECT(outer_QI_test);
+DEFINE_EXPECT(Protocol_destructor);
static const WCHAR wszIndexHtml[] = {'i','n','d','e','x','.','h','t','m','l',0};
static const WCHAR index_url[] =
static DWORD prot_read, filter_state, http_post_test, thread_id;
static BOOL security_problem, test_async_req, impl_protex;
static BOOL async_read_pending, mimefilter_test, direct_read, wait_for_switch, emulate_prot, short_read, test_abort;
-static BOOL empty_file, no_mime, bind_from_cache, file_with_hash;
+static BOOL empty_file, no_mime, bind_from_cache, file_with_hash, reuse_protocol_thread;
enum {
STATE_CONNECTING,
BIND_TEST
} tested_protocol;
+typedef struct {
+ IUnknown IUnknown_inner;
+ IInternetProtocolEx IInternetProtocolEx_iface;
+ IInternetPriority IInternetPriority_iface;
+ IUnknown *outer;
+ LONG inner_ref;
+ LONG outer_ref;
+} Protocol;
+
+static Protocol *protocol_emul;
+
static const WCHAR protocol_names[][10] = {
{'f','i','l','e',0},
{'h','t','t','p',0},
static HRESULT WINAPI HttpSecurity_OnSecurityProblem(IHttpSecurity *iface, DWORD dwProblem)
{
- trace("Security problem: %u\n", dwProblem);
- ok(dwProblem == ERROR_INTERNET_SEC_CERT_REV_FAILED, "Expected ERROR_INTERNET_SEC_CERT_REV_FAILED got %u\n", dwProblem);
+ win_skip("Security problem: %u\n", dwProblem);
+ ok(dwProblem == ERROR_INTERNET_SEC_CERT_REV_FAILED || dwProblem == ERROR_INTERNET_INVALID_CA,
+ "Expected got %u security problem\n", dwProblem);
/* Only retry once */
if (security_problem)
return E_ABORT;
security_problem = TRUE;
+ if(dwProblem == ERROR_INTERNET_INVALID_CA)
+ return E_ABORT;
SET_EXPECT(BeginningTransaction);
return RPC_E_RETRY;
{
CHECK_EXPECT(ReportResult);
+ if(security_problem)
+ return S_OK;
+
if(tested_protocol == FTP_TEST)
ok(hrResult == E_PENDING || hrResult == S_OK, "hrResult = %08x, expected E_PENDING or S_OK\n", hrResult);
else
static IInternetBindInfo bind_info = { &bind_info_vtbl };
+static Protocol *impl_from_IInternetPriority(IInternetPriority *iface)
+{
+ return CONTAINING_RECORD(iface, Protocol, IInternetPriority_iface);
+}
+
static HRESULT WINAPI InternetPriority_QueryInterface(IInternetPriority *iface,
REFIID riid, void **ppv)
{
static ULONG WINAPI InternetPriority_AddRef(IInternetPriority *iface)
{
- return 2;
+ Protocol *This = impl_from_IInternetPriority(iface);
+ This->outer_ref++;
+ return IUnknown_AddRef(This->outer);
}
static ULONG WINAPI InternetPriority_Release(IInternetPriority *iface)
{
- return 1;
+ Protocol *This = impl_from_IInternetPriority(iface);
+ This->outer_ref--;
+ return IUnknown_Release(This->outer);
}
static HRESULT WINAPI InternetPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
InternetPriority_GetPriority
};
-static IInternetPriority InternetPriority = { &InternetPriorityVtbl };
-
static ULONG WINAPI Protocol_AddRef(IInternetProtocolEx *iface)
{
return 2;
return E_NOTIMPL;
}
-static HRESULT WINAPI ProtocolEmul_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
+static Protocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
{
- static const IID unknown_iid = {0x7daf9908,0x8415,0x4005,{0x95,0xae, 0xbd,0x27,0xf6,0xe3,0xdc,0x00}};
+ return CONTAINING_RECORD(iface, Protocol, IInternetProtocolEx_iface);
+}
- if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocol, riid)) {
- *ppv = iface;
- return S_OK;
- }
+static HRESULT WINAPI ProtocolEmul_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
+{
+ Protocol *This = impl_from_IInternetProtocolEx(iface);
- if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
- if(impl_protex) {
- *ppv = iface;
- return S_OK;
- }
- *ppv = NULL;
- return E_NOINTERFACE;
- }
+ static const IID unknown_iid = {0x7daf9908,0x8415,0x4005,{0x95,0xae, 0xbd,0x27,0xf6,0xe3,0xdc,0x00}};
+ static const IID unknown_iid2 = {0x5b7ebc0c,0xf630,0x4cea,{0x89,0xd3,0x5a,0xf0,0x38,0xed,0x05,0x5c}};
- if(IsEqualGUID(&IID_IInternetPriority, riid)) {
- *ppv = &InternetPriority;
+ /* FIXME: Why is it calling here instead of outer IUnknown? */
+ if(IsEqualGUID(riid, &IID_IInternetPriority)) {
+ *ppv = &This->IInternetPriority_iface;
+ IInternetPriority_AddRef(&This->IInternetPriority_iface);
return S_OK;
}
-
- if(IsEqualGUID(&IID_IWinInetInfo, riid)) {
- CHECK_EXPECT(QueryInterface_IWinInetInfo);
- *ppv = NULL;
- return E_NOINTERFACE;
- }
-
- if(IsEqualGUID(&IID_IWinInetHttpInfo, riid)) {
- CHECK_EXPECT(QueryInterface_IWinInetHttpInfo);
- *ppv = NULL;
- return E_NOINTERFACE;
- }
-
- if(!IsEqualGUID(riid, &unknown_iid)) /* IE10 */
+ if(!IsEqualGUID(riid, &unknown_iid) && !IsEqualGUID(riid, &unknown_iid2)) /* IE10 */
ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid));
*ppv = NULL;
return E_NOINTERFACE;
}
+static ULONG WINAPI ProtocolEmul_AddRef(IInternetProtocolEx *iface)
+{
+ Protocol *This = impl_from_IInternetProtocolEx(iface);
+ This->outer_ref++;
+ return IUnknown_AddRef(This->outer);
+}
+
+static ULONG WINAPI ProtocolEmul_Release(IInternetProtocolEx *iface)
+{
+ Protocol *This = impl_from_IInternetProtocolEx(iface);
+ This->outer_ref--;
+ return IUnknown_Release(This->outer);
+}
+
static DWORD WINAPI thread_proc(PVOID arg)
{
- BOOL redirect_only = redirect_on_continue;
+ BOOL redirect = redirect_on_continue;
HRESULT hres;
memset(&protocoldata, -1, sizeof(protocoldata));
- prot_state = 0;
+ while(1) {
+ prot_state = 0;
- SET_EXPECT(ReportProgress_FINDINGRESOURCE);
- hres = IInternetProtocolSink_ReportProgress(binding_sink,
- BINDSTATUS_FINDINGRESOURCE, hostW);
- CHECK_CALLED(ReportProgress_FINDINGRESOURCE);
- ok(hres == S_OK, "ReportProgress failed: %08x\n", hres);
+ SET_EXPECT(ReportProgress_FINDINGRESOURCE);
+ hres = IInternetProtocolSink_ReportProgress(binding_sink,
+ BINDSTATUS_FINDINGRESOURCE, hostW);
+ CHECK_CALLED(ReportProgress_FINDINGRESOURCE);
+ ok(hres == S_OK, "ReportProgress failed: %08x\n", hres);
- SET_EXPECT(ReportProgress_CONNECTING);
- hres = IInternetProtocolSink_ReportProgress(binding_sink,
- BINDSTATUS_CONNECTING, winehq_ipW);
- CHECK_CALLED(ReportProgress_CONNECTING);
- ok(hres == S_OK, "ReportProgress failed: %08x\n", hres);
+ SET_EXPECT(ReportProgress_CONNECTING);
+ hres = IInternetProtocolSink_ReportProgress(binding_sink,
+ BINDSTATUS_CONNECTING, winehq_ipW);
+ CHECK_CALLED(ReportProgress_CONNECTING);
+ ok(hres == S_OK, "ReportProgress failed: %08x\n", hres);
- SET_EXPECT(ReportProgress_SENDINGREQUEST);
- hres = IInternetProtocolSink_ReportProgress(binding_sink,
- BINDSTATUS_SENDINGREQUEST, NULL);
- CHECK_CALLED(ReportProgress_SENDINGREQUEST);
- ok(hres == S_OK, "ReportProgress failed: %08x\n", hres);
+ SET_EXPECT(ReportProgress_SENDINGREQUEST);
+ hres = IInternetProtocolSink_ReportProgress(binding_sink,
+ BINDSTATUS_SENDINGREQUEST, NULL);
+ CHECK_CALLED(ReportProgress_SENDINGREQUEST);
+ ok(hres == S_OK, "ReportProgress failed: %08x\n", hres);
- prot_state = 1;
- SET_EXPECT(Switch);
- hres = IInternetProtocolSink_Switch(binding_sink, &protocoldata);
- CHECK_CALLED(Switch);
- ok(hres == S_OK, "Switch failed: %08x\n", hres);
+ prot_state = 1;
+ SET_EXPECT(Switch);
+ hres = IInternetProtocolSink_Switch(binding_sink, &protocoldata);
+ CHECK_CALLED(Switch);
+ ok(hres == S_OK, "Switch failed: %08x\n", hres);
- if(redirect_only) {
- prot_state = 0;
- return 0;
+ if(!redirect)
+ break;
+ redirect = FALSE;
}
if(!short_read) {
IServiceProvider_Release(service_provider);
- CreateThread(NULL, 0, thread_proc, NULL, 0, &tid);
+ if(!reuse_protocol_thread)
+ CreateThread(NULL, 0, thread_proc, NULL, 0, &tid);
return;
}
if(redirect_on_continue) {
redirect_on_continue = FALSE;
+ reuse_protocol_thread = TRUE;
if(bindinfo_options & BINDINFO_OPTIONS_DISABLEAUTOREDIRECTS)
SET_EXPECT(Redirect);
SET_EXPECT(ReportProgress_REDIRECTING);
SET_EXPECT(Terminate);
+ SET_EXPECT(Protocol_destructor);
SET_EXPECT(QueryService_InternetProtocol);
SET_EXPECT(CreateInstance);
SET_EXPECT(ReportProgress_PROTOCOLCLASSID);
CHECK_CALLED(Redirect);
CHECK_CALLED(ReportProgress_REDIRECTING);
CHECK_CALLED(Terminate);
+ CHECK_CALLED(Protocol_destructor);
CHECK_CALLED(QueryService_InternetProtocol);
CHECK_CALLED(CreateInstance);
CHECK_CALLED(ReportProgress_PROTOCOLCLASSID);
static const IInternetProtocolExVtbl ProtocolVtbl = {
ProtocolEmul_QueryInterface,
- Protocol_AddRef,
- Protocol_Release,
+ ProtocolEmul_AddRef,
+ ProtocolEmul_Release,
ProtocolEmul_Start,
ProtocolEmul_Continue,
Protocol_Abort,
ProtocolEmul_StartEx
};
-static IInternetProtocolEx Protocol = { &ProtocolVtbl };
+static Protocol *impl_from_IUnknown(IUnknown *iface)
+{
+ return CONTAINING_RECORD(iface, Protocol, IUnknown_inner);
+}
+
+static HRESULT WINAPI ProtocolUnk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
+{
+ Protocol *This = impl_from_IUnknown(iface);
+
+ if(IsEqualGUID(&IID_IUnknown, riid)) {
+ trace("QI(IUnknown)\n");
+ *ppv = &This->IUnknown_inner;
+ }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
+ trace("QI(InternetProtocol)\n");
+ *ppv = &This->IInternetProtocolEx_iface;
+ }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
+ trace("QI(InternetProtocolEx)\n");
+ if(!impl_protex) {
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+ *ppv = &This->IInternetProtocolEx_iface;
+ }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
+ trace("QI(InternetPriority)\n");
+ *ppv = &This->IInternetPriority_iface;
+ }else if(IsEqualGUID(&IID_IWinInetInfo, riid)) {
+ trace("QI(IWinInetInfo)\n");
+ CHECK_EXPECT(QueryInterface_IWinInetInfo);
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }else if(IsEqualGUID(&IID_IWinInetHttpInfo, riid)) {
+ trace("QI(IWinInetHttpInfo)\n");
+ CHECK_EXPECT(QueryInterface_IWinInetHttpInfo);
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }else {
+ ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid));
+ *ppv = NULL;
+ return E_NOINTERFACE;
+ }
+
+ IUnknown_AddRef((IUnknown*)*ppv);
+ return S_OK;
+}
+
+static ULONG WINAPI ProtocolUnk_AddRef(IUnknown *iface)
+{
+ Protocol *This = impl_from_IUnknown(iface);
+ return ++This->inner_ref;
+}
+
+static ULONG WINAPI ProtocolUnk_Release(IUnknown *iface)
+{
+ Protocol *This = impl_from_IUnknown(iface);
+ LONG ref = --This->inner_ref;
+ if(!ref) {
+ /* IE9 is broken on redirects. It will cause -1 outer_ref on original protocol handler
+ * and 1 on redirected handler. */
+ ok(!This->outer_ref
+ || broken(test_redirect && (This->outer_ref == -1 || This->outer_ref == 1)),
+ "outer_ref = %d\n", This->outer_ref);
+ if(This->outer_ref)
+ trace("outer_ref %d\n", This->outer_ref);
+ CHECK_EXPECT(Protocol_destructor);
+ heap_free(This);
+ }
+ return ref;
+}
+
+static const IUnknownVtbl ProtocolUnkVtbl = {
+ ProtocolUnk_QueryInterface,
+ ProtocolUnk_AddRef,
+ ProtocolUnk_Release
+};
static HRESULT WINAPI MimeProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
{
static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *pOuter,
REFIID riid, void **ppv)
{
+ Protocol *ret;
+
CHECK_EXPECT(CreateInstance);
ok(pOuter == (IUnknown*)prot_bind_info, "pOuter != protocol_unk\n");
ok(IsEqualGUID(&IID_IUnknown, riid), "unexpected riid %s\n", wine_dbgstr_guid(riid));
ok(ppv != NULL, "ppv == NULL\n");
- *ppv = &Protocol;
+ ret = heap_alloc(sizeof(*ret));
+ ret->IUnknown_inner.lpVtbl = &ProtocolUnkVtbl;
+ ret->IInternetProtocolEx_iface.lpVtbl = &ProtocolVtbl;
+ ret->IInternetPriority_iface.lpVtbl = &InternetPriorityVtbl;
+ ret->outer = pOuter;
+ ret->inner_ref = 1;
+ ret->outer_ref = 0;
+
+ protocol_emul = ret;
+ *ppv = &ret->IUnknown_inner;
return S_OK;
}
empty_file = (flags & TEST_EMPTY) != 0;
bind_from_cache = (flags & TEST_FROMCACHE) != 0;
file_with_hash = FALSE;
+ security_problem = FALSE;
+ reuse_protocol_thread = FALSE;
bindinfo_options = 0;
if(flags & TEST_DISABLEAUTOREDIRECT)
SET_EXPECT(SetPriority);
SET_EXPECT(Start);
+ trace("Start >\n");
expect_hrResult = S_OK;
hres = IInternetProtocol_Start(protocol, test_url, &protocol_sink, &bind_info, 0, 0);
ok(hres == S_OK, "Start failed: %08x\n", hres);
+ trace("Start <\n");
CHECK_CALLED(QueryService_InternetProtocol);
CHECK_CALLED(CreateInstance);
ok(hres == S_OK, "Terminate failed: %08x\n", hres);
CHECK_CALLED(Terminate);
+ ok(protocol_emul->outer_ref == 0, "protocol_outer_ref = %u\n", protocol_emul->outer_ref);
+
SET_EXPECT(Continue);
hres = IInternetProtocolSink_Switch(binding_sink, &protocoldata);
ok(hres == S_OK, "Switch failed: %08x\n", hres);
CHECK_CALLED(Continue);
+ SET_EXPECT(Read);
+ read = 0xdeadbeef;
+ hres = IInternetProtocol_Read(protocol, expect_pv = buf, sizeof(buf), &read);
+ todo_wine
+ ok(hres == E_ABORT, "Read failed: %08x\n", hres);
+ todo_wine
+ ok(read == 0, "read = %d\n", read);
+ todo_wine
+ CHECK_NOT_CALLED(Read);
+
hres = IInternetProtocolSink_ReportProgress(binding_sink,
BINDSTATUS_CACHEFILENAMEAVAILABLE, expect_wsz = emptyW);
ok(hres == S_OK, "ReportProgress(BINDSTATUS_CACHEFILENAMEAVAILABLE) failed: %08x\n", hres);
IInternetProtocolSink_Release(binding_sink);
IInternetPriority_Release(priority);
IInternetBindInfo_Release(prot_bind_info);
+
+ SET_EXPECT(Protocol_destructor);
IInternetProtocol_Release(protocol);
+ CHECK_CALLED(Protocol_destructor);
hres = IInternetSession_CreateBinding(session, NULL, test_url, NULL, NULL, &protocol, 0);
ok(hres == S_OK, "CreateBinding failed: %08x\n", hres);
IInternetProtocol_Release(filtered_protocol);
IInternetBindInfo_Release(prot_bind_info);
IInternetProtocolSink_Release(binding_sink);
+
+ SET_EXPECT(Protocol_destructor);
ref = IInternetProtocol_Release(protocol);
ok(!ref, "ref=%u, expected 0\n", ref);
+ CHECK_CALLED(Protocol_destructor);
if(test_flags & TEST_EMULATEPROT) {
hres = IInternetSession_UnregisterNameSpace(session, &ClassFactory, protocol_names[prot]);
IInternetSession_Release(session);
}
+static const IID outer_test_iid = {0xabcabc00,0,0,{0,0,0,0,0,0,0,0x66}};
+
+static HRESULT WINAPI outer_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
+{
+ if(IsEqualGUID(riid, &outer_test_iid)) {
+ CHECK_EXPECT(outer_QI_test);
+ *ppv = (IUnknown*)0xdeadbeef;
+ return S_OK;
+ }
+ ok(0, "unexpected call %s\n", wine_dbgstr_guid(riid));
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI outer_AddRef(IUnknown *iface)
+{
+ return 2;
+}
+
+static ULONG WINAPI outer_Release(IUnknown *iface)
+{
+ return 1;
+}
+
+static const IUnknownVtbl outer_vtbl = {
+ outer_QueryInterface,
+ outer_AddRef,
+ outer_Release
+};
+
+static void test_com_aggregation(const CLSID *clsid)
+{
+ IUnknown outer = { &outer_vtbl };
+ IClassFactory *class_factory;
+ IUnknown *unk, *unk2, *unk3;
+ HRESULT hres;
+
+ hres = CoGetClassObject(clsid, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory, (void**)&class_factory);
+ ok(hres == S_OK, "CoGetClassObject failed: %08x\n", hres);
+
+ hres = IClassFactory_CreateInstance(class_factory, &outer, &IID_IUnknown, (void**)&unk);
+ ok(hres == S_OK, "CreateInstance returned: %08x\n", hres);
+
+ hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocol, (void**)&unk2);
+ ok(hres == S_OK, "Could not get IDispatch iface: %08x\n", hres);
+
+ SET_EXPECT(outer_QI_test);
+ hres = IUnknown_QueryInterface(unk2, &outer_test_iid, (void**)&unk3);
+ CHECK_CALLED(outer_QI_test);
+ ok(hres == S_OK, "Could not get IInternetProtocol iface: %08x\n", hres);
+ ok(unk3 == (IUnknown*)0xdeadbeef, "unexpected unk2\n");
+
+ IUnknown_Release(unk2);
+ IUnknown_Release(unk);
+
+ unk = (void*)0xdeadbeef;
+ hres = IClassFactory_CreateInstance(class_factory, &outer, &IID_IInternetProtocol, (void**)&unk);
+ ok(hres == CLASS_E_NOAGGREGATION, "CreateInstance returned: %08x\n", hres);
+ ok(!unk, "unk = %p\n", unk);
+
+ IClassFactory_Release(class_factory);
+}
+
START_TEST(protocol)
{
HMODULE hurlmon;
CloseHandle(event_continue);
CloseHandle(event_continue_done);
+ test_com_aggregation(&CLSID_FileProtocol);
+ test_com_aggregation(&CLSID_HttpProtocol);
+ test_com_aggregation(&CLSID_HttpSProtocol);
+ test_com_aggregation(&CLSID_FtpProtocol);
+ test_com_aggregation(&CLSID_MkProtocol);
+
OleUninitialize();
}