fix(type): focus switch between contenteditables in shadow dom (#13510)

Firefox has a bug: calling `node.focus()` does make the node focused,
but some internal "current contenteditable node" is not changed.
Blurring the previous one and focusing the new one helps.
This commit is contained in:
Dmitry Gozman 2022-04-12 16:44:27 -07:00 committed by GitHub
parent ae4d2e75aa
commit 7a5b070e95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 1 deletions

View file

@ -663,7 +663,14 @@ export class InjectedScript {
return 'error:notconnected';
if (node.nodeType !== Node.ELEMENT_NODE)
throw this.createStacklessError('Node is not an element');
const wasFocused = (node.getRootNode() as (Document | ShadowRoot)).activeElement === node && node.ownerDocument && node.ownerDocument.hasFocus();
const activeElement = (node.getRootNode() as (Document | ShadowRoot)).activeElement;
const wasFocused = activeElement === node && node.ownerDocument && node.ownerDocument.hasFocus();
if (!wasFocused && activeElement && (activeElement as HTMLElement | SVGElement).blur) {
// Workaround the Firefox bug where focusing the element does not switch current
// contenteditable to the new element. However, blurring the previous one helps.
(activeElement as HTMLElement | SVGElement).blur();
}
(node as HTMLElement | SVGElement).focus();
if (resetSelectionIfNotFocused && !wasFocused && node.nodeName.toLowerCase() === 'input') {

View file

@ -476,6 +476,48 @@ it('should support simple copy-pasting', async ({ page, isMac, browserName }) =>
expect(await page.evaluate(() => document.querySelector('div').textContent)).toBe('123123');
});
it('should type repeatedly in contenteditable in shadow dom', async ({ page }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/12941' });
await page.setContent(`
<html>
<body>
<shadow-element></shadow-element>
<script>
customElements.define('shadow-element', class extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.shadowRoot.innerHTML = \`
<style>
.editor { padding: 1rem; margin: 1rem; border: 1px solid #ccc; }
</style>
<div class=editor contenteditable id=foo></div>
<hr>
<section>
<div class=editor contenteditable id=bar></div>
</section>
\`;
}
});
</script>
</body>
</html>
`);
const editor = page.locator('shadow-element > .editor').first();
await editor.type('This is the first box.');
const sectionEditor = page.locator('section .editor');
await sectionEditor.type('This is the second box.');
expect(await editor.textContent()).toBe('This is the first box.');
expect(await sectionEditor.textContent()).toBe('This is the second box.');
});
async function captureLastKeydown(page) {
const lastEvent = await page.evaluateHandle(() => {
const lastEvent = {