Prevent Firefox's global paste behavior if paste target is not also focused (#2581)
* Prevent Firefox's global paste behavior if paste target is not also focused * Write a test, fix code thanks to test * Add one more comment to test
This commit is contained in:
@ -5,6 +5,7 @@ import {
|
|||||||
getMovementUtils,
|
getMovementUtils,
|
||||||
wiggleMove,
|
wiggleMove,
|
||||||
doExport,
|
doExport,
|
||||||
|
metaModifier,
|
||||||
} from './test-utils'
|
} from './test-utils'
|
||||||
import waitOn from 'wait-on'
|
import waitOn from 'wait-on'
|
||||||
import { XOR, roundOff, uuidv4 } from 'lib/utils'
|
import { XOR, roundOff, uuidv4 } from 'lib/utils'
|
||||||
@ -4846,3 +4847,51 @@ const part001 = startSketchOn('-XZ')
|
|||||||
// file types in snapshot-tests.spec.ts
|
// file types in snapshot-tests.spec.ts
|
||||||
await expect(page.getByText('Exported successfully')).toBeVisible()
|
await expect(page.getByText('Exported successfully')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Paste should not work unless an input is focused', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
// To run this test locally, uncomment Firefox in playwright.config.ts
|
||||||
|
test.skip(
|
||||||
|
browserName !== 'firefox',
|
||||||
|
"This bug is really Firefox-only, which we don't run in CI."
|
||||||
|
)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/', { waitUntil: 'domcontentloaded' })
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
.waitFor({ state: 'visible' })
|
||||||
|
|
||||||
|
const codeEditorText = page.locator('.cm-content')
|
||||||
|
const pasteContent = `// was this pasted?`
|
||||||
|
const typeContent = `// this should be typed`
|
||||||
|
|
||||||
|
// Load text into the clipboard
|
||||||
|
await page.evaluate((t) => navigator.clipboard.writeText(t), pasteContent)
|
||||||
|
|
||||||
|
// Focus the text editor
|
||||||
|
await codeEditorText.focus()
|
||||||
|
|
||||||
|
// Show that we can type into it
|
||||||
|
await page.keyboard.type(typeContent)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Paste without the code pane focused
|
||||||
|
await codeEditorText.blur()
|
||||||
|
await page.keyboard.press(`${metaModifier}+KeyV`)
|
||||||
|
|
||||||
|
// Show that the paste didn't work but typing did
|
||||||
|
await expect(codeEditorText).not.toContainText(pasteContent)
|
||||||
|
await expect(codeEditorText).toContainText(typeContent)
|
||||||
|
|
||||||
|
// Paste with the code editor focused
|
||||||
|
// Following this guidance: https://github.com/microsoft/playwright/issues/8114
|
||||||
|
await codeEditorText.focus()
|
||||||
|
await page.keyboard.press(`${metaModifier}+KeyV`)
|
||||||
|
await expect(
|
||||||
|
await page.evaluate(
|
||||||
|
() => document.querySelector('.cm-content')?.textContent
|
||||||
|
)
|
||||||
|
).toContain(pasteContent)
|
||||||
|
})
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { test, expect, Page, Download } from '@playwright/test'
|
import { test, expect, Page, Download } from '@playwright/test'
|
||||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
||||||
|
import os from 'os'
|
||||||
import fsp from 'fs/promises'
|
import fsp from 'fs/promises'
|
||||||
import pixelMatch from 'pixelmatch'
|
import pixelMatch from 'pixelmatch'
|
||||||
import { PNG } from 'pngjs'
|
import { PNG } from 'pngjs'
|
||||||
@ -443,3 +444,8 @@ export const doExport = async (
|
|||||||
outputType: output.type,
|
outputType: output.type,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the appropriate modifier key for the platform.
|
||||||
|
*/
|
||||||
|
export const metaModifier = os.platform() === 'darwin' ? 'Meta' : 'Control'
|
||||||
|
@ -34,7 +34,14 @@ export default defineConfig({
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Google Chrome',
|
name: 'Google Chrome',
|
||||||
use: { ...devices['Desktop Chrome'], channel: 'chrome' }, // or 'chrome-beta'
|
use: {
|
||||||
|
...devices['Desktop Chrome'],
|
||||||
|
channel: 'chrome',
|
||||||
|
contextOptions: {
|
||||||
|
/* Chromium is the only one with these permission types */
|
||||||
|
permissions: ['clipboard-write', 'clipboard-read'],
|
||||||
|
},
|
||||||
|
}, // or 'chrome-beta'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'webkit',
|
name: 'webkit',
|
||||||
|
@ -35,6 +35,38 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
overallState === NetworkHealthState.Ok ||
|
overallState === NetworkHealthState.Ok ||
|
||||||
overallState === NetworkHealthState.Weak
|
overallState === NetworkHealthState.Weak
|
||||||
|
|
||||||
|
// Linux has a default behavior to paste text on middle mouse up
|
||||||
|
// This adds a listener to block that pasting if the click target
|
||||||
|
// is not a text input, so users can move in the 3D scene with
|
||||||
|
// middle mouse drag with a text input focused without pasting.
|
||||||
|
useEffect(() => {
|
||||||
|
const handlePaste = (e: ClipboardEvent) => {
|
||||||
|
const isHtmlElement = e.target && e.target instanceof HTMLElement
|
||||||
|
const isEditable =
|
||||||
|
(isHtmlElement && !('explicitOriginalTarget' in e)) ||
|
||||||
|
('explicitOriginalTarget' in e &&
|
||||||
|
((e.explicitOriginalTarget as HTMLElement).contentEditable ===
|
||||||
|
'true' ||
|
||||||
|
['INPUT', 'TEXTAREA'].some(
|
||||||
|
(tagName) =>
|
||||||
|
tagName === (e.explicitOriginalTarget as HTMLElement).tagName
|
||||||
|
)))
|
||||||
|
if (!isEditable) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
e.stopImmediatePropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis?.window?.document?.addEventListener('paste', handlePaste, {
|
||||||
|
capture: true,
|
||||||
|
})
|
||||||
|
return () =>
|
||||||
|
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
||||||
|
capture: true,
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
typeof window === 'undefined' ||
|
typeof window === 'undefined' ||
|
||||||
|
Reference in New Issue
Block a user