@@ -29,8 +29,11 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
2929 private isFirefoxBrowser =
3030 globalThis . navigator . userAgent . indexOf ( " Firefox/" ) !== - 1 ||
3131 globalThis . navigator . userAgent . indexOf ( " Gecko/" ) !== - 1 ;
32- private buttonElement : HTMLElement ;
33- private listElement : HTMLElement ;
32+ private buttonElement ?: HTMLElement ;
33+ private listElement ?: HTMLElement ;
34+ private htmlMutationObserver : MutationObserver ;
35+ private bodyMutationObserver : MutationObserver ;
36+ private pageIsOpaque = true ;
3437 private inlineMenuElementsMutationObserver : MutationObserver ;
3538 private containerElementMutationObserver : MutationObserver ;
3639 private mutationObserverIterations = 0 ;
@@ -49,6 +52,7 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
4952 } ;
5053
5154 constructor ( ) {
55+ this . checkPageOpacity ( ) ;
5256 this . setupMutationObserver ( ) ;
5357 }
5458
@@ -281,6 +285,9 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
281285 * that the inline menu elements are always present at the bottom of the menu container.
282286 */
283287 private setupMutationObserver = ( ) => {
288+ this . htmlMutationObserver = new MutationObserver ( this . handlePageMutations ) ;
289+ this . bodyMutationObserver = new MutationObserver ( this . handlePageMutations ) ;
290+
284291 this . inlineMenuElementsMutationObserver = new MutationObserver (
285292 this . handleInlineMenuElementMutationObserverUpdate ,
286293 ) ;
@@ -295,6 +302,9 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
295302 * elements are not modified by the website.
296303 */
297304 private observeCustomElements ( ) {
305+ this . htmlMutationObserver ?. observe ( document . querySelector ( "html" ) , { attributes : true } ) ;
306+ this . bodyMutationObserver ?. observe ( document . body , { attributes : true } ) ;
307+
298308 if ( this . buttonElement ) {
299309 this . inlineMenuElementsMutationObserver ?. observe ( this . buttonElement , {
300310 attributes : true ,
@@ -395,11 +405,56 @@ export class AutofillInlineMenuContentService implements AutofillInlineMenuConte
395405 } ) ;
396406 } ;
397407
408+ private checkPageOpacity = ( ) => {
409+ this . pageIsOpaque = this . getPageIsOpaque ( ) ;
410+
411+ if ( ! this . pageIsOpaque ) {
412+ this . closeInlineMenu ( ) ;
413+ }
414+ } ;
415+
416+ private handlePageMutations = ( mutations : MutationRecord [ ] ) => {
417+ for ( const mutation of mutations ) {
418+ if ( mutation . type === "attributes" ) {
419+ this . checkPageOpacity ( ) ;
420+ }
421+ }
422+ } ;
423+
424+ /**
425+ * Checks the opacity of the page body and body parent, since the inline menu experience
426+ * will inherit the opacity, despite being otherwise encapsulated from styling changes
427+ * of parents below the body. Assumes the target element will be a direct child of the page
428+ * `body` (enforced elsewhere).
429+ */
430+ private getPageIsOpaque ( ) {
431+ // These are computed style values, so we don't need to worry about non-float values
432+ // for `opacity`, here
433+ const htmlOpacity = globalThis . window . getComputedStyle (
434+ globalThis . document . querySelector ( "html" ) ,
435+ ) . opacity ;
436+ const bodyOpacity = globalThis . window . getComputedStyle (
437+ globalThis . document . querySelector ( "body" ) ,
438+ ) . opacity ;
439+
440+ // Any value above this is considered "opaque" for our purposes
441+ const opacityThreshold = 0.6 ;
442+
443+ return parseFloat ( htmlOpacity ) > opacityThreshold && parseFloat ( bodyOpacity ) > opacityThreshold ;
444+ }
445+
398446 /**
399447 * Processes the mutation of the element that contains the inline menu. Will trigger when an
400448 * idle moment in the execution of the main thread is detected.
401449 */
402450 private processContainerElementMutation = async ( containerElement : HTMLElement ) => {
451+ // If the computed opacity of the body and parent is not sufficiently opaque, tear
452+ // down and prevent building the inline menu experience.
453+ this . checkPageOpacity ( ) ;
454+ if ( ! this . pageIsOpaque ) {
455+ return ;
456+ }
457+
403458 const lastChild = containerElement . lastElementChild ;
404459 const secondToLastChild = lastChild ?. previousElementSibling ;
405460 const lastChildIsInlineMenuList = lastChild === this . listElement ;
0 commit comments