fix: do not allow the window to grab focus when tabbing / shift+tabbing (#16042)
* fix: do not allow the window to grab focus when tabbing / shift+tabbing * test: add tests.
This commit is contained in:
parent
ed3e5739dc
commit
9c783f53ba
5 changed files with 171 additions and 0 deletions
|
@ -346,6 +346,19 @@ blink::WebSecurityStyle CommonWebContentsDelegate::GetSecurityStyle(
|
|||
security_style_explanations);
|
||||
}
|
||||
|
||||
bool CommonWebContentsDelegate::TakeFocus(content::WebContents* source,
|
||||
bool reverse) {
|
||||
if (source && source->GetOutermostWebContents() == source) {
|
||||
// If this is the outermost web contents and the user has tabbed or
|
||||
// shift + tabbed through all the elements, reset the focus back to
|
||||
// the first or last element so that it doesn't stay in the body.
|
||||
source->FocusThroughTabTraversal(reverse);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CommonWebContentsDelegate::DevToolsSaveToFile(const std::string& url,
|
||||
const std::string& content,
|
||||
bool save_as) {
|
||||
|
|
|
@ -98,6 +98,7 @@ class CommonWebContentsDelegate : public content::WebContentsDelegate,
|
|||
blink::WebSecurityStyle GetSecurityStyle(
|
||||
content::WebContents* web_contents,
|
||||
content::SecurityStyleExplanations* explanations) override;
|
||||
bool TakeFocus(content::WebContents* source, bool reverse) override;
|
||||
void HandleKeyboardEvent(
|
||||
content::WebContents* source,
|
||||
const content::NativeWebKeyboardEvent& event) override;
|
||||
|
|
|
@ -1410,6 +1410,111 @@ describe('chromium feature', () => {
|
|||
await new Promise((resolve) => { utter.onend = resolve })
|
||||
})
|
||||
})
|
||||
|
||||
describe('focus handling', () => {
|
||||
let webviewContents = null
|
||||
|
||||
beforeEach(async () => {
|
||||
w = new BrowserWindow({
|
||||
show: true
|
||||
})
|
||||
|
||||
const webviewReady = emittedOnce(w.webContents, 'did-attach-webview')
|
||||
await w.loadFile(path.join(fixtures, 'pages', 'tab-focus-loop-elements.html'))
|
||||
const [, wvContents] = await webviewReady
|
||||
webviewContents = wvContents
|
||||
await emittedOnce(webviewContents, 'did-finish-load')
|
||||
w.focus()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
webviewContents = null
|
||||
})
|
||||
|
||||
const expectFocusChange = async () => {
|
||||
const [, focusedElementId] = await emittedOnce(ipcMain, 'focus-changed')
|
||||
return focusedElementId
|
||||
}
|
||||
|
||||
describe('a TAB press', () => {
|
||||
const tabPressEvent = {
|
||||
type: 'keyDown',
|
||||
keyCode: 'Tab'
|
||||
}
|
||||
|
||||
it('moves focus to the next focusable item', async () => {
|
||||
let focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(tabPressEvent)
|
||||
let focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-1', `should start focused in element-1, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(tabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-2', `focus should've moved to element-2, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(tabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-wv-element-1', `focus should've moved to the webview's element-1, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
webviewContents.sendInputEvent(tabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-wv-element-2', `focus should've moved to the webview's element-2, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
webviewContents.sendInputEvent(tabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-3', `focus should've moved to element-3, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(tabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-1', `focus should've looped back to element-1, it's instead in ${focusedElementId}`)
|
||||
})
|
||||
})
|
||||
|
||||
describe('a SHIFT + TAB press', () => {
|
||||
const shiftTabPressEvent = {
|
||||
type: 'keyDown',
|
||||
modifiers: ['Shift'],
|
||||
keyCode: 'Tab'
|
||||
}
|
||||
|
||||
it('moves focus to the previous focusable item', async () => {
|
||||
let focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(shiftTabPressEvent)
|
||||
let focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-3', `should start focused in element-3, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(shiftTabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-wv-element-2', `focus should've moved to the webview's element-2, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
webviewContents.sendInputEvent(shiftTabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-wv-element-1', `focus should've moved to the webview's element-1, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
webviewContents.sendInputEvent(shiftTabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-2', `focus should've moved to element-2, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(shiftTabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-1', `focus should've moved to element-1, it's instead in ${focusedElementId}`)
|
||||
|
||||
focusChange = expectFocusChange()
|
||||
w.webContents.sendInputEvent(shiftTabPressEvent)
|
||||
focusedElementId = await focusChange
|
||||
assert.strictEqual(focusedElementId, 'BUTTON-element-3', `focus should've looped back to element-3, it's instead in ${focusedElementId}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('font fallback', () => {
|
||||
|
|
25
spec/fixtures/pages/tab-focus-loop-elements-wv.html
vendored
Normal file
25
spec/fixtures/pages/tab-focus-loop-elements-wv.html
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<body>
|
||||
<div>
|
||||
<button id="wv-element-1">Button 1</button>
|
||||
<button id="wv-element-2">Button 2</button>
|
||||
</div>
|
||||
<script>
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
function handleFocusChange(event) {
|
||||
if (event.target.tagName) {
|
||||
const elementId = event.target.id ? `-${event.target.id}` : ''
|
||||
const elementIdentifier = `${event.target.tagName}${elementId}`
|
||||
ipcRenderer.send('focus-changed', elementIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener('focus', handleFocusChange, true)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
27
spec/fixtures/pages/tab-focus-loop-elements.html
vendored
Normal file
27
spec/fixtures/pages/tab-focus-loop-elements.html
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
<script>
|
||||
const { ipcRenderer } = require('electron')
|
||||
|
||||
function handleFocusChange(event) {
|
||||
if (event.target.tagName && event.target.tagName !== 'WEBVIEW') {
|
||||
const elementId = event.target.id ? `-${event.target.id}` : ''
|
||||
const elementIdentifier = `${event.target.tagName}${elementId}`
|
||||
ipcRenderer.send('focus-changed', elementIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener('focus', handleFocusChange, true)
|
||||
</script>
|
||||
<body>
|
||||
<div>
|
||||
<button id="element-1">Button 1</button>
|
||||
<button id="element-2">Button 2</button>
|
||||
<webview src="tab-focus-loop-elements-wv.html" nodeintegration></webview>
|
||||
<button id="element-3">Button 3</button>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue