From 4b0fc030777cd541604f5ebaaad47a2b76d61ff9 Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Wed, 12 Feb 2020 10:53:12 +0100 Subject: [PATCH 1/7] Update md4c to 0.4.3 Fixes important bugs. [ChangeLog][Third-Party Code] md4c was updated to 0.4.3. Change-Id: Ifed7939b7bd67e3f0a7776b4f4b1132d80141033 Reviewed-by: Volker Hilsheimer Reviewed-by: Qt CI Bot --- src/3rdparty/md4c/md4c.c | 596 ++++++++++++++++---------- src/3rdparty/md4c/md4c.h | 19 +- src/3rdparty/md4c/qt_attribution.json | 6 +- 3 files changed, 386 insertions(+), 235 deletions(-) diff --git a/src/3rdparty/md4c/md4c.c b/src/3rdparty/md4c/md4c.c index 3745cf3e46..b0ef739b3c 100644 --- a/src/3rdparty/md4c/md4c.c +++ b/src/3rdparty/md4c/md4c.c @@ -2,7 +2,7 @@ * MD4C: Markdown parser for C * (http://github.com/mity/md4c) * - * Copyright (c) 2016-2019 Martin Mitas + * Copyright (c) 2016-2020 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -35,14 +35,23 @@ *** Miscellaneous Stuff *** *****************************/ -#ifdef _MSC_VER - /* MSVC does not understand "inline" when building as pure C (not C++). - * However it understands "__inline" */ - #ifndef __cplusplus +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199409L + /* C89/90 or old compilers in general may not understand "inline". */ + #if defined __GNUC__ + #define inline __inline__ + #elif defined _MSC_VER #define inline __inline + #else + #define inline #endif #endif +/* Make the UTF-8 support the default. */ +#if !defined MD4C_USE_ASCII && !defined MD4C_USE_UTF8 && !defined MD4C_USE_UTF16 + #define MD4C_USE_UTF8 +#endif + +/* Magic for making wide literals with MD4C_USE_UTF16. */ #ifdef _T #undef _T #endif @@ -127,7 +136,7 @@ struct MD_CTX_tag { #endif /* For resolving of inline spans. */ - MD_MARKCHAIN mark_chains[12]; + MD_MARKCHAIN mark_chains[13]; #define PTR_CHAIN ctx->mark_chains[0] #define TABLECELLBOUNDARIES ctx->mark_chains[1] #define ASTERISK_OPENERS_extraword_mod3_0 ctx->mark_chains[2] @@ -137,11 +146,12 @@ struct MD_CTX_tag { #define ASTERISK_OPENERS_intraword_mod3_1 ctx->mark_chains[6] #define ASTERISK_OPENERS_intraword_mod3_2 ctx->mark_chains[7] #define UNDERSCORE_OPENERS ctx->mark_chains[8] -#define TILDE_OPENERS ctx->mark_chains[9] -#define BRACKET_OPENERS ctx->mark_chains[10] -#define DOLLAR_OPENERS ctx->mark_chains[11] +#define TILDE_OPENERS_1 ctx->mark_chains[9] +#define TILDE_OPENERS_2 ctx->mark_chains[10] +#define BRACKET_OPENERS ctx->mark_chains[11] +#define DOLLAR_OPENERS ctx->mark_chains[12] #define OPENERS_CHAIN_FIRST 2 -#define OPENERS_CHAIN_LAST 11 +#define OPENERS_CHAIN_LAST 12 int n_table_cell_boundaries; @@ -263,6 +273,9 @@ struct MD_VERBATIMLINE_tag { #define CH(off) (ctx->text[(off)]) #define STR(off) (ctx->text + (off)) +/* Check whether the pointer points into ctx->text. */ +#define IS_INPUT_STR(ptr) (ctx->text <= (ptr) && (ptr) < (ctx->text + ctx->size)) + /* Character classification. * Note we assume ASCII compatibility of code points < 128 here. */ #define ISIN_(ch, ch_min, ch_max) ((ch_min) <= (unsigned)(ch) && (unsigned)(ch) <= (ch_max)) @@ -297,16 +310,14 @@ struct MD_VERBATIMLINE_tag { #define ISDIGIT(off) ISDIGIT_(CH(off)) #define ISXDIGIT(off) ISXDIGIT_(CH(off)) #define ISALNUM(off) ISALNUM_(CH(off)) -static inline const CHAR* -md_strchr(const CHAR* str, CHAR ch) -{ - OFF i; - for(i = 0; str[i] != _T('\0'); i++) { - if(ch == str[i]) - return (str + i); - } - return NULL; -} + + +#if defined MD4C_USE_UTF16 + #define md_strchr wcschr +#else + #define md_strchr strchr +#endif + /* Case insensitive check of string equality. */ static inline int @@ -364,89 +375,89 @@ md_text_with_null_replacement(MD_CTX* ctx, MD_TEXTTYPE type, const CHAR* str, SZ } -#define MD_CHECK(func) \ - do { \ - ret = (func); \ - if(ret < 0) \ - goto abort; \ +#define MD_CHECK(func) \ + do { \ + ret = (func); \ + if(ret < 0) \ + goto abort; \ } while(0) -#define MD_TEMP_BUFFER(sz) \ - do { \ - if(sz > ctx->alloc_buffer) { \ - CHAR* new_buffer; \ - SZ new_size = ((sz) + (sz) / 2 + 128) & ~127; \ - \ - new_buffer = realloc(ctx->buffer, new_size); \ - if(new_buffer == NULL) { \ - MD_LOG("realloc() failed."); \ - ret = -1; \ - goto abort; \ - } \ - \ - ctx->buffer = new_buffer; \ - ctx->alloc_buffer = new_size; \ - } \ +#define MD_TEMP_BUFFER(sz) \ + do { \ + if(sz > ctx->alloc_buffer) { \ + CHAR* new_buffer; \ + SZ new_size = ((sz) + (sz) / 2 + 128) & ~127; \ + \ + new_buffer = realloc(ctx->buffer, new_size); \ + if(new_buffer == NULL) { \ + MD_LOG("realloc() failed."); \ + ret = -1; \ + goto abort; \ + } \ + \ + ctx->buffer = new_buffer; \ + ctx->alloc_buffer = new_size; \ + } \ } while(0) -#define MD_ENTER_BLOCK(type, arg) \ - do { \ - ret = ctx->parser.enter_block((type), (arg), ctx->userdata); \ - if(ret != 0) { \ - MD_LOG("Aborted from enter_block() callback."); \ - goto abort; \ - } \ +#define MD_ENTER_BLOCK(type, arg) \ + do { \ + ret = ctx->parser.enter_block((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from enter_block() callback."); \ + goto abort; \ + } \ } while(0) -#define MD_LEAVE_BLOCK(type, arg) \ - do { \ - ret = ctx->parser.leave_block((type), (arg), ctx->userdata); \ - if(ret != 0) { \ - MD_LOG("Aborted from leave_block() callback."); \ - goto abort; \ - } \ +#define MD_LEAVE_BLOCK(type, arg) \ + do { \ + ret = ctx->parser.leave_block((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from leave_block() callback."); \ + goto abort; \ + } \ } while(0) -#define MD_ENTER_SPAN(type, arg) \ - do { \ - ret = ctx->parser.enter_span((type), (arg), ctx->userdata); \ - if(ret != 0) { \ - MD_LOG("Aborted from enter_span() callback."); \ - goto abort; \ - } \ +#define MD_ENTER_SPAN(type, arg) \ + do { \ + ret = ctx->parser.enter_span((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from enter_span() callback."); \ + goto abort; \ + } \ } while(0) -#define MD_LEAVE_SPAN(type, arg) \ - do { \ - ret = ctx->parser.leave_span((type), (arg), ctx->userdata); \ - if(ret != 0) { \ - MD_LOG("Aborted from leave_span() callback."); \ - goto abort; \ - } \ +#define MD_LEAVE_SPAN(type, arg) \ + do { \ + ret = ctx->parser.leave_span((type), (arg), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from leave_span() callback."); \ + goto abort; \ + } \ } while(0) -#define MD_TEXT(type, str, size) \ - do { \ - if(size > 0) { \ - ret = ctx->parser.text((type), (str), (size), ctx->userdata); \ - if(ret != 0) { \ - MD_LOG("Aborted from text() callback."); \ - goto abort; \ - } \ - } \ +#define MD_TEXT(type, str, size) \ + do { \ + if(size > 0) { \ + ret = ctx->parser.text((type), (str), (size), ctx->userdata); \ + if(ret != 0) { \ + MD_LOG("Aborted from text() callback."); \ + goto abort; \ + } \ + } \ } while(0) -#define MD_TEXT_INSECURE(type, str, size) \ - do { \ - if(size > 0) { \ - ret = md_text_with_null_replacement(ctx, type, str, size); \ - if(ret != 0) { \ - MD_LOG("Aborted from text() callback."); \ - goto abort; \ - } \ - } \ +#define MD_TEXT_INSECURE(type, str, size) \ + do { \ + if(size > 0) { \ + ret = md_text_with_null_replacement(ctx, type, str, size); \ + if(ret != 0) { \ + MD_LOG("Aborted from text() callback."); \ + goto abort; \ + } \ + } \ } while(0) @@ -1329,8 +1340,9 @@ md_build_attr_append_substr(MD_CTX* ctx, MD_ATTRIBUTE_BUILD* build, MD_TEXTTYPE* new_substr_types; OFF* new_substr_offsets; - build->substr_alloc = (build->substr_alloc == 0 ? 8 : build->substr_alloc * 2); - + build->substr_alloc = (build->substr_alloc > 0 + ? build->substr_alloc + build->substr_alloc / 2 + : 8); new_substr_types = (MD_TEXTTYPE*) realloc(build->substr_types, build->substr_alloc * sizeof(MD_TEXTTYPE)); if(new_substr_types == NULL) { @@ -1456,8 +1468,8 @@ abort: *** Dictionary of Reference Definitions *** *********************************************/ -#define MD_FNV1A_BASE 2166136261 -#define MD_FNV1A_PRIME 16777619 +#define MD_FNV1A_BASE 2166136261U +#define MD_FNV1A_PRIME 16777619U static inline unsigned md_fnv1a(unsigned base, const void* data, size_t n) @@ -1479,9 +1491,7 @@ struct MD_REF_DEF_tag { CHAR* label; CHAR* title; unsigned hash; - SZ label_size : 24; - unsigned label_needs_free : 1; - unsigned title_needs_free : 1; + SZ label_size; SZ title_size; OFF dest_beg; OFF dest_end; @@ -1530,7 +1540,7 @@ md_link_label_cmp_load_fold_info(const CHAR* label, OFF off, SZ size, SZ char_size; if(off >= size) { - /* Treat end of link label as a whitespace. */ + /* Treat end of a link label as a whitespace. */ goto whitespace; } @@ -1554,7 +1564,7 @@ md_link_label_cmp_load_fold_info(const CHAR* label, OFF off, SZ size, whitespace: fold_info->codepoints[0] = _T(' '); fold_info->n_codepoints = 1; - return off; + return md_skip_unicode_whitespace(label, off, size); } static int @@ -1572,7 +1582,7 @@ md_link_label_cmp(const CHAR* a_label, SZ a_size, const CHAR* b_label, SZ b_size a_off = md_skip_unicode_whitespace(a_label, 0, a_size); b_off = md_skip_unicode_whitespace(b_label, 0, b_size); - while(!a_reached_end && !b_reached_end) { + while(!a_reached_end || !b_reached_end) { /* If needed, load fold info for next char. */ if(a_fi_off >= a_fi.n_codepoints) { a_fi_off = 0; @@ -1618,7 +1628,7 @@ md_ref_def_cmp(const void* a, const void* b) } static int -md_ref_def_cmp_stable(const void* a, const void* b) +md_ref_def_cmp_for_sort(const void* a, const void* b) { int cmp; @@ -1671,6 +1681,7 @@ md_build_ref_def_hashtable(MD_CTX* ctx) bucket = ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size]; if(bucket == NULL) { + /* The bucket is empty. Make it just point to the def. */ ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = def; continue; } @@ -1682,12 +1693,12 @@ md_build_ref_def_hashtable(MD_CTX* ctx) MD_REF_DEF* old_def = (MD_REF_DEF*) bucket; if(md_link_label_cmp(def->label, def->label_size, old_def->label, old_def->label_size) == 0) { - /* Ignore this ref. def. */ + /* Duplicate label: Ignore this ref. def. */ continue; } - /* Make the bucket capable of holding more ref. defs. */ - list = (MD_REF_DEF_LIST*) malloc(sizeof(MD_REF_DEF_LIST) + 4 * sizeof(MD_REF_DEF*)); + /* Make the bucket complex, i.e. able to hold more ref. defs. */ + list = (MD_REF_DEF_LIST*) malloc(sizeof(MD_REF_DEF_LIST) + 2 * sizeof(MD_REF_DEF*)); if(list == NULL) { MD_LOG("malloc() failed."); goto abort; @@ -1695,22 +1706,28 @@ md_build_ref_def_hashtable(MD_CTX* ctx) list->ref_defs[0] = old_def; list->ref_defs[1] = def; list->n_ref_defs = 2; - list->alloc_ref_defs = 4; + list->alloc_ref_defs = 2; ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list; continue; } - /* Append the def to the bucket list. */ + /* Append the def to the complex bucket list. + * + * Note in this case we ignore potential duplicates to avoid expensive + * iterating over the complex bucket. Below, we revisit all the complex + * buckets and handle it more cheaply after the complex bucket contents + * is sorted. */ list = (MD_REF_DEF_LIST*) bucket; if(list->n_ref_defs >= list->alloc_ref_defs) { + int alloc_ref_defs = list->alloc_ref_defs + list->alloc_ref_defs / 2; MD_REF_DEF_LIST* list_tmp = (MD_REF_DEF_LIST*) realloc(list, - sizeof(MD_REF_DEF_LIST) + 2 * list->alloc_ref_defs * sizeof(MD_REF_DEF*)); + sizeof(MD_REF_DEF_LIST) + alloc_ref_defs * sizeof(MD_REF_DEF*)); if(list_tmp == NULL) { MD_LOG("realloc() failed."); goto abort; } list = list_tmp; - list->alloc_ref_defs *= 2; + list->alloc_ref_defs = alloc_ref_defs; ctx->ref_def_hashtable[def->hash % ctx->ref_def_hashtable_size] = list; } @@ -1729,9 +1746,12 @@ md_build_ref_def_hashtable(MD_CTX* ctx) continue; list = (MD_REF_DEF_LIST*) bucket; - qsort(list->ref_defs, list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp_stable); + qsort(list->ref_defs, list->n_ref_defs, sizeof(MD_REF_DEF*), md_ref_def_cmp_for_sort); - /* Disable duplicates. */ + /* Disable all duplicates in the complex bucket by forcing all such + * records to point to the 1st such ref. def. I.e. no matter which + * record is found during the lookup, it will always point to the right + * ref. def. in ctx->ref_defs[]. */ for(j = 1; j < list->n_ref_defs; j++) { if(md_ref_def_cmp(&list->ref_defs[j-1], &list->ref_defs[j]) == 0) list->ref_defs[j] = list->ref_defs[j-1]; @@ -2055,9 +2075,8 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) OFF label_contents_end; int label_contents_line_index = -1; int label_is_multiline; - CHAR* label; + CHAR* label = NULL; SZ label_size; - int label_needs_free = FALSE; OFF dest_contents_beg; OFF dest_contents_end; OFF title_contents_beg; @@ -2123,23 +2142,22 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) if(!label_is_multiline) { label = (CHAR*) STR(label_contents_beg); label_size = label_contents_end - label_contents_beg; - label_needs_free = FALSE; } else { MD_CHECK(md_merge_lines_alloc(ctx, label_contents_beg, label_contents_end, lines + label_contents_line_index, n_lines - label_contents_line_index, _T(' '), &label, &label_size)); - label_needs_free = TRUE; } /* Store the reference definition. */ if(ctx->n_ref_defs >= ctx->alloc_ref_defs) { MD_REF_DEF* new_defs; - ctx->alloc_ref_defs = (ctx->alloc_ref_defs > 0 ? ctx->alloc_ref_defs * 2 : 16); + ctx->alloc_ref_defs = (ctx->alloc_ref_defs > 0 + ? ctx->alloc_ref_defs + ctx->alloc_ref_defs / 2 + : 16); new_defs = (MD_REF_DEF*) realloc(ctx->ref_defs, ctx->alloc_ref_defs * sizeof(MD_REF_DEF)); if(new_defs == NULL) { MD_LOG("realloc() failed."); - ret = -1; goto abort; } @@ -2151,7 +2169,6 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) def->label = label; def->label_size = label_size; - def->label_needs_free = label_needs_free; def->dest_beg = dest_contents_beg; def->dest_end = dest_contents_end; @@ -2166,7 +2183,6 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) MD_CHECK(md_merge_lines_alloc(ctx, title_contents_beg, title_contents_end, lines + title_contents_line_index, n_lines - title_contents_line_index, _T('\n'), &def->title, &def->title_size)); - def->title_needs_free = TRUE; } /* Success. */ @@ -2175,9 +2191,9 @@ md_is_link_reference_definition(MD_CTX* ctx, const MD_LINE* lines, int n_lines) abort: /* Failure. */ - if(label_needs_free) + if(!IS_INPUT_STR(label)) free(label); - return -1; + return ret; } static int @@ -2225,7 +2241,7 @@ md_is_link_reference(MD_CTX* ctx, const MD_LINE* lines, int n_lines, attr->title_needs_free = FALSE; } - if(beg_line != end_line) + if(!IS_INPUT_STR(label)) free(label); ret = (def != NULL); @@ -2339,9 +2355,9 @@ md_free_ref_defs(MD_CTX* ctx) for(i = 0; i < ctx->n_ref_defs; i++) { MD_REF_DEF* def = &ctx->ref_defs[i]; - if(def->label_needs_free) + if(!IS_INPUT_STR(def->label)) free(def->label); - if(def->title_needs_free) + if(!IS_INPUT_STR(def->title)) free(def->title); } @@ -2470,7 +2486,7 @@ md_mark_chain(MD_CTX* ctx, int mark_index) switch(mark->ch) { case _T('*'): return md_asterisk_chain(ctx, mark->flags); case _T('_'): return &UNDERSCORE_OPENERS; - case _T('~'): return &TILDE_OPENERS; + case _T('~'): return (mark->end - mark->beg == 1) ? &TILDE_OPENERS_1 : &TILDE_OPENERS_2; case _T('['): return &BRACKET_OPENERS; case _T('|'): return &TABLECELLBOUNDARIES; default: return NULL; @@ -2483,7 +2499,9 @@ md_push_mark(MD_CTX* ctx) if(ctx->n_marks >= ctx->alloc_marks) { MD_MARK* new_marks; - ctx->alloc_marks = (ctx->alloc_marks > 0 ? ctx->alloc_marks * 2 : 64); + ctx->alloc_marks = (ctx->alloc_marks > 0 + ? ctx->alloc_marks + ctx->alloc_marks / 2 + : 64); new_marks = realloc(ctx->marks, ctx->alloc_marks * sizeof(MD_MARK)); if(new_marks == NULL) { MD_LOG("realloc() failed."); @@ -2526,6 +2544,7 @@ md_mark_chain_append(MD_CTX* ctx, MD_MARKCHAIN* chain, int mark_index) chain->head = mark_index; ctx->marks[mark_index].prev = chain->tail; + ctx->marks[mark_index].next = -1; chain->tail = mark_index; } @@ -2617,7 +2636,7 @@ md_rollback(MD_CTX* ctx, int opener_index, int closer_index, int how) chain->head = -1; } - /* Go backwards so that un-resolved openers are re-added into their + /* Go backwards so that unresolved openers are re-added into their * respective chains, in the right order. */ mark_index = closer_index - 1; while(mark_index > opener_index) { @@ -2696,7 +2715,7 @@ md_build_mark_char_map(MD_CTX* ctx) if(ctx->parser.flags & MD_FLAG_PERMISSIVEWWWAUTOLINKS) ctx->mark_char_map['.'] = 1; - if(ctx->parser.flags & MD_FLAG_TABLES) + if((ctx->parser.flags & MD_FLAG_TABLES) || (ctx->parser.flags & MD_FLAG_WIKILINKS)) ctx->mark_char_map['|'] = 1; if(ctx->parser.flags & MD_FLAG_COLLAPSEWHITESPACE) { @@ -2896,7 +2915,7 @@ md_is_autolink_email(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end) return FALSE; off++; - /* Labels delimited with '.'; each label is sequence of 1 - 62 alnum + /* Labels delimited with '.'; each label is sequence of 1 - 63 alnum * characters or '-', but '-' is not allowed as first or last char. */ label_len = 0; while(off < max_end) { @@ -2909,7 +2928,7 @@ md_is_autolink_email(MD_CTX* ctx, OFF beg, OFF max_end, OFF* p_end) else break; - if(label_len > 62) + if(label_len > 63) return FALSE; off++; @@ -3236,8 +3255,8 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) continue; } - /* A potential table cell boundary. */ - if(table_mode && ch == _T('|')) { + /* A potential table cell boundary or wiki link label delimiter. */ + if((table_mode || ctx->parser.flags & MD_FLAG_WIKILINKS) && ch == _T('|')) { PUSH_MARK(ch, off, off+1, 0); off++; continue; @@ -3250,7 +3269,17 @@ md_collect_marks(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mode) while(tmp < line_end && CH(tmp) == _T('~')) tmp++; - PUSH_MARK(ch, off, tmp, MD_MARK_POTENTIAL_OPENER | MD_MARK_POTENTIAL_CLOSER); + if(tmp - off < 3) { + unsigned flags = 0; + + if(tmp < line_end && !ISUNICODEWHITESPACE(tmp)) + flags |= MD_MARK_POTENTIAL_OPENER; + if(off > line->beg && !ISUNICODEWHITESPACEBEFORE(off)) + flags |= MD_MARK_POTENTIAL_CLOSER; + if(flags != 0) + PUSH_MARK(ch, off, tmp, flags); + } + off = tmp; continue; } @@ -3398,6 +3427,91 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) continue; } + /* Recognize and resolve wiki links. + * Wiki-links maybe '[[destination]]' or '[[destination|label]]'. + */ + if ((ctx->parser.flags & MD_FLAG_WIKILINKS) && + (opener->end - opener->beg == 1) && /* not image */ + next_opener != NULL && /* double '[' opener */ + next_opener->ch == '[' && + (next_opener->beg == opener->beg - 1) && + (next_opener->end - next_opener->beg == 1) && + next_closer != NULL && /* double ']' closer */ + next_closer->ch == ']' && + (next_closer->beg == closer->beg + 1) && + (next_closer->end - next_closer->beg == 1)) + { + MD_MARK* delim = NULL; + int delim_index; + OFF dest_beg, dest_end; + + is_link = TRUE; + + /* We don't allow destination to be longer then 100 characters. + * Lets scan to see whether there is '|'. (If not then the whole + * wiki-link has to be below the 100 characters.) */ + delim_index = opener_index + 1; + while(delim_index < closer_index) { + MD_MARK* m = &ctx->marks[delim_index]; + if(m->ch == '|') { + delim = m; + break; + } + if(m->ch != 'D' && m->beg - opener->end > 100) + break; + delim_index++; + } + dest_beg = opener->end; + dest_end = (delim != NULL) ? delim->beg : closer->beg; + if(dest_end - dest_beg == 0 || dest_end - dest_beg > 100) + is_link = FALSE; + + /* There may not be any new line in the destination. */ + if(is_link) { + OFF off; + for(off = dest_beg; off < dest_end; off++) { + if(ISNEWLINE(off)) { + is_link = FALSE; + break; + } + } + } + + if(is_link) { + if(delim != NULL) { + if(delim->end < closer->beg) { + opener->end = delim->beg; + } else { + /* The pipe is just before the closer: [[foo|]] */ + closer->beg = delim->beg; + delim = NULL; + } + } + + opener->beg = next_opener->beg; + opener->next = closer_index; + opener->flags |= MD_MARK_OPENER | MD_MARK_RESOLVED; + + closer->end = next_closer->end; + closer->prev = opener_index; + closer->flags |= MD_MARK_CLOSER | MD_MARK_RESOLVED; + + last_link_beg = opener->beg; + last_link_end = closer->end; + + if(delim != NULL) { + delim->flags |= MD_MARK_RESOLVED; + md_rollback(ctx, opener_index, delim_index, MD_ROLLBACK_ALL); + md_analyze_link_contents(ctx, lines, n_lines, opener_index+1, closer_index); + } else { + md_rollback(ctx, opener_index, closer_index, MD_ROLLBACK_ALL); + } + + opener_index = next_opener->prev; + continue; + } + } + if(next_opener != NULL && next_opener->beg == closer->end) { if(next_closer->beg > closer->end + 1) { /* Might be full reference link. */ @@ -3436,7 +3550,7 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) if((mark->flags & (MD_MARK_OPENER | MD_MARK_RESOLVED)) == (MD_MARK_OPENER | MD_MARK_RESOLVED)) { if(ctx->marks[mark->next].beg >= inline_link_end) { /* Cancel the link status. */ - if(attr.title_needs_free) + if(!IS_INPUT_STR(attr.title)) free(attr.title); is_link = FALSE; break; @@ -3476,6 +3590,7 @@ md_resolve_links(MD_CTX* ctx, const MD_LINE* lines, int n_lines) MD_ASSERT(ctx->marks[opener_index+2].ch == 'D'); md_mark_store_ptr(ctx, opener_index+2, attr.title); + /* The title might or might not have been allocated for us. */ if(attr.title_needs_free) md_mark_chain_append(ctx, &PTR_CHAIN, opener_index+2); ctx->marks[opener_index+2].prev = attr.title_size; @@ -3574,8 +3689,7 @@ md_analyze_emph(MD_CTX* ctx, int mark_index) int i, n_opener_chains; unsigned flags = mark->flags; - /* Apply "rule of three". (This is why we break asterisk opener - * marks into multiple chains.) */ + /* Apply the "rule of three". */ n_opener_chains = 0; opener_chains[n_opener_chains++] = &ASTERISK_OPENERS_intraword_mod3_0; if((flags & MD_MARK_EMPH_MOD3_MASK) != MD_MARK_EMPH_MOD3_2) @@ -3611,16 +3725,17 @@ md_analyze_emph(MD_CTX* ctx, int mark_index) if(opener != NULL) { SZ opener_size = opener->end - opener->beg; SZ closer_size = mark->end - mark->beg; + MD_MARKCHAIN* opener_chain = md_mark_chain(ctx, opener_index); if(opener_size > closer_size) { opener_index = md_split_emph_mark(ctx, opener_index, closer_size); - md_mark_chain_append(ctx, md_mark_chain(ctx, opener_index), opener_index); + md_mark_chain_append(ctx, opener_chain, opener_index); } else if(opener_size < closer_size) { md_split_emph_mark(ctx, mark_index, closer_size - opener_size); } md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); - md_resolve_range(ctx, chain, opener_index, mark_index); + md_resolve_range(ctx, opener_chain, opener_index, mark_index); return; } } @@ -3633,20 +3748,23 @@ md_analyze_emph(MD_CTX* ctx, int mark_index) static void md_analyze_tilde(MD_CTX* ctx, int mark_index) { - /* We attempt to be Github Flavored Markdown compatible here. GFM says - * that length of the tilde sequence is not important at all. Note that - * implies the TILDE_OPENERS chain can have at most one item. */ + MD_MARK* mark = &ctx->marks[mark_index]; + MD_MARKCHAIN* chain = md_mark_chain(ctx, mark_index); - if(TILDE_OPENERS.head >= 0) { - /* The chain already contains an opener, so we may resolve the span. */ - int opener_index = TILDE_OPENERS.head; + /* We attempt to be Github Flavored Markdown compatible here. GFM accepts + * only tildes sequences of length 1 and 2, and the length of the opener + * and closer has to match. */ + + if((mark->flags & MD_MARK_POTENTIAL_CLOSER) && chain->head >= 0) { + int opener_index = chain->head; md_rollback(ctx, opener_index, mark_index, MD_ROLLBACK_CROSSING); - md_resolve_range(ctx, &TILDE_OPENERS, opener_index, mark_index); - } else { - /* We can only be opener. */ - md_mark_chain_append(ctx, &TILDE_OPENERS, mark_index); + md_resolve_range(ctx, chain, opener_index, mark_index); + return; } + + if(mark->flags & MD_MARK_POTENTIAL_OPENER) + md_mark_chain_append(ctx, chain, mark_index); } static void @@ -3860,8 +3978,16 @@ md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mod /* (1) Entities; code spans; autolinks; raw HTML. */ md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("&")); + /* (2) Links. */ + md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("[]!")); + MD_CHECK(md_resolve_links(ctx, lines, n_lines)); + BRACKET_OPENERS.head = -1; + BRACKET_OPENERS.tail = -1; + ctx->unresolved_link_head = -1; + ctx->unresolved_link_tail = -1; + if(table_mode) { - /* (2) Analyze table cell boundaries. + /* (3) Analyze table cell boundaries. * Note we reset TABLECELLBOUNDARIES chain prior to the call md_analyze_marks(), * not after, because caller may need it. */ MD_ASSERT(n_lines == 1); @@ -3872,14 +3998,6 @@ md_analyze_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int table_mod return ret; } - /* (3) Links. */ - md_analyze_marks(ctx, lines, n_lines, 0, ctx->n_marks, _T("[]!")); - MD_CHECK(md_resolve_links(ctx, lines, n_lines)); - BRACKET_OPENERS.head = -1; - BRACKET_OPENERS.tail = -1; - ctx->unresolved_link_head = -1; - ctx->unresolved_link_tail = -1; - /* (4) Emphasis and strong emphasis; permissive autolinks. */ md_analyze_link_contents(ctx, lines, n_lines, 0, ctx->n_marks); @@ -3891,25 +4009,14 @@ static void md_analyze_link_contents(MD_CTX* ctx, const MD_LINE* lines, int n_lines, int mark_beg, int mark_end) { + int i; + md_analyze_marks(ctx, lines, n_lines, mark_beg, mark_end, _T("*_~$@:.")); - ASTERISK_OPENERS_extraword_mod3_0.head = -1; - ASTERISK_OPENERS_extraword_mod3_0.tail = -1; - ASTERISK_OPENERS_extraword_mod3_1.head = -1; - ASTERISK_OPENERS_extraword_mod3_1.tail = -1; - ASTERISK_OPENERS_extraword_mod3_2.head = -1; - ASTERISK_OPENERS_extraword_mod3_2.tail = -1; - ASTERISK_OPENERS_intraword_mod3_0.head = -1; - ASTERISK_OPENERS_intraword_mod3_0.tail = -1; - ASTERISK_OPENERS_intraword_mod3_1.head = -1; - ASTERISK_OPENERS_intraword_mod3_1.tail = -1; - ASTERISK_OPENERS_intraword_mod3_2.head = -1; - ASTERISK_OPENERS_intraword_mod3_2.tail = -1; - UNDERSCORE_OPENERS.head = -1; - UNDERSCORE_OPENERS.tail = -1; - TILDE_OPENERS.head = -1; - TILDE_OPENERS.tail = -1; - DOLLAR_OPENERS.head = -1; - DOLLAR_OPENERS.tail = -1; + + for(i = OPENERS_CHAIN_FIRST; i <= OPENERS_CHAIN_LAST; i++) { + ctx->mark_chains[i].head = -1; + ctx->mark_chains[i].tail = -1; + } } static int @@ -3941,6 +4048,27 @@ abort: return ret; } +static int +md_enter_leave_span_wikilink(MD_CTX* ctx, int enter, const CHAR* target, SZ target_size) +{ + MD_ATTRIBUTE_BUILD target_build = { 0 }; + MD_SPAN_WIKILINK_DETAIL det; + int ret = 0; + + memset(&det, 0, sizeof(MD_SPAN_WIKILINK_DETAIL)); + MD_CHECK(md_build_attribute(ctx, target, target_size, 0, &det.target, &target_build)); + + if (enter) + MD_ENTER_SPAN(MD_SPAN_WIKILINK, &det); + else + MD_LEAVE_SPAN(MD_SPAN_WIKILINK, &det); + +abort: + md_free_attribute(ctx, &target_build); + return ret; +} + + /* Render the output, accordingly to the analyzed ctx->marks. */ static int md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) @@ -3996,7 +4124,23 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) } break; - case '_': + case '_': /* Underline (or emphasis if we fall through). */ + if(ctx->parser.flags & MD_FLAG_UNDERLINE) { + if(mark->flags & MD_MARK_OPENER) { + while(off < mark->end) { + MD_ENTER_SPAN(MD_SPAN_U, NULL); + off++; + } + } else { + while(off < mark->end) { + MD_LEAVE_SPAN(MD_SPAN_U, NULL); + off++; + } + } + break; + } + /* Fall though. */ + case '*': /* Emphasis, strong emphasis. */ if(mark->flags & MD_MARK_OPENER) { if((mark->end - off) % 2) { @@ -4036,15 +4180,37 @@ md_process_inlines(MD_CTX* ctx, const MD_LINE* lines, int n_lines) } break; - case '[': /* Link, image. */ + case '[': /* Link, wiki link, image. */ case '!': case ']': { const MD_MARK* opener = (mark->ch != ']' ? mark : &ctx->marks[mark->prev]); - const MD_MARK* dest_mark = opener+1; - const MD_MARK* title_mark = opener+2; + const MD_MARK* closer = &ctx->marks[opener->next]; + const MD_MARK* dest_mark; + const MD_MARK* title_mark; + if ((opener->ch == '[' && closer->ch == ']') && + opener->end - opener->beg >= 2 && + closer->end - closer->beg >= 2) + { + int has_label = (opener->end - opener->beg > 2); + SZ target_sz; + + if(has_label) + target_sz = opener->end - (opener->beg+2); + else + target_sz = closer->beg - opener->end; + + MD_CHECK(md_enter_leave_span_wikilink(ctx, (mark->ch != ']'), + has_label ? STR(opener->beg+2) : STR(opener->end), + target_sz)); + + break; + } + + dest_mark = opener+1; MD_ASSERT(dest_mark->ch == 'D'); + title_mark = opener+2; MD_ASSERT(title_mark->ch == 'D'); MD_CHECK(md_enter_leave_span_a(ctx, (mark->ch != ']'), @@ -4251,7 +4417,7 @@ md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, { MD_LINE line; OFF* pipe_offs = NULL; - int i, j, n; + int i, j, k, n; int ret = 0; line.beg = beg; @@ -4263,32 +4429,32 @@ md_process_table_row(MD_CTX* ctx, MD_BLOCKTYPE cell_type, OFF beg, OFF end, /* We have to remember the cell boundaries in local buffer because * ctx->marks[] shall be reused during cell contents processing. */ - n = ctx->n_table_cell_boundaries; + n = ctx->n_table_cell_boundaries + 2; pipe_offs = (OFF*) malloc(n * sizeof(OFF)); if(pipe_offs == NULL) { MD_LOG("malloc() failed."); ret = -1; goto abort; } - for(i = TABLECELLBOUNDARIES.head, j = 0; i >= 0; i = ctx->marks[i].next) { + j = 0; + pipe_offs[j++] = beg; + for(i = TABLECELLBOUNDARIES.head; i >= 0; i = ctx->marks[i].next) { MD_MARK* mark = &ctx->marks[i]; - pipe_offs[j++] = mark->beg; + pipe_offs[j++] = mark->end; } + pipe_offs[j++] = end+1; /* Process cells. */ MD_ENTER_BLOCK(MD_BLOCK_TR, NULL); - j = 0; - if(beg < pipe_offs[0] && j < col_count) - MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], beg, pipe_offs[0])); - for(i = 0; i < n-1 && j < col_count; i++) - MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], pipe_offs[i]+1, pipe_offs[i+1])); - if(pipe_offs[n-1] < end-1 && j < col_count) - MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], pipe_offs[n-1]+1, end)); + k = 0; + for(i = 0; i < j-1 && k < col_count; i++) { + if(pipe_offs[i] < pipe_offs[i+1]-1) + MD_CHECK(md_process_table_cell(ctx, cell_type, align[k++], pipe_offs[i], pipe_offs[i+1]-1)); + } /* Make sure we call enough table cells even if the current table contains * too few of them. */ - while(j < col_count) - MD_CHECK(md_process_table_cell(ctx, cell_type, align[j++], 0, 0)); - + while(k < col_count) + MD_CHECK(md_process_table_cell(ctx, cell_type, align[k++], 0, 0)); MD_LEAVE_BLOCK(MD_BLOCK_TR, NULL); abort: @@ -4340,38 +4506,6 @@ abort: return ret; } -static int -md_is_table_row(MD_CTX* ctx, OFF beg, OFF* p_end) -{ - MD_LINE line; - int i; - int ret = FALSE; - - line.beg = beg; - line.end = beg; - - /* Find end of line. */ - while(line.end < ctx->size && !ISNEWLINE(line.end)) - line.end++; - - MD_CHECK(md_analyze_inlines(ctx, &line, 1, TRUE)); - - if(TABLECELLBOUNDARIES.head >= 0) { - if(p_end != NULL) - *p_end = line.end; - ret = TRUE; - } - -abort: - /* Free any temporary memory blocks stored within some dummy marks. */ - for(i = PTR_CHAIN.head; i >= 0; i = ctx->marks[i].next) - free(md_mark_get_ptr(ctx, i)); - PTR_CHAIN.head = -1; - PTR_CHAIN.tail = -1; - - return ret; -} - /************************** *** Processing Block *** @@ -4704,7 +4838,9 @@ md_push_block_bytes(MD_CTX* ctx, int n_bytes) if(ctx->n_block_bytes + n_bytes > ctx->alloc_block_bytes) { void* new_block_bytes; - ctx->alloc_block_bytes = (ctx->alloc_block_bytes > 0 ? ctx->alloc_block_bytes * 2 : 512); + ctx->alloc_block_bytes = (ctx->alloc_block_bytes > 0 + ? ctx->alloc_block_bytes + ctx->alloc_block_bytes / 2 + : 512); new_block_bytes = realloc(ctx->block_bytes, ctx->alloc_block_bytes); if(new_block_bytes == NULL) { MD_LOG("realloc() failed."); @@ -5136,7 +5272,7 @@ md_is_html_block_start_condition(MD_CTX* ctx, OFF beg) #ifdef X #undef X #endif -#define X(name) { _T(name), sizeof(name)-1 } +#define X(name) { _T(name), (sizeof(name)-1) / sizeof(CHAR) } #define Xend { NULL, 0 } static const TAG t1[] = { X("script"), X("pre"), X("style"), Xend }; @@ -5192,7 +5328,7 @@ md_is_html_block_start_condition(MD_CTX* ctx, OFF beg) /* Check for type 5: size) { - if(md_ascii_eq(STR(off), _T("![CDATA["), 8 * sizeof(CHAR))) + if(md_ascii_eq(STR(off), _T("![CDATA["), 8)) return 5; } } @@ -5341,7 +5477,9 @@ md_push_container(MD_CTX* ctx, const MD_CONTAINER* container) if(ctx->n_containers >= ctx->alloc_containers) { MD_CONTAINER* new_containers; - ctx->alloc_containers = (ctx->alloc_containers > 0 ? ctx->alloc_containers * 2 : 16); + ctx->alloc_containers = (ctx->alloc_containers > 0 + ? ctx->alloc_containers + ctx->alloc_containers / 2 + : 16); new_containers = realloc(ctx->containers, ctx->alloc_containers * sizeof(MD_CONTAINER)); if(new_containers == NULL) { MD_LOG("realloc() failed."); @@ -5561,6 +5699,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, line->indent--; line->beg = off; + } else if(c->ch != _T('>') && line->indent >= c->contents_indent) { /* List. */ line->indent -= c->contents_indent; @@ -5803,9 +5942,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, } /* Check whether we are table continuation. */ - if(pivot_line->type == MD_LINE_TABLE && md_is_table_row(ctx, off, &off) && - n_parents == ctx->n_containers) - { + if(pivot_line->type == MD_LINE_TABLE && n_parents == ctx->n_containers) { line->type = MD_LINE_TABLE; break; } @@ -5859,8 +5996,7 @@ md_analyze_line(MD_CTX* ctx, OFF beg, OFF* p_end, unsigned col_count; if(ctx->current_block != NULL && ctx->current_block->n_lines == 1 && - md_is_table_underline(ctx, off, &off, &col_count) && - md_is_table_row(ctx, pivot_line->beg, NULL)) + md_is_table_underline(ctx, off, &off, &col_count)) { line->data = col_count; line->type = MD_LINE_TABLEUNDERLINE; diff --git a/src/3rdparty/md4c/md4c.h b/src/3rdparty/md4c/md4c.h index 6d9fce5180..c2c4311f50 100644 --- a/src/3rdparty/md4c/md4c.h +++ b/src/3rdparty/md4c/md4c.h @@ -2,7 +2,7 @@ * MD4C: Markdown parser for C * (http://github.com/mity/md4c) * - * Copyright (c) 2016-2019 Martin Mitas + * Copyright (c) 2016-2020 Martin Mitas * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -135,7 +135,16 @@ typedef enum MD_SPANTYPE { * Note: Recognized only when MD_FLAG_LATEXMATHSPANS is enabled. */ MD_SPAN_LATEXMATH, - MD_SPAN_LATEXMATH_DISPLAY + MD_SPAN_LATEXMATH_DISPLAY, + + /* Wiki links + * Note: Recognized only when MD_FLAG_WIKILINKS is enabled. + */ + MD_SPAN_WIKILINK, + + /* ... + * Note: Recognized only when MD_FLAG_UNDERLINE is enabled. */ + MD_SPAN_U } MD_SPANTYPE; /* Text is the actual textual contents of span. */ @@ -268,6 +277,10 @@ typedef struct MD_SPAN_IMG_DETAIL { MD_ATTRIBUTE title; } MD_SPAN_IMG_DETAIL; +/* Detailed info for MD_SPAN_WIKILINK. */ +typedef struct MD_SPAN_WIKILINK { + MD_ATTRIBUTE target; +} MD_SPAN_WIKILINK_DETAIL; /* Flags specifying extensions/deviations from CommonMark specification. * @@ -286,6 +299,8 @@ typedef struct MD_SPAN_IMG_DETAIL { #define MD_FLAG_PERMISSIVEWWWAUTOLINKS 0x0400 /* Enable WWW autolinks (even without any scheme prefix, if they begin with 'www.') */ #define MD_FLAG_TASKLISTS 0x0800 /* Enable task list extension. */ #define MD_FLAG_LATEXMATHSPANS 0x1000 /* Enable $ and $$ containing LaTeX equations. */ +#define MD_FLAG_WIKILINKS 0x2000 /* Enable wiki links extension. */ +#define MD_FLAG_UNDERLINE 0x4000 /* Enable underline extension (and disables '_' for normal emphasis). */ #define MD_FLAG_PERMISSIVEAUTOLINKS (MD_FLAG_PERMISSIVEEMAILAUTOLINKS | MD_FLAG_PERMISSIVEURLAUTOLINKS | MD_FLAG_PERMISSIVEWWWAUTOLINKS) #define MD_FLAG_NOHTML (MD_FLAG_NOHTMLBLOCKS | MD_FLAG_NOHTMLSPANS) diff --git a/src/3rdparty/md4c/qt_attribution.json b/src/3rdparty/md4c/qt_attribution.json index 5fd77269e9..c574b97711 100644 --- a/src/3rdparty/md4c/qt_attribution.json +++ b/src/3rdparty/md4c/qt_attribution.json @@ -9,7 +9,7 @@ "License": "MIT License", "LicenseId": "MIT", "LicenseFile": "LICENSE.md", - "Version": "0.3.4", - "DownloadLocation": "https://github.com/mity/md4c/releases/tag/release-0.3.4", - "Copyright": "Copyright © 2016-2019 Martin Mitáš" + "Version": "0.4.3", + "DownloadLocation": "https://github.com/mity/md4c/releases/tag/release-0.4.3", + "Copyright": "Copyright © 2016-2020 Martin Mitáš" } From c3951470ca22004fff158a0494db88a3f9867213 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Thu, 6 Feb 2020 15:35:57 +0100 Subject: [PATCH 2/7] Discover the conditions under which registerTimer is flaky, and skip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS, the registerTimer test case fails frequently, and blocks valid integrations. With this change we try to detect the condition and skip the test. Reviewed-by: Tor Arne Vestbø (cherry picked from commit 5c520f4b0ad4b539dc0184c764ca9f12c98730d9) Change-Id: I97644b5b4654b4c96fbc99858bbf191e6edb5977 Reviewed-by: Timur Pocheptsov --- .../qeventdispatcher/tst_qeventdispatcher.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp index 49c10c6a24..6f65932e5f 100644 --- a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp +++ b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp @@ -205,10 +205,26 @@ void tst_QEventDispatcher::registerTimer() QVERIFY(timers.foundCoarse()); QVERIFY(timers.foundVeryCoarse()); +#ifdef Q_OS_DARWIN + /* + We frequently experience flaky failures on macOS. Assumption is that this is + due to undeterministic VM scheduling, making us process events for significantly + longer than expected and resulting in timers firing in undefined order. + To detect this condition, we use a QElapsedTimer, and skip the test. + */ + QElapsedTimer elapsedTimer; + elapsedTimer.start(); +#endif + // process events, waiting for the next event... this should only fire the precise timer receivedEventType = -1; timerIdFromEvent = -1; QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), PreciseTimerInterval * 2); +#ifdef Q_OS_DARWIN + if (timerIdFromEvent != timers.preciseTimerId() + && elapsedTimer.elapsed() > PreciseTimerInterval * 3) + QSKIP("Ignore flaky test behavior due to VM scheduling on macOS"); +#endif QCOMPARE(timerIdFromEvent, timers.preciseTimerId()); // now unregister it and make sure it's gone timers.unregister(timers.preciseTimerId()); @@ -223,6 +239,12 @@ void tst_QEventDispatcher::registerTimer() receivedEventType = -1; timerIdFromEvent = -1; QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), CoarseTimerInterval * 2); +#ifdef Q_OS_DARWIN + if (timerIdFromEvent != timers.coarseTimerId() + && elapsedTimer.elapsed() > CoarseTimerInterval * 3) + QSKIP("Ignore flaky test behavior due to VM scheduling on macOS"); +#endif + QCOMPARE(timerIdFromEvent, timers.coarseTimerId()); // now unregister it and make sure it's gone timers.unregister(timers.coarseTimerId()); From 1ce3585305febbd2fed6899cad0c58729991ec43 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Wed, 12 Feb 2020 17:50:44 +0100 Subject: [PATCH 3/7] Detect double timer during single timeout in registerTimer test, and skip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We observe this happening on macOS in the CI system, and it might happen if a VM doesn't get CPU cycles for long enough time so that two timers time out. Then event processing will process two timer events, and we overwrite the timerIdFromEvent with the second event. Instead, skip the test when this happens. This is an ammendment to 5c520f4b0ad4b539dc0184c764ca9f12c98730d9 Fixes: QTBUG-71751 Reviewed-by: Timur Pocheptsov Reviewed-by: Tor Arne Vestbø (cherry picked from commit 67491e2df5357706dbf88ddaf1f030ff095b4528) Change-Id: I30eef8cfc94988e6cad500dd5e6722488c2985be --- .../qeventdispatcher/tst_qeventdispatcher.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp index 6f65932e5f..ed44026805 100644 --- a/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp +++ b/tests/auto/corelib/kernel/qeventdispatcher/tst_qeventdispatcher.cpp @@ -45,8 +45,10 @@ class tst_QEventDispatcher : public QObject Q_OBJECT QAbstractEventDispatcher *eventDispatcher; - int receivedEventType; - int timerIdFromEvent; + + int receivedEventType = -1; + int timerIdFromEvent = -1; + bool doubleTimer = false; protected: bool event(QEvent *e); @@ -54,9 +56,7 @@ protected: public: inline tst_QEventDispatcher() : QObject(), - eventDispatcher(QAbstractEventDispatcher::instance(thread())), - receivedEventType(-1), - timerIdFromEvent(-1) + eventDispatcher(QAbstractEventDispatcher::instance(thread())) { } private slots: @@ -74,6 +74,9 @@ bool tst_QEventDispatcher::event(QEvent *e) switch (receivedEventType = e->type()) { case QEvent::Timer: { + // sometimes, two timers fire during a single QTRY_xxx wait loop + if (timerIdFromEvent != -1) + doubleTimer = true; timerIdFromEvent = static_cast(e)->timerId(); return true; } @@ -219,12 +222,17 @@ void tst_QEventDispatcher::registerTimer() // process events, waiting for the next event... this should only fire the precise timer receivedEventType = -1; timerIdFromEvent = -1; + doubleTimer = false; QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), PreciseTimerInterval * 2); + #ifdef Q_OS_DARWIN + if (doubleTimer) + QSKIP("Double timer during a single timeout - aborting test as flaky on macOS"); if (timerIdFromEvent != timers.preciseTimerId() && elapsedTimer.elapsed() > PreciseTimerInterval * 3) QSKIP("Ignore flaky test behavior due to VM scheduling on macOS"); #endif + QCOMPARE(timerIdFromEvent, timers.preciseTimerId()); // now unregister it and make sure it's gone timers.unregister(timers.preciseTimerId()); @@ -238,8 +246,12 @@ void tst_QEventDispatcher::registerTimer() // do the same again for the coarse timer receivedEventType = -1; timerIdFromEvent = -1; + doubleTimer = false; QTRY_COMPARE_WITH_TIMEOUT(receivedEventType, int(QEvent::Timer), CoarseTimerInterval * 2); + #ifdef Q_OS_DARWIN + if (doubleTimer) + QSKIP("Double timer during a single timeout - aborting test as flaky on macOS"); if (timerIdFromEvent != timers.coarseTimerId() && elapsedTimer.elapsed() > CoarseTimerInterval * 3) QSKIP("Ignore flaky test behavior due to VM scheduling on macOS"); From 962a3d8a988cb695b08d79900fb25d2c41a2225c Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Fri, 7 Feb 2020 11:28:44 +0100 Subject: [PATCH 4/7] Stabilize QFileSystemModel::dirsBeforeFiles test Make the test operate in its own temporary directory, so that entries left behind by other test functions don't impact this test. Also, call QFileSystemModel::sort explicitly; it would otherwise only be done once through a single-shot timer, and the test processes events until the model is populated, which might not process that delayed sorting. Since dirsBeforeFiles tests the sorting algorithm and not the sorting logic, best to do this explicitly. In case of sort failure, print diagnostics. Done-with: Friedemann Kleint Fixes: QTBUG-75452 Reviewed-by: Friedemann Kleint (cherry picked from commit 4e796e0b0dcf4c0848044471021db3afce16ee5d) Change-Id: I144b68a17280a38cc7d6daf7ec343eea4453623d Reviewed-by: Timur Pocheptsov --- .../qfilesystemmodel/tst_qfilesystemmodel.cpp | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp index 590b3502f1..e29a715617 100644 --- a/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp +++ b/tests/auto/widgets/dialogs/qfilesystemmodel/tst_qfilesystemmodel.cpp @@ -1014,30 +1014,38 @@ void tst_QFileSystemModel::drives() void tst_QFileSystemModel::dirsBeforeFiles() { - QDir dir(flatDirTestPath); + auto diagnosticMsg = [](int row, const QFileInfo &left, const QFileInfo &right) -> QByteArray { + QString message; + QDebug(&message).noquote() << "Unexpected sort order at #" << row << ':' << left << right; + return message.toLocal8Bit(); + }; + QTemporaryDir testDir(flatDirTestPath); + QVERIFY2(testDir.isValid(), qPrintable(testDir.errorString())); + QDir dir(testDir.path()); const int itemCount = 3; for (int i = 0; i < itemCount; ++i) { QLatin1Char c('a' + char(i)); QVERIFY(dir.mkdir(c + QLatin1String("-dir"))); - QFile file(flatDirTestPath + QLatin1Char('/') + c + QLatin1String("-file")); + QFile file(dir.filePath(c + QLatin1String("-file"))); QVERIFY(file.open(QIODevice::ReadWrite)); file.close(); } QScopedPointer model(new QFileSystemModel); - QModelIndex root = model->setRootPath(flatDirTestPath); + QModelIndex root = model->setRootPath(dir.absolutePath()); // Wait for model to be notified by the file system watcher QTRY_COMPARE(model->rowCount(root), 2 * itemCount); - - // Ensure that no file occurs before any directory: - for (int i = 1; i < model->rowCount(root); ++i) { + // sort explicitly - dirs before files (except on macOS), and then by name + model->sort(0); + // Ensure that no file occurs before any directory (see QFileSystemModelSorter): + for (int i = 1, count = model->rowCount(root); i < count; ++i) { + const QFileInfo previous = model->fileInfo(model->index(i - 1, 0, root)); + const QFileInfo current = model->fileInfo(model->index(i, 0, root)); #ifndef Q_OS_MAC - QVERIFY(!(model->fileInfo(model->index(i - 1, 0, root)).isFile() - && model->fileInfo(model->index(i, 0, root)).isDir())); + QVERIFY2(!(previous.isFile() && current.isDir()), diagnosticMsg(i, previous, current).constData()); #else - QVERIFY(model->fileInfo(model->index(i - 1, 0, root)).fileName() < - model->fileInfo(model->index(i, 0, root)).fileName()); + QVERIFY2(previous.fileName() < current.fileName(), diagnosticMsg(i, previous, current).constData()); #endif } } From c58ee54a707717b142f6e90cf48302884feb0b12 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 20 Feb 2020 09:50:44 +0100 Subject: [PATCH 5/7] tst_qtcpsocket::bind - amend the recent fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0. The recent patch fixed the case when we can suddenly (meaning from a particular version of Darwin) bind on a port number 1. Unfortunately, it's not the case for IPv4 and while fixing one test case, the patch broke another - so this patch addresses this. 1. Unfortunately, binding on a fixed port 1 on macOS made the test flaky - we run this 'bind' several times and sometimes OS thinks port is already bound (because of the previous test case) - closing the connection seems to fix this problem (thus this patch do this also). 2. As a bonus a proper resource management added (aka RAII) where we would previously leak a socket in case some QCOMPARE failed. Fixes: QTBUG-81905 Change-Id: I90c128a332903bb44ab37de4775ca00d390dc162 Reviewed-by: Mårten Nordheim Reviewed-by: Edward Welbourne --- .../socket/qtcpsocket/tst_qtcpsocket.cpp | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp index 5b3a17bdd7..f85d041f49 100644 --- a/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp +++ b/tests/auto/network/socket/qtcpsocket/tst_qtcpsocket.cpp @@ -556,8 +556,14 @@ void tst_QTcpSocket::bind_data() // try to bind to a privileged ports // we should fail if we're not root (unless the ports are in use!) +#ifdef Q_OS_DARWIN + // Alas, some quirk (starting from macOS 10.14): bind with port number 1 + // fails with IPv4 (not IPv6 though, see below). + QTest::newRow("127.0.0.1:1") << "127.0.0.1" << 1 << false << QString(); +#else QTest::newRow("127.0.0.1:1") << "127.0.0.1" << 1 << QtNetworkSettings::canBindToLowPorts() << (QtNetworkSettings::canBindToLowPorts() ? "127.0.0.1" : QString()); +#endif // Q_OS_DARWIN if (testIpv6) QTest::newRow("[::]:1") << "::" << 1 << QtNetworkSettings::canBindToLowPorts() << (QtNetworkSettings::canBindToLowPorts() ? "::" : QString()); @@ -579,7 +585,7 @@ void tst_QTcpSocket::bind() QTcpSocket dummySocket; // used only to "use up" a file descriptor dummySocket.bind(); - QTcpSocket *socket = newSocket(); + std::unique_ptr socket(newSocket()); quint16 boundPort; qintptr fd; @@ -645,9 +651,24 @@ void tst_QTcpSocket::bind() QCOMPARE(acceptedSocket->peerPort(), boundPort); QCOMPARE(socket->localAddress(), remoteAddr); QCOMPARE(socket->socketDescriptor(), fd); +#ifdef Q_OS_DARWIN + // Normally, we don't see this problem: macOS sometimes does not + // allow us to immediately re-use a port, thinking connection is + // still alive. With fixed port 1 (we testing starting from + // macOS 10.14), this problem shows, making the test flaky: + // we run this 'bind' with port 1 several times (different + // test cases) and the problem manifests itself as + // "The bound address is already in use, tried port 1". + QTestEventLoop cleanupHelper; + auto client = socket.get(); + connect(client, &QTcpSocket::disconnected, [&cleanupHelper, client](){ + client->close(); + cleanupHelper.exitLoop(); + }); + acceptedSocket->close(); + cleanupHelper.enterLoopMSecs(100); +#endif // Q_OS_DARWIN } - - delete socket; } //---------------------------------------------------------------------------------- From 835e18d9ff79867f508fc8baac7fcc5bb4efc0cb Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 20 Feb 2020 11:37:41 +0100 Subject: [PATCH 6/7] Fix wrong DPI used by QStyle::pixelMetric() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass on the option or the widget in order to ensure usage of the correct DPI for High DPI scaling. Task-number: QTBUG-82356 Change-Id: I5df903a83f88adebd143e514e2fead367d39f015 Reviewed-by: Morten Johan Sørvig --- .../windowsvista/qwindowsvistastyle.cpp | 2 +- .../styles/windowsvista/qwindowsxpstyle.cpp | 4 +- src/widgets/dialogs/qcolordialog.cpp | 3 +- src/widgets/dialogs/qwizard.cpp | 7 +++- src/widgets/graphicsview/qgraphicslayout_p.h | 4 +- .../graphicsview/qgraphicslayoutstyleinfo.cpp | 4 +- src/widgets/graphicsview/qgraphicswidget.cpp | 2 +- src/widgets/itemviews/qitemdelegate.cpp | 2 +- src/widgets/itemviews/qlistview.cpp | 7 +++- src/widgets/styles/qcommonstyle.cpp | 42 +++++++++---------- src/widgets/styles/qfusionstyle.cpp | 8 ++-- src/widgets/styles/qpixmapstyle.cpp | 4 +- src/widgets/widgets/qcalendarwidget.cpp | 4 +- src/widgets/widgets/qcombobox_p.h | 2 +- src/widgets/widgets/qcommandlinkbutton.cpp | 8 ++-- src/widgets/widgets/qfocusframe.cpp | 12 +++--- src/widgets/widgets/qgroupbox.cpp | 6 +-- src/widgets/widgets/qlineedit.cpp | 2 +- src/widgets/widgets/qmenu.cpp | 2 +- src/widgets/widgets/qmenubar.cpp | 2 +- src/widgets/widgets/qtoolbarextension.cpp | 4 +- src/widgets/widgets/qwidgettextcontrol.cpp | 2 +- 22 files changed, 74 insertions(+), 59 deletions(-) diff --git a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp index e213d65946..f9d26c49ae 100644 --- a/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsvistastyle.cpp @@ -2215,7 +2215,7 @@ QRect QWindowsVistaStyle::subControlRect(ComplexControl control, const QStyleOpt { const int controlTop = int(6 * factor); const int controlHeight = int(height - controlTop - 3 * factor); - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); if (tb->icon.isNull()) iconSize = QSize(controlHeight, controlHeight); diff --git a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp index bf80138b32..ba590b3eca 100644 --- a/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp +++ b/src/plugins/styles/windowsvista/qwindowsxpstyle.cpp @@ -3508,7 +3508,7 @@ QRect QWindowsXPStyle::subControlRect(ComplexControl cc, const QStyleOptionCompl { const int controlTop = 6; const int controlHeight = height - controlTop - 3; - const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + const int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, option); QSize iconSize = tb->icon.actualSize(QSize(iconExtent, iconExtent)); if (tb->icon.isNull()) iconSize = QSize(controlHeight, controlHeight); @@ -3630,7 +3630,7 @@ QSize QWindowsXPStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt sz.rheight() += int(borderSize.bottom() + borderSize.top() - margin + qreal(1) / factor - 1); } - const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin) + 1); + const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, option) + 1); sz += QSize(qMax(pixelMetric(QStyle::PM_ScrollBarExtent, option, widget) + textMargins, 23), 0); //arrow button } diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index d00a600424..9a34a4a9b6 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -346,7 +346,8 @@ void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect) const QPalette & g = palette(); QStyleOptionFrame opt; - int dfw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + opt.initFrom(this); + int dfw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &opt); opt.lineWidth = dfw; opt.midLineWidth = 1; opt.rect = rect.adjusted(b, b, -b, -b); diff --git a/src/widgets/dialogs/qwizard.cpp b/src/widgets/dialogs/qwizard.cpp index 0295241a74..a50dc13bc1 100644 --- a/src/widgets/dialogs/qwizard.cpp +++ b/src/widgets/dialogs/qwizard.cpp @@ -64,6 +64,7 @@ # include "qshortcut.h" #endif #include "qstyle.h" +#include "qstyleoption.h" #include "qvarlengtharray.h" #if defined(Q_OS_MACX) #include @@ -897,7 +898,9 @@ QWizardLayoutInfo QWizardPrivate::layoutInfoForCurrentPage() QWizardLayoutInfo info; - const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); + QStyleOption option; + option.initFrom(q); + const int layoutHorizontalSpacing = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &option); info.topLevelMarginLeft = style->pixelMetric(QStyle::PM_LayoutLeftMargin, 0, q); info.topLevelMarginRight = style->pixelMetric(QStyle::PM_LayoutRightMargin, 0, q); info.topLevelMarginTop = style->pixelMetric(QStyle::PM_LayoutTopMargin, 0, q); @@ -909,7 +912,7 @@ QWizardLayoutInfo QWizardPrivate::layoutInfoForCurrentPage() info.hspacing = (layoutHorizontalSpacing == -1) ? style->layoutSpacing(QSizePolicy::DefaultType, QSizePolicy::DefaultType, Qt::Horizontal) : layoutHorizontalSpacing; - info.vspacing = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing); + info.vspacing = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &option); info.buttonSpacing = (layoutHorizontalSpacing == -1) ? style->layoutSpacing(QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal) : layoutHorizontalSpacing; diff --git a/src/widgets/graphicsview/qgraphicslayout_p.h b/src/widgets/graphicsview/qgraphicslayout_p.h index 0d91151e22..9e86ae2f76 100644 --- a/src/widgets/graphicsview/qgraphicslayout_p.h +++ b/src/widgets/graphicsview/qgraphicslayout_p.h @@ -87,8 +87,8 @@ public: Q_ASSERT(style); if (widget) //### m_styleOption.initFrom(widget); - m_defaultSpacing[0] = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing); - m_defaultSpacing[1] = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing); + m_defaultSpacing[0] = style->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &m_styleOption); + m_defaultSpacing[1] = style->pixelMetric(QStyle::PM_LayoutVerticalSpacing, &m_styleOption); } inline void invalidate() { m_valid = false; m_style = nullptr; m_widget = nullptr; } diff --git a/src/widgets/graphicsview/qgraphicslayoutstyleinfo.cpp b/src/widgets/graphicsview/qgraphicslayoutstyleinfo.cpp index da2510a8cb..7759cfe757 100644 --- a/src/widgets/graphicsview/qgraphicslayoutstyleinfo.cpp +++ b/src/widgets/graphicsview/qgraphicslayoutstyleinfo.cpp @@ -80,7 +80,9 @@ qreal QGraphicsLayoutStyleInfo::perItemSpacing(QLayoutPolicy::ControlType contro qreal QGraphicsLayoutStyleInfo::spacing(Qt::Orientation orientation) const { Q_ASSERT(style()); - return style()->pixelMetric(orientation == Qt::Horizontal ? QStyle::PM_LayoutHorizontalSpacing : QStyle::PM_LayoutVerticalSpacing); + return style()->pixelMetric(orientation == Qt::Horizontal + ? QStyle::PM_LayoutHorizontalSpacing : QStyle::PM_LayoutVerticalSpacing, + &m_styleOption); } qreal QGraphicsLayoutStyleInfo::windowMargin(Qt::Orientation orientation) const diff --git a/src/widgets/graphicsview/qgraphicswidget.cpp b/src/widgets/graphicsview/qgraphicswidget.cpp index cf041b9817..1bb668e74c 100644 --- a/src/widgets/graphicsview/qgraphicswidget.cpp +++ b/src/widgets/graphicsview/qgraphicswidget.cpp @@ -615,7 +615,7 @@ void QGraphicsWidget::unsetWindowFrameMargins() QStyleOptionTitleBar bar; d->initStyleOptionTitleBar(&bar); QStyle *style = this->style(); - qreal margin = style->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth); + const qreal margin = style->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, &bar); qreal titleBarHeight = d->titleBarHeight(bar); setWindowFrameMargins(margin, titleBarHeight, margin, margin); } else { diff --git a/src/widgets/itemviews/qitemdelegate.cpp b/src/widgets/itemviews/qitemdelegate.cpp index 460764f1b8..f048b1b7e8 100644 --- a/src/widgets/itemviews/qitemdelegate.cpp +++ b/src/widgets/itemviews/qitemdelegate.cpp @@ -1122,7 +1122,7 @@ QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect, QSizeF fpSize = d->doTextLayout(rect.width()); const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height())); // ###: textRectangle should take style option as argument - const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; + const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr) + 1; return QRect(0, 0, size.width() + 2 * textMargin, size.height()); } diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index 62fffc17df..c3720aef73 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -1728,8 +1728,11 @@ void QListViewPrivate::prepareItemsLayout() layoutBounds = QRect(QPoint(), q->maximumViewportSize()); int frameAroundContents = 0; - if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) - frameAroundContents = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 2; + if (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { + QStyleOption option; + option.initFrom(q); + frameAroundContents = q->style()->pixelMetric(QStyle::PM_DefaultFrameWidth, &option) * 2; + } // maximumViewportSize() already takes scrollbar into account if policy is // Qt::ScrollBarAlwaysOn but scrollbar extent must be deduced if policy diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 9fa00b8d3f..ea42749846 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -431,7 +431,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q QIcon::Active, QIcon::Off); } - int size = proxy()->pixelMetric(QStyle::PM_SmallIconSize); + const int size = proxy()->pixelMetric(QStyle::PM_SmallIconSize, opt); QIcon::Mode mode = opt->state & State_Enabled ? (opt->state & State_Raised ? QIcon::Active : QIcon::Normal) : QIcon::Disabled; @@ -477,7 +477,7 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q if (const QStyleOptionFrame *frame = qstyleoption_cast(opt)) { int lw = frame->lineWidth; if (lw <= 0) - lw = proxy()->pixelMetric(PM_DockWidgetFrameWidth); + lw = proxy()->pixelMetric(PM_DockWidgetFrameWidth, opt); qDrawShadePanel(p, frame->rect, frame->palette, false, lw); } @@ -563,8 +563,8 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q int bsx = 0; int bsy = 0; if (opt->state & State_Sunken) { - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt); } p->save(); p->translate(sx + bsx, sy + bsy); @@ -994,7 +994,7 @@ QSize QCommonStylePrivate::viewItemSize(const QStyleOptionViewItem *option, int } if (wrapText && option->features & QStyleOptionViewItem::HasCheckIndicator) - bounds.setWidth(bounds.width() - proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth) - 2 * textMargin); + bounds.setWidth(bounds.width() - proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option) - 2 * textMargin); const int lineWidth = bounds.width(); const QSizeF size = viewItemTextLayout(textLayout, lineWidth); @@ -1240,7 +1240,7 @@ void QCommonStylePrivate::tabLayout(const QStyleOptionTab *opt, const QWidget *w if (!opt->icon.isNull()) { QSize iconSize = opt->iconSize; if (!iconSize.isValid()) { - int iconExtent = proxyStyle->pixelMetric(QStyle::PM_SmallIconSize); + int iconExtent = proxyStyle->pixelMetric(QStyle::PM_SmallIconSize, opt); iconSize = QSize(iconExtent, iconExtent); } QSize tabIconSize = opt->icon.actualSize(iconSize, @@ -1490,7 +1490,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, | Qt::TextSingleLine; if (!proxy()->styleHint(SH_UnderlineShortcut, mbi, widget)) alignment |= Qt::TextHideMnemonic; - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt); QPixmap pix = mbi->icon.pixmap(qt_getWindow(widget), QSize(iconExtent, iconExtent), (mbi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled); if (!pix.isNull()) proxy()->drawItemPixmap(p,mbi->rect, alignment, pix); @@ -1646,7 +1646,7 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, if (const QStyleOptionHeader *header = qstyleoption_cast(opt)) { QRect rect = header->rect; if (!header->icon.isNull()) { - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt); QPixmap pixmap = header->icon.pixmap(qt_getWindow(widget), QSize(iconExtent, iconExtent), (header->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled); int pixw = pixmap.width() / pixmap.devicePixelRatio(); @@ -3857,8 +3857,8 @@ void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCompl if (opt->activeSubControls & QStyle::SC_MdiCloseButton && (opt->state & State_Sunken)) { btnOpt.state |= State_Sunken; btnOpt.state &= ~State_Raised; - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt); } else { btnOpt.state |= State_Raised; btnOpt.state &= ~State_Sunken; @@ -3874,8 +3874,8 @@ void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCompl if (opt->activeSubControls & QStyle::SC_MdiNormalButton && (opt->state & State_Sunken)) { btnOpt.state |= State_Sunken; btnOpt.state &= ~State_Raised; - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt); } else { btnOpt.state |= State_Raised; btnOpt.state &= ~State_Sunken; @@ -3891,8 +3891,8 @@ void QCommonStyle::drawComplexControl(ComplexControl cc, const QStyleOptionCompl if (opt->activeSubControls & QStyle::SC_MdiMinButton && (opt->state & State_Sunken)) { btnOpt.state |= State_Sunken; btnOpt.state &= ~State_Raised; - bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal); - bsy = proxy()->pixelMetric(PM_ButtonShiftVertical); + bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt); + bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt); } else { btnOpt.state |= State_Raised; btnOpt.state &= ~State_Sunken; @@ -4775,12 +4775,12 @@ int QCommonStyle::pixelMetric(PixelMetric m, const QStyleOption *opt, const QWid } else if (widget) { isWindow = widget->isWindow(); } - ret = proxy()->pixelMetric(isWindow ? PM_DefaultTopLevelMargin : PM_DefaultChildMargin); + ret = proxy()->pixelMetric(isWindow ? PM_DefaultTopLevelMargin : PM_DefaultChildMargin, opt); } break; case PM_LayoutHorizontalSpacing: case PM_LayoutVerticalSpacing: - ret = proxy()->pixelMetric(PM_DefaultLayoutSpacing); + ret = proxy()->pixelMetric(PM_DefaultLayoutSpacing, opt); break; case PM_DefaultTopLevelMargin: @@ -4931,7 +4931,7 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, } else { h = mi->fontMetrics.height() + 8; if (!mi->icon.isNull()) { - int iconExtent = proxy()->pixelMetric(PM_SmallIconSize); + int iconExtent = proxy()->pixelMetric(PM_SmallIconSize, opt); h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4); } } @@ -4957,7 +4957,7 @@ QSize QCommonStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt, case CT_ComboBox: if (const QStyleOptionComboBox *cmb = qstyleoption_cast(opt)) { int fw = cmb->frame ? proxy()->pixelMetric(PM_ComboBoxFrameWidth, opt, widget) * 2 : 0; - const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin) + 1); + const int textMargins = 2*(proxy()->pixelMetric(PM_FocusFrameHMargin, opt) + 1); // QItemDelegate::sizeHint expands the textMargins two times, thus the 2*textMargins... int other = qMax(23, 2*textMargins + proxy()->pixelMetric(QStyle::PM_ScrollBarExtent, opt, widget)); sz = QSize(sz.width() + fw + other, sz.height() + fw); @@ -5207,8 +5207,8 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget if (widget) { if(QStyleHintReturnMask *mask = qstyleoption_cast(hret)) { mask->region = widget->rect(); - int vmargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin), - hmargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin); + const int vmargin = proxy()->pixelMetric(QStyle::PM_FocusFrameVMargin, opt); + const int hmargin = proxy()->pixelMetric(QStyle::PM_FocusFrameHMargin, opt); mask->region -= QRect(widget->rect().adjusted(hmargin, vmargin, -hmargin, -vmargin)); } } @@ -5221,7 +5221,7 @@ int QCommonStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget ret = true; if(QStyleHintReturnMask *mask = qstyleoption_cast(hret)) { mask->region = opt->rect; - int margin = proxy()->pixelMetric(PM_DefaultFrameWidth) * 2; + const int margin = proxy()->pixelMetric(PM_DefaultFrameWidth, opt) * 2; mask->region -= opt->rect.adjusted(margin, margin, -margin, -margin); } } diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp index 1646ec7583..548469068f 100644 --- a/src/widgets/styles/qfusionstyle.cpp +++ b/src/widgets/styles/qfusionstyle.cpp @@ -3347,8 +3347,8 @@ QRect QFusionStyle::subControlRect(ComplexControl control, const QStyleOptionCom switch (subControl) { case SC_SliderHandle: { if (slider->orientation == Qt::Horizontal) { - rect.setHeight(proxy()->pixelMetric(PM_SliderThickness)); - rect.setWidth(proxy()->pixelMetric(PM_SliderLength)); + rect.setHeight(proxy()->pixelMetric(PM_SliderThickness, option)); + rect.setWidth(proxy()->pixelMetric(PM_SliderLength, option)); int centerY = slider->rect.center().y() - rect.height() / 2; if (slider->tickPosition & QSlider::TicksAbove) centerY += tickSize; @@ -3356,8 +3356,8 @@ QRect QFusionStyle::subControlRect(ComplexControl control, const QStyleOptionCom centerY -= tickSize; rect.moveTop(centerY); } else { - rect.setWidth(proxy()->pixelMetric(PM_SliderThickness)); - rect.setHeight(proxy()->pixelMetric(PM_SliderLength)); + rect.setWidth(proxy()->pixelMetric(PM_SliderThickness, option)); + rect.setHeight(proxy()->pixelMetric(PM_SliderLength, option)); int centerX = slider->rect.center().x() - rect.width() / 2; if (slider->tickPosition & QSlider::TicksAbove) centerX += tickSize; diff --git a/src/widgets/styles/qpixmapstyle.cpp b/src/widgets/styles/qpixmapstyle.cpp index 05e8467528..7cc32b2039 100644 --- a/src/widgets/styles/qpixmapstyle.cpp +++ b/src/widgets/styles/qpixmapstyle.cpp @@ -1003,13 +1003,13 @@ QSize QPixmapStyle::pushButtonSizeFromContents(const QStyleOption *option, return d->computeSize(desc, w, h); } -QSize QPixmapStyle::lineEditSizeFromContents(const QStyleOption *, +QSize QPixmapStyle::lineEditSizeFromContents(const QStyleOption *option, const QSize &contentsSize, const QWidget *) const { Q_D(const QPixmapStyle); const QPixmapStyleDescriptor &desc = d->descriptors.value(LE_Enabled); - const int border = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth); + const int border = 2 * proxy()->pixelMetric(PM_DefaultFrameWidth, option); int w = contentsSize.width() + border + desc.margins.left() + desc.margins.right(); int h = contentsSize.height() + border + desc.margins.top() + desc.margins.bottom(); diff --git a/src/widgets/widgets/qcalendarwidget.cpp b/src/widgets/widgets/qcalendarwidget.cpp index 8593001f8b..da958a61dc 100644 --- a/src/widgets/widgets/qcalendarwidget.cpp +++ b/src/widgets/widgets/qcalendarwidget.cpp @@ -2232,7 +2232,9 @@ QSize QCalendarWidget::minimumSizeHint() const int rows = 7; int cols = 8; - const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1) * 2; + QStyleOption option; + option.initFrom(this); + const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option) + 1) * 2; if (horizontalHeaderFormat() == QCalendarWidget::NoHorizontalHeader) { rows = 6; diff --git a/src/widgets/widgets/qcombobox_p.h b/src/widgets/widgets/qcombobox_p.h index 5967776a61..8d660c11e1 100644 --- a/src/widgets/widgets/qcombobox_p.h +++ b/src/widgets/widgets/qcombobox_p.h @@ -146,7 +146,7 @@ public: setAttribute(Qt::WA_NoMousePropagation); } QSize sizeHint() const override { - return QSize(20, style()->pixelMetric(QStyle::PM_MenuScrollerHeight)); + return QSize(20, style()->pixelMetric(QStyle::PM_MenuScrollerHeight, nullptr, this)); } protected: diff --git a/src/widgets/widgets/qcommandlinkbutton.cpp b/src/widgets/widgets/qcommandlinkbutton.cpp index 2b9258fd91..e9462ed229 100644 --- a/src/widgets/widgets/qcommandlinkbutton.cpp +++ b/src/widgets/widgets/qcommandlinkbutton.cpp @@ -208,7 +208,7 @@ bool QCommandLinkButtonPrivate::usingVistaStyle() const //### This is a hack to detect if we are indeed running Vista style themed and not in classic // When we add api to query for this, we should change this implementation to use it. return q->style()->inherits("QWindowsVistaStyle") - && !q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal); + && q->style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, nullptr) == 0; } void QCommandLinkButtonPrivate::init() @@ -355,8 +355,10 @@ void QCommandLinkButton::paintEvent(QPaintEvent *) option.icon = QIcon(); //we draw this ourselves QSize pixmapSize = icon().actualSize(iconSize()); - int vOffset = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical) : 0; - int hOffset = isDown() ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal) : 0; + const int vOffset = isDown() + ? style()->pixelMetric(QStyle::PM_ButtonShiftVertical, &option) : 0; + const int hOffset = isDown() + ? style()->pixelMetric(QStyle::PM_ButtonShiftHorizontal, &option) : 0; //Draw icon p.drawControl(QStyle::CE_PushButton, option); diff --git a/src/widgets/widgets/qfocusframe.cpp b/src/widgets/widgets/qfocusframe.cpp index 0992becdf0..c5b2a32aec 100644 --- a/src/widgets/widgets/qfocusframe.cpp +++ b/src/widgets/widgets/qfocusframe.cpp @@ -86,8 +86,10 @@ void QFocusFramePrivate::updateSize() if (!widget) return; - int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin), - hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin); + QStyleOption opt; + q->initStyleOption(&opt); + int vmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt), + hmargin = q->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &opt); QPoint pos(widget->x(), widget->y()); if (q->parentWidget() != widget->parentWidget()) pos = widget->parentWidget()->mapTo(q->parentWidget(), pos); @@ -98,8 +100,6 @@ void QFocusFramePrivate::updateSize() q->setGeometry(geom); QStyleHintReturnMask mask; - QStyleOption opt; - q->initStyleOption(&opt); if (q->style()->styleHint(QStyle::SH_FocusFrame_Mask, &opt, q, &mask)) q->setMask(mask.region); } @@ -263,8 +263,8 @@ QFocusFrame::paintEvent(QPaintEvent *) QStylePainter p(this); QStyleOption option; initStyleOption(&option); - int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin); - int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin); + const int vmargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &option); + const int hmargin = style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option); QWidgetPrivate *wd = qt_widget_private(d->widget); QRect rect = wd->clipRect().adjusted(0, 0, hmargin*2, vmargin*2); p.setClipRect(rect); diff --git a/src/widgets/widgets/qgroupbox.cpp b/src/widgets/widgets/qgroupbox.cpp index eec794562a..735e950602 100644 --- a/src/widgets/widgets/qgroupbox.cpp +++ b/src/widgets/widgets/qgroupbox.cpp @@ -491,9 +491,9 @@ QSize QGroupBox::minimumSizeHint() const int baseWidth = metrics.horizontalAdvance(d->title) + metrics.horizontalAdvance(QLatin1Char(' ')); int baseHeight = metrics.height(); if (d->checkable) { - baseWidth += style()->pixelMetric(QStyle::PM_IndicatorWidth); - baseWidth += style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing); - baseHeight = qMax(baseHeight, style()->pixelMetric(QStyle::PM_IndicatorHeight)); + baseWidth += style()->pixelMetric(QStyle::PM_IndicatorWidth, &option); + baseWidth += style()->pixelMetric(QStyle::PM_CheckBoxLabelSpacing, &option); + baseHeight = qMax(baseHeight, style()->pixelMetric(QStyle::PM_IndicatorHeight, &option)); } QSize size = style()->sizeFromContents(QStyle::CT_GroupBox, &option, QSize(baseWidth, baseHeight), this); diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp index 658315028a..eaf31551d9 100644 --- a/src/widgets/widgets/qlineedit.cpp +++ b/src/widgets/widgets/qlineedit.cpp @@ -2073,7 +2073,7 @@ void QLineEdit::paintEvent(QPaintEvent *) if (d->cursorVisible && !d->control->isReadOnly()) flags |= QWidgetLineControl::DrawCursor; - d->control->setCursorWidth(style()->pixelMetric(QStyle::PM_TextCursorWidth)); + d->control->setCursorWidth(style()->pixelMetric(QStyle::PM_TextCursorWidth, &panel)); d->control->draw(&p, topLeft, r, flags); } diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 57ef7905d9..2d6e6179af 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -2828,7 +2828,7 @@ void QMenu::paintEvent(QPaintEvent *e) frame.rect = rect(); frame.palette = palette(); frame.state = QStyle::State_None; - frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth); + frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &frame); frame.midLineWidth = 0; style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this); } diff --git a/src/widgets/widgets/qmenubar.cpp b/src/widgets/widgets/qmenubar.cpp index 7a751597bc..a2087c8ab1 100644 --- a/src/widgets/widgets/qmenubar.cpp +++ b/src/widgets/widgets/qmenubar.cpp @@ -1019,7 +1019,7 @@ void QMenuBar::paintEvent(QPaintEvent *e) frame.rect = rect(); frame.palette = palette(); frame.state = QStyle::State_None; - frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth); + frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, &frame); frame.midLineWidth = 0; style()->drawPrimitive(QStyle::PE_PanelMenuBar, &frame, &p, this); } diff --git a/src/widgets/widgets/qtoolbarextension.cpp b/src/widgets/widgets/qtoolbarextension.cpp index bbe7eddaa4..165c7f274b 100644 --- a/src/widgets/widgets/qtoolbarextension.cpp +++ b/src/widgets/widgets/qtoolbarextension.cpp @@ -81,7 +81,9 @@ void QToolBarExtension::paintEvent(QPaintEvent *) QSize QToolBarExtension::sizeHint() const { - int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent); + QStyleOption opt; + opt.initFrom(this); + const int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent, &opt); return QSize(ext, ext); } diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index 83e2315c36..fb41f38b5a 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -2463,7 +2463,7 @@ void QWidgetTextControl::setCursorWidth(int width) Q_UNUSED(width); #else if (width == -1) - width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth); + width = QApplication::style()->pixelMetric(QStyle::PM_TextCursorWidth, nullptr); d->doc->documentLayout()->setProperty("cursorWidth", width); #endif d->repaintCursor(); From 875420f1c29f0d5e66b4dcacbe95d31dd5dca585 Mon Sep 17 00:00:00 2001 From: Christian Ehrlicher Date: Mon, 3 Feb 2020 20:26:22 +0100 Subject: [PATCH 7/7] QAbstractItemView: Make sure to update the editor geometries QAbstractItemView::setIndexWidget() does not trigger a relayouting when a new widget is set. This results in a wrong editor geometry under some circumstances. Fix it by triggering a delayed relayout. Fixes: QTBUG-81763 Change-Id: I75d0e19bd5e56d63effe4990d782d202fb39e3e6 Reviewed-by: Friedemann Kleint --- src/widgets/itemviews/qabstractitemview.cpp | 4 +++- tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index c3f8dd894e..f879af6ad2 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -3234,8 +3234,10 @@ void QAbstractItemView::setIndexWidget(const QModelIndex &index, QWidget *widget widget->installEventFilter(this); widget->show(); dataChanged(index, index); // update the geometry - if (!d->delayedPendingLayout) + if (!d->delayedPendingLayout) { widget->setGeometry(visualRect(index)); + d->doDelayedItemsLayout(); // relayout due to updated geometry + } } } diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index b2ca62227f..0580c466cf 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -952,6 +952,7 @@ void tst_QTreeView::indexWidget() QStandardItemModel treeModel; initStandardTreeModel(&treeModel); view.setModel(&treeModel); + view.resize(300, 400); // make sure the width of the view is larger than the widgets below QModelIndex index = view.model()->index(0, 0); @@ -980,6 +981,7 @@ void tst_QTreeView::indexWidget() //now let's try to do that later when the widget is already shown view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); index = view.model()->index(1, 0); QVERIFY(!view.indexWidget(index));