comctl32/listview: Fix LVM_GETITEM for out-of-range iSubItem case.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=44842
Signed-off-by: Nikolay Sivov <nsivov@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Nikolay Sivov 2019-12-04 12:55:56 +03:00 committed by Alexandre Julliard
parent 8f9e1ae705
commit 37c3810557
2 changed files with 99 additions and 23 deletions

View File

@ -6674,11 +6674,11 @@ static HIMAGELIST LISTVIEW_GetImageList(const LISTVIEW_INFO *infoPtr, INT nImage
static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem, BOOL isW)
{
ITEMHDR callbackHdr = { LPSTR_TEXTCALLBACKW, I_IMAGECALLBACK };
BOOL is_subitem_invalid = FALSE;
NMLVDISPINFOW dispInfo;
ITEM_INFO *lpItem;
ITEMHDR* pItemHdr;
HDPA hdpaSubItems;
INT isubitem;
TRACE("(item=%s, isW=%d)\n", debuglvitem_t(lpLVItem, isW), isW);
@ -6688,10 +6688,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
if (lpLVItem->mask == 0) return TRUE;
TRACE("mask=%x\n", lpLVItem->mask);
/* make a local copy */
isubitem = lpLVItem->iSubItem;
if (isubitem && (lpLVItem->mask & LVIF_STATE))
if (lpLVItem->iSubItem && (lpLVItem->mask & LVIF_STATE))
lpLVItem->state = 0;
/* a quick optimization if all we're asked is the focus state
@ -6701,7 +6698,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
!(infoPtr->uCallbackMask & LVIS_FOCUSED) )
{
lpLVItem->state = 0;
if (infoPtr->nFocusedItem == lpLVItem->iItem && isubitem == 0)
if (infoPtr->nFocusedItem == lpLVItem->iItem && !lpLVItem->iSubItem)
lpLVItem->state |= LVIS_FOCUSED;
return TRUE;
}
@ -6723,7 +6720,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
* depend on the uninitialized fields being 0 */
dispInfo.item.mask = lpLVItem->mask & ~LVIF_PARAM;
dispInfo.item.iItem = lpLVItem->iItem;
dispInfo.item.iSubItem = isubitem;
dispInfo.item.iSubItem = lpLVItem->iSubItem;
if (lpLVItem->mask & LVIF_TEXT)
{
if (lpLVItem->mask & LVIF_NORECOMPUTE)
@ -6770,7 +6767,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
lpLVItem->pszText = LPSTR_TEXTCALLBACKW;
/* we store only a little state, so if we're not asked, we're done */
if (!(lpLVItem->mask & LVIF_STATE) || isubitem) return TRUE;
if (!(lpLVItem->mask & LVIF_STATE) || lpLVItem->iSubItem) return TRUE;
/* if focus is handled by us, report it */
if ( lpLVItem->stateMask & ~infoPtr->uCallbackMask & LVIS_FOCUSED )
@ -6796,21 +6793,22 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
lpItem = DPA_GetPtr(hdpaSubItems, 0);
assert (lpItem);
if (isubitem)
if (lpLVItem->iSubItem)
{
SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, isubitem);
pItemHdr = lpSubItem ? &lpSubItem->hdr : &callbackHdr;
if (!lpSubItem)
SUBITEM_INFO *lpSubItem = LISTVIEW_GetSubItemPtr(hdpaSubItems, lpLVItem->iSubItem);
if (lpSubItem)
pItemHdr = &lpSubItem->hdr;
else
{
WARN(" iSubItem invalid (%08x), ignored.\n", isubitem);
isubitem = 0;
pItemHdr = &callbackHdr;
is_subitem_invalid = TRUE;
}
}
else
pItemHdr = &lpItem->hdr;
/* Do we need to query the state from the app? */
if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && isubitem == 0)
if ((lpLVItem->mask & LVIF_STATE) && infoPtr->uCallbackMask && (!lpLVItem->iSubItem || is_subitem_invalid))
{
dispInfo.item.mask |= LVIF_STATE;
dispInfo.item.stateMask = infoPtr->uCallbackMask;
@ -6818,15 +6816,14 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
/* Do we need to enquire about the image? */
if ((lpLVItem->mask & LVIF_IMAGE) && pItemHdr->iImage == I_IMAGECALLBACK &&
(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
(!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES)))
{
dispInfo.item.mask |= LVIF_IMAGE;
dispInfo.item.iImage = I_IMAGECALLBACK;
}
/* Only items support indentation */
if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK &&
(isubitem == 0))
if ((lpLVItem->mask & LVIF_INDENT) && lpItem->iIndent == I_INDENTCALLBACK && !lpLVItem->iSubItem)
{
dispInfo.item.mask |= LVIF_INDENT;
dispInfo.item.iIndent = I_INDENTCALLBACK;
@ -6847,14 +6844,14 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
if (dispInfo.item.mask)
{
dispInfo.item.iItem = lpLVItem->iItem;
dispInfo.item.iSubItem = lpLVItem->iSubItem; /* yes: the original subitem */
dispInfo.item.iSubItem = lpLVItem->iSubItem;
dispInfo.item.lParam = lpItem->lParam;
notify_dispinfoT(infoPtr, LVN_GETDISPINFOW, &dispInfo, isW);
TRACE(" getdispinfo(2):item=%s\n", debuglvitem_t(&dispInfo.item, isW));
}
/* we should not store values for subitems */
if (isubitem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
if (lpLVItem->iSubItem) dispInfo.item.mask &= ~LVIF_DI_SETITEM;
/* Now, handle the iImage field */
if (dispInfo.item.mask & LVIF_IMAGE)
@ -6865,7 +6862,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
}
else if (lpLVItem->mask & LVIF_IMAGE)
{
if(isubitem == 0 || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
if (!lpLVItem->iSubItem || (infoPtr->dwLvExStyle & LVS_EX_SUBITEMIMAGES))
lpLVItem->iImage = pItemHdr->iImage;
else
lpLVItem->iImage = 0;
@ -6897,7 +6894,7 @@ static BOOL LISTVIEW_GetItemT(const LISTVIEW_INFO *infoPtr, LPLVITEMW lpLVItem,
lpLVItem->lParam = lpItem->lParam;
/* if this is a subitem, we're done */
if (isubitem) return TRUE;
if (lpLVItem->iSubItem) return TRUE;
/* ... the state field (this one is different due to uCallbackmask) */
if (lpLVItem->mask & LVIF_STATE)

View File

@ -956,6 +956,37 @@ static void test_images(void)
ok(EqualRect(&r1, &r2), "rectangle should be the same\n");
DestroyWindow(hwnd);
/* I_IMAGECALLBACK set for item, try to get image with invalid subitem. */
hwnd = create_listview_control(LVS_REPORT);
ok(hwnd != NULL, "Failed to create listview.\n");
memset(&item, 0, sizeof(item));
item.mask = LVIF_IMAGE;
item.iImage = I_IMAGECALLBACK;
r = SendMessageA(hwnd, LVM_INSERTITEMA, 0, (LPARAM)&item);
ok(!r, "Failed to insert item.\n");
flush_sequences(sequences, NUM_MSG_SEQUENCES);
memset(&item, 0, sizeof(item));
item.mask = LVIF_IMAGE;
r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(r, "Failed to get item.\n");
ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq, "get image dispinfo 1", FALSE);
flush_sequences(sequences, NUM_MSG_SEQUENCES);
memset(&item, 0, sizeof(item));
item.mask = LVIF_IMAGE;
item.iSubItem = 1;
r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(r, "Failed to get item.\n");
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "get image dispinfo 2", FALSE);
DestroyWindow(hwnd);
}
static void test_checkboxes(void)
@ -4551,6 +4582,17 @@ static void test_indentation(void)
ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
"get indent dispinfo", FALSE);
/* Ask for iIndent with invalid subitem. */
flush_sequences(sequences, NUM_MSG_SEQUENCES);
memset(&item, 0, sizeof(item));
item.mask = LVIF_INDENT;
item.iSubItem = 1;
r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(r, "Failed to get item.\n");
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "get indent dispinfo 2", FALSE);
DestroyWindow(hwnd);
}
@ -6058,6 +6100,44 @@ static void test_callback_mask(void)
mask = SendMessageA(hwnd, LVM_GETCALLBACKMASK, 0, 0);
ok(mask == ~0u, "got 0x%08x\n", mask);
/* Ask for state, invalid subitem. */
insert_item(hwnd, 0);
ret = SendMessageA(hwnd, LVM_SETCALLBACKMASK, LVIS_FOCUSED, 0);
ok(ret, "Failed to set callback mask.\n");
flush_sequences(sequences, NUM_MSG_SEQUENCES);
memset(&item, 0, sizeof(item));
item.iSubItem = 1;
item.mask = LVIF_STATE;
item.stateMask = LVIS_SELECTED;
ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(ret, "Failed to get item data.\n");
memset(&item, 0, sizeof(item));
item.mask = LVIF_STATE;
item.stateMask = LVIS_SELECTED;
ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(ret, "Failed to get item data.\n");
ok_sequence(sequences, PARENT_SEQ_INDEX, empty_seq, "parent seq, callback mask/invalid subitem 1", TRUE);
flush_sequences(sequences, NUM_MSG_SEQUENCES);
memset(&item, 0, sizeof(item));
memset(&g_itema, 0, sizeof(g_itema));
item.iSubItem = 1;
item.mask = LVIF_STATE;
item.stateMask = LVIS_FOCUSED | LVIS_SELECTED;
ret = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(ret, "Failed to get item data.\n");
ok(g_itema.iSubItem == 1, "Unexpected LVN_DISPINFO subitem %d.\n", g_itema.iSubItem);
ok(g_itema.stateMask == LVIS_FOCUSED, "Unexpected state mask %#x.\n", g_itema.stateMask);
ok_sequence(sequences, PARENT_SEQ_INDEX, single_getdispinfo_parent_seq,
"parent seq, callback mask/invalid subitem 2", FALSE);
DestroyWindow(hwnd);
/* LVS_OWNERDATA, mask LVIS_FOCUSED */
@ -6271,7 +6351,6 @@ static void test_state_image(void)
item.iSubItem = 2;
r = SendMessageA(hwnd, LVM_GETITEMA, 0, (LPARAM)&item);
ok(r, "Failed to get subitem state.\n");
todo_wine
ok(item.state == 0, "Unexpected state %#x.\n", item.state);
item.mask = LVIF_TEXT;