Project

General

Profile

tc11180d_false_steal.p

Sergey Ivanovskiy, 08/17/2026 05:51 AM

Download (6.13 KB)

 
1
/*------------------------------------------------------------------------------
2
  tc11180d_false_steal.p
3

    
4
  Recreate for review point #1 on the #11180b fix
5
  (ThinClient.processProgressEvent, "stolen" predicate).
6

    
7
    boolean stolen = oldFocusGone
8
                  && redirected != null
9
                  && redirected != src            // <-- WRONG operand
10
                  && redirected.isDisplayed()
11
                  && UiUtils.locateFrame(redirected) != frame;
12

    
13
  The predicate is meant to detect a trigger-REDIRECTED focus (a genuine
14
  steal).  The correct test - the one FocusManager.handleFocusChange uses
15
  (curFocus != oldFocus) and the one both methods' comments describe - is
16
  "redirected != inFocus" (inFocus is the analog of oldFocus; src is the
17
  analog of newFocus).  Using "redirected != src" instead produces a FALSE
18
  POSITIVE in the legitimate frame-deletion-WITHOUT-steal case.
19

    
20
  Topology (no steal at all):
21
    - focus is on browse W in DYNAMIC frame A  (inFocus, oldFrame)
22
    - APPLY 'ENTRY' targets fill-in fB in a DIFFERENT, LIVE frame B (src/frame)
23
    - frame A's LEAVE trigger DELETEs frame A (oldFocusGone becomes true) but
24
      does NOT redirect focus anywhere - it does no APPLY.
25

    
26
  What happens in ThinClient.processProgressEvent:
27
    - tryFocusChange(src=fB, .., doEntry=false) returns before requestFocus
28
      (doEntry is false) -> focus is NOT moved, still on W.
29
    - sendLeave(oldFrame=A, frame=B) fires A's frame LEAVE -> A is deleted;
30
      sendLeave never calls requestFocus, so focus stays on W and
31
      destroyFrame() defers the hide -> getCurrentFocus() == W, W.isDisplayed().
32
    - redirected = getCurrentFocus() == W == inFocus.
33
        oldFocusGone      = true            (A deleted)
34
        redirected != src = W != fB = true  <-- FALSE POSITIVE
35
        (with the correct "redirected != inFocus": W != W = false -> not stolen)
36
        redirected.isDisplayed()          = true
37
        locateFrame(W)=A != frame=B        = true
38
      => stolen == true (wrongly) => evt.consume(); return;
39

    
40
  Result: the LEGITIMATE ENTRY into fB is silently dropped - fB's ENTRY
41
  trigger never runs - and FOCUS is left parked on the DELETED browse W
42
  (the invalid-focus outcome the fix was meant to prevent).
43

    
44
  W is a BROWSE only so that getCurrentFocus() reliably survives A's deletion
45
  (a read-only browse remains the focus widget after its frame is destroyed).
46
  fB is a plain fill-in, so the both-Browse gate on the spurious-LEAVE emit is
47
  NOT satisfied - this isolates the defect to the consume/return itself
48
  (point #1), independent of the spurious-LEAVE emit (point #2).
49

    
50
  Expected (OpenEdge / correctly-predicated FWD):
51
    fB ENTRY fires, FOCUS ends on fB.
52
  Bug (FWD 11180b as reviewed):
53
    fB ENTRY never fires; FOCUS parked on the deleted browse.
54

    
55
  FWD drives the whole path synchronously from APPLY "ENTRY", so this runs
56
  head-less: log to tc11180d.log, then QUIT.
57
------------------------------------------------------------------------------*/
58

    
59
DEFINE TEMP-TABLE tt NO-UNDO FIELD nm AS CHARACTER.
60
DEFINE QUERY qA FOR tt SCROLLING.
61

    
62
DEFINE VARIABLE fB  AS CHARACTER NO-UNDO FORMAT "x(8)" INITIAL "bbb".
63
DEFINE VARIABLE seq AS INTEGER   NO-UNDO.
64

    
65
DEFINE VARIABLE hFrmA AS HANDLE NO-UNDO.
66
DEFINE VARIABLE hBrw  AS HANDLE NO-UNDO.
67

    
68
DEFINE FRAME frmB fB WITH TITLE "FRAME-B (legitimate destination)" AT ROW 6 COLUMN 2 SIZE 40 BY 4.
69

    
70
PROCEDURE logit:
71
   DEFINE INPUT PARAMETER m AS CHARACTER NO-UNDO.
72
   seq = seq + 1.
73
   OUTPUT TO "tc11180d.log" APPEND.
74
   PUT UNFORMATTED STRING(seq, "99") " " m SKIP.
75
   OUTPUT CLOSE.
76
END PROCEDURE.
77

    
78
/* Frame A's LEAVE: destroy A synchronously while its own LEAVE runs.
79
   CRUCIALLY there is NO APPLY here - focus is NOT redirected/stolen.
80
   This is the legitimate frame-deletion-without-steal case. */
81
PROCEDURE aLeave:
82
   RUN logit("frmA  LEAVE  (begin - delete frame A, NO focus steal)").
83
   IF VALID-HANDLE(hFrmA) THEN DELETE WIDGET hFrmA.
84
   RUN logit("frmA  LEAVE  (end - no APPLY performed)").
85
END PROCEDURE.
86

    
87
/* ---- destination frame B (the legitimate ENTRY target that MUST fire) ---- */
88
ON ENTRY OF FRAME frmB
89
   RUN logit("frmB  ENTRY  <== destination FRAME ENTRY (MUST fire)").
90
ON LEAVE OF FRAME frmB
91
   RUN logit("frmB  LEAVE").
92
ON ENTRY OF fB IN FRAME frmB
93
   RUN logit("fB    ENTRY  <== LEGITIMATE destination ENTRY (MUST fire; dropped by bug)").
94
ON LEAVE OF fB IN FRAME frmB
95
   RUN logit("fB    LEAVE").
96

    
97
/* ---- main ---- */
98
OUTPUT TO "tc11180d.log".
99
PUT UNFORMATTED "=== tc11180d_false_steal start ===" SKIP.
100
OUTPUT CLOSE.
101

    
102
CREATE tt. tt.nm = "row1".
103
CREATE tt. tt.nm = "row2".
104
CREATE tt. tt.nm = "row3".
105

    
106
ENABLE fB WITH FRAME frmB.
107
VIEW FRAME frmB.
108

    
109
/* dynamic frame A holding a read-only browse (the source W) */
110
CREATE FRAME hFrmA
111
   ASSIGN TITLE = "FRAME-A (browse source)"
112
          ROW = 2 COLUMN = 2 WIDTH = 34 HEIGHT = 5
113
          BOX = TRUE VISIBLE = FALSE.
114

    
115
OPEN QUERY qA FOR EACH tt.
116

    
117
CREATE BROWSE hBrw
118
   ASSIGN FRAME = hFrmA
119
          QUERY = QUERY qA:HANDLE
120
          X = 4 Y = 4 WIDTH = 30 DOWN = 2
121
          SEPARATORS = TRUE ROW-MARKERS = FALSE
122
          SENSITIVE = TRUE VISIBLE = TRUE.
123
hBrw:ADD-COLUMNS-FROM(BUFFER tt:HANDLE).
124
hBrw:READ-ONLY = TRUE.
125

    
126
hFrmA:VISIBLE = TRUE.
127

    
128
/* Register the source-FRAME LEAVE trigger that deletes A (no steal).
129
   Hooked on the FRAME (not the browse) so it fires from the fix's
130
   sendLeave(oldFrame) at ThinClient ~line 23244, AFTER tryFocusChange -
131
   this is what reaches the "stolen" branch. */
132
ON "LEAVE":U OF hFrmA PERSISTENT RUN aLeave.
133

    
134
RUN logit("--- APPLY ENTRY to browse (land focus in browse source W) ---").
135
APPLY "ENTRY" TO hBrw.
136

    
137
IF VALID-HANDLE(FOCUS) THEN
138
   RUN logit("--- FOCUS before move: TYPE=" + STRING(FOCUS:TYPE)
139
             + " is-browse=" + STRING(FOCUS:TYPE = "BROWSE") + " ---").
140
ELSE
141
   RUN logit("--- FOCUS before move: <none> ---").
142

    
143
RUN logit("--- APPLY ENTRY to fB (legitimate move browse-A -> destination B) ---").
144
APPLY "ENTRY" TO fB IN FRAME frmB.
145

    
146
RUN logit("--- done; FOCUS is now: "
147
          + (IF VALID-HANDLE(FOCUS) THEN STRING(FOCUS:TYPE) + "/" + FOCUS:NAME
148
             ELSE "<none/invalid>") + " ---").
149
RUN logit("--- EXPECT (OE/fixed): fB ENTRY fired, FOCUS=FILL-IN/fB ---").
150
RUN logit("--- BUG   (11180b):    fB ENTRY absent, FOCUS on deleted browse ---").
151
QUIT.