@@ -81,7 +81,7 @@ class DKIMWarningsTooltipXUL {
81
81
return ;
82
82
}
83
83
84
- /** @type {DKIMTooltipElement } */
84
+ /** @type {DKIMWarningsTooltipXULElement } */
85
85
// @ts -expect-error
86
86
this . element = document . createXULElement ( "tooltip" ) ;
87
87
@@ -154,9 +154,6 @@ class DKIMTooltip {
154
154
this . element . style . position = "absolute" ;
155
155
this . element . style . zIndex = "99" ;
156
156
157
- // Bottom Tooltip
158
- this . element . style . top = "calc(100% + 10px)" ;
159
-
160
157
this . element . style . backgroundColor = "var(--arrowpanel-background)" ;
161
158
this . element . style . color = "var(--arrowpanel-color)" ;
162
159
this . element . style . borderStyle = "solid" ;
@@ -171,20 +168,30 @@ class DKIMTooltip {
171
168
}
172
169
173
170
/**
174
- * Add the tooltip to the given parent .
171
+ * Add the tooltip to the given target .
175
172
*
176
173
* @protected
177
- * @param {HTMLElement } parent
174
+ * @param {DKIMTooltipTarget } target
178
175
*/
179
- add ( parent ) {
180
- const parentSyle = parent . ownerDocument . defaultView ?. getComputedStyle ( parent ) ;
181
- if ( parentSyle ?. position === "static" ) {
182
- parent . style . position = "relative" ;
176
+ add ( target ) {
177
+ if ( target . _dkimTooltip ) {
178
+ throw new Error ( "DKIM: A DKIMTooltip already exist on target" ) ;
179
+ }
180
+ if ( this . element . _target ) {
181
+ throw new Error ( "DKIM: The DKIMTooltip was already added to a target" ) ;
183
182
}
184
183
185
- parent . appendChild ( this . element ) ;
186
- parent . addEventListener ( "mouseenter" , this . element . _dkimOnmouseenter ) ;
187
- parent . addEventListener ( "mouseleave" , this . element . _dkimOnmouseleave ) ;
184
+ target . _dkimTooltip = this . element ;
185
+ this . element . _target = target ;
186
+
187
+ // The tooltip is added to the body instead of the target
188
+ // to avoid potential issues with overflow.
189
+ // See also
190
+ // - https://stackoverflow.com/questions/36531708/why-does-position-absolute-make-page-to-overflow
191
+ // - https://stackoverflow.com/questions/6421966/css-overflow-x-visible-and-overflow-y-hidden-causing-scrollbar-issue
192
+ target . ownerDocument . body . appendChild ( this . element ) ;
193
+ target . addEventListener ( "mouseenter" , this . element . _dkimOnmouseenter ) ;
194
+ target . addEventListener ( "mouseleave" , this . element . _dkimOnmouseleave ) ;
188
195
}
189
196
190
197
/**
@@ -193,10 +200,11 @@ class DKIMTooltip {
193
200
* @protected
194
201
*/
195
202
remove ( ) {
196
- const parent = this . element . parentElement ;
197
- if ( parent ) {
198
- parent . removeEventListener ( "mouseenter" , this . element . _dkimOnmouseenter ) ;
199
- parent . removeEventListener ( "mouseleave" , this . element . _dkimOnmouseleave ) ;
203
+ const target = this . element . _target ;
204
+ if ( target ) {
205
+ target . removeEventListener ( "mouseenter" , this . element . _dkimOnmouseenter ) ;
206
+ target . removeEventListener ( "mouseleave" , this . element . _dkimOnmouseleave ) ;
207
+ delete target . _dkimTooltip ;
200
208
}
201
209
this . element . remove ( ) ;
202
210
}
@@ -206,10 +214,24 @@ class DKIMTooltip {
206
214
* @param {MouseEvent } _event
207
215
*/
208
216
static #mouseEnter( tooltip , _event ) {
209
- if ( tooltip . element . parentElement ?. title ) {
210
- tooltip . element . parentElement . dataset . titleBackup = tooltip . element . parentElement . title ;
211
- tooltip . element . parentElement . title = "" ;
217
+ const target = tooltip . element . _target ;
218
+ if ( ! target ) {
219
+ throw new Error ( "DKIM: mouseEnter event called for a DKIMTooltip that has no target" ) ;
212
220
}
221
+
222
+ // Avoid title being shown together with tooltip
223
+ if ( target ?. title ) {
224
+ target . dataset . titleBackup = target . title ;
225
+ target . title = "" ;
226
+ }
227
+
228
+ // Calculate and set tooltip position
229
+ const clientRect = target . getBoundingClientRect ( ) ;
230
+ const tooltipSpaceToTarget = 10 ;
231
+ tooltip . element . style . top = `${ clientRect . bottom + tooltipSpaceToTarget } px` ;
232
+ tooltip . element . style . left = `${ clientRect . left } px` ;
233
+
234
+ // Show tooltip
213
235
tooltip . element . style . visibility = "visible" ;
214
236
}
215
237
@@ -218,10 +240,14 @@ class DKIMTooltip {
218
240
* @param {MouseEvent } _event
219
241
*/
220
242
static #mouseLeave( tooltip , _event ) {
243
+ // Hide tooltip
221
244
tooltip . element . style . visibility = "hidden" ;
222
- if ( tooltip . element . parentElement ?. dataset . titleBackup ) {
223
- tooltip . element . parentElement . title = tooltip . element . parentElement . dataset . titleBackup ;
224
- tooltip . element . parentElement . dataset . titleBackup = "" ;
245
+
246
+ // Restore title
247
+ const target = tooltip . element . _target ;
248
+ if ( target ?. dataset . titleBackup ) {
249
+ target . title = target . dataset . titleBackup ;
250
+ target . dataset . titleBackup = "" ;
225
251
}
226
252
}
227
253
}
@@ -294,7 +320,7 @@ class DkimResultTooltip extends DKIMTooltip {
294
320
*
295
321
* @param {string[] } warnings
296
322
*/
297
- set warnings ( warnings ) {
323
+ set warnings ( warnings ) {
298
324
if ( ! this . element . _warningsBox ) {
299
325
throw Error ( "Underlying element of DkimResultTooltip does not contain _warningsBox" ) ;
300
326
}
@@ -326,15 +352,28 @@ class DkimResultTooltip extends DKIMTooltip {
326
352
}
327
353
328
354
/**
329
- * Get all tooltips under the given document or parent.
355
+ * Try getting the tooltip of a target.
356
+ *
357
+ * @param {DKIMTooltipTarget } target
358
+ * @returns {DkimResultTooltip|null }
359
+ */
360
+ static get ( target ) {
361
+ if ( target . _dkimTooltip ) {
362
+ return new DkimResultTooltip ( target . _dkimTooltip . ownerDocument , target . _dkimTooltip ) ;
363
+ }
364
+ return null ;
365
+ }
366
+
367
+ /**
368
+ * Get all tooltips in the given document.
330
369
*
331
- * @param {Document|Element } searchRoot
370
+ * @param {Document } document
332
371
* @returns {DkimResultTooltip[] }
333
372
*/
334
- static get ( searchRoot ) {
373
+ static getAll ( document ) {
335
374
// eslint-disable-next-line no-extra-parens
336
375
const elements = /** @type {HTMLElement[] } */ (
337
- Array . from ( searchRoot . getElementsByClassName ( DkimResultTooltip . #class) ) ) ;
376
+ Array . from ( document . getElementsByClassName ( DkimResultTooltip . #class) ) ) ;
338
377
const tooltips = [ ] ;
339
378
for ( const element of elements ) {
340
379
tooltips . push ( new DkimResultTooltip ( element . ownerDocument , element ) ) ;
@@ -343,33 +382,31 @@ class DkimResultTooltip extends DKIMTooltip {
343
382
}
344
383
345
384
/**
346
- * Add the tooltip for the given parent .
385
+ * Add a tooltip to the given target .
347
386
*
348
- * @param {HTMLElement } parent
387
+ * @param {DKIMTooltipTarget } target
349
388
* @returns {DkimResultTooltip }
350
389
*/
351
- static add ( parent ) {
352
- const existingTooltip = DkimResultTooltip . get ( parent ) [ 0 ] ;
390
+ static add ( target ) {
391
+ const existingTooltip = DkimResultTooltip . get ( target ) ;
353
392
if ( existingTooltip ) {
354
393
console . warn ( "DKIM: DkimResultTooltip already exist and will be reused" ) ;
355
394
return existingTooltip ;
356
395
}
357
396
358
- const tooltip = new DkimResultTooltip ( parent . ownerDocument ) ;
359
- tooltip . add ( parent ) ;
397
+ const tooltip = new DkimResultTooltip ( target . ownerDocument ) ;
398
+ tooltip . add ( target ) ;
360
399
return tooltip ;
361
400
}
362
401
363
402
/**
364
- * Remove the tooltip from the given parent .
403
+ * Remove an existing tooltip from the given target .
365
404
*
366
- * @param {HTMLElement } parent
405
+ * @param {HTMLElement } target
367
406
*/
368
- static remove ( parent ) {
369
- const tooltips = DkimResultTooltip . get ( parent ) ;
370
- for ( const tooltip of tooltips ) {
371
- tooltip . remove ( ) ;
372
- }
407
+ static remove ( target ) {
408
+ const tooltip = DkimResultTooltip . get ( target ) ;
409
+ tooltip ?. remove ( ) ;
373
410
}
374
411
375
412
static #class = "DkimResultTooltip" ;
@@ -727,8 +764,9 @@ class DkimFavicon {
727
764
if ( element ) {
728
765
// @ts -expect-error
729
766
this . element = element ;
730
- this . _dkimTooltipFrom = DkimResultTooltip . get ( this . element ) [ 0 ] ;
767
+ this . _dkimTooltipFrom = DkimResultTooltip . get ( this . element ) ;
731
768
if ( ! this . _dkimTooltipFrom ) {
769
+ console . warn ( "DKIM: DkimResultTooltip for DkimFavicon not found - will recreate it" ) ;
732
770
this . _dkimTooltipFrom = DkimResultTooltip . add ( this . element ) ;
733
771
}
734
772
return ;
@@ -821,16 +859,6 @@ class DkimFavicon {
821
859
// TB <102
822
860
expandedFromBox . prepend ( favicon . element ) ;
823
861
}
824
-
825
- // Add the tooltip used by the favicon and the from address.
826
- // As the tooltip is reused, it can not be defined directly under the favicon.
827
- if ( "longEmailAddresses" in expandedFromBox ) {
828
- // TB <102
829
- expandedFromBox . longEmailAddresses . prepend ( favicon . _dkimTooltipFrom . element ) ;
830
- } else {
831
- // TB >=102
832
- expandedFromBox . prepend ( favicon . _dkimTooltipFrom . element ) ;
833
- }
834
862
}
835
863
836
864
/**
@@ -1226,7 +1254,7 @@ this.dkimHeader = class extends ExtensionCommon.ExtensionAPI {
1226
1254
const favicon = DkimFavicon . get ( document ) ;
1227
1255
favicon . setFaviconUrl ( faviconUrl ) ;
1228
1256
1229
- const resultTooltips = DkimResultTooltip . get ( document ) ;
1257
+ const resultTooltips = DkimResultTooltip . getAll ( document ) ;
1230
1258
for ( const tooltip of resultTooltips ) {
1231
1259
tooltip . value = result ;
1232
1260
tooltip . warnings = warnings ;
0 commit comments