From e46f2cd9bf69effe927e484d48a4d06888f5aa9d Mon Sep 17 00:00:00 2001 From: Steve Repsher Date: Tue, 19 Sep 2023 08:51:38 -0400 Subject: [PATCH] Adjust password manager polyfill for form injections (#17830) --- src/auth/ha-password-manager-polyfill.ts | 91 ++++++++++++++++++------ 1 file changed, 68 insertions(+), 23 deletions(-) diff --git a/src/auth/ha-password-manager-polyfill.ts b/src/auth/ha-password-manager-polyfill.ts index 99e6e1926f21..7c66d11fe3e6 100644 --- a/src/auth/ha-password-manager-polyfill.ts +++ b/src/auth/ha-password-manager-polyfill.ts @@ -35,20 +35,47 @@ export class HaPasswordManagerPolyfill extends LitElement { super.connectedCallback(); this._styleElement = document.createElement("style"); this._styleElement.textContent = css` + /* Polyfill form is sized and vertically aligned with true form, then positioned offscreen + rather than hiding so it does not create a new stacking context */ .password-manager-polyfill { position: absolute; - opacity: 0; - z-index: -1; + box-sizing: border-box; + } + /* Excluding our wrapper, move any children back on screen, including anything injected that might not already be positioned */ + .password-manager-polyfill > *:not(.wrapper), + .password-manager-polyfill > .wrapper > * { + position: relative; + left: 10000px; } - .password-manager-polyfill input { + /* Size and hide our polyfill fields */ + .password-manager-polyfill .underneath { + display: block; + box-sizing: border-box; width: 100%; - height: 62px; - padding: 0; + padding: 0 16px; border: 0; + z-index: -1; + height: 21px; + /* Transparency is only needed to hide during paint or in case of misalignment, + but LastPass will fail if it's 0, so we use 1% */ + opacity: 0.01; + } + .password-manager-polyfill input.underneath { + height: 28px; + margin-bottom: 30.5px; + } + /* Button position is not important, but size should not be zero */ + .password-manager-polyfill > input.underneath[type="submit"] { + width: 1px; + height: 1px; + margin: 0 auto; + overflow: hidden; } - .password-manager-polyfill input[type="submit"] { - width: 0; - height: 0; + /* Ensure injected elements will be on top */ + .password-manager-polyfill > *:not(.underneath, .wrapper), + .password-manager-polyfill > .wrapper > *:not(.underneath) { + isolation: isolate; + z-index: auto; } `.toString(); document.head.append(this._styleElement); @@ -77,16 +104,25 @@ export class HaPasswordManagerPolyfill extends LitElement { class="password-manager-polyfill" style=${styleMap({ top: `${this.boundingRect?.y || 148}px`, - left: `calc(50% - ${(this.boundingRect?.width || 360) / 2}px)`, + left: `calc(50% - ${ + (this.boundingRect?.width || 360) / 2 + }px - 10000px)`, width: `${this.boundingRect?.width || 360}px`, })} - aria-hidden="true" + action="/auth" + method="post" @submit=${this._handleSubmit} > ${autocompleteLoginFields(this.step.data_schema).map((input) => this.render_input(input) )} - + `; } @@ -99,26 +135,35 @@ export class HaPasswordManagerPolyfill extends LitElement { return ""; } return html` - + + + + `; } - private _handleSubmit(ev: Event) { + private _handleSubmit(ev: SubmitEvent) { ev.preventDefault(); fireEvent(this, "form-submitted"); } private _valueChanged(ev: Event) { - const target = ev.target! as HTMLInputElement; + const target = ev.target as HTMLInputElement; this.stepData = { ...this.stepData, [target.id]: target.value }; fireEvent(this, "value-changed", { value: this.stepData,