fix: emit focus/blur events for webview (backport: 3-0-x) (#14359)

* fix: emit focus/blur events for webview

* test: webview can emit focus event
This commit is contained in:
trop[bot] 2018-08-28 13:38:11 -05:00 committed by Charles Kerr
parent 873f39b159
commit b1c22ba531
5 changed files with 53 additions and 21 deletions

View file

@ -26,6 +26,7 @@ const supportedWebViewEvents = [
'did-navigate',
'did-frame-navigate',
'did-navigate-in-page',
'focus-change',
'close',
'crashed',
'gpu-crashed',
@ -322,6 +323,10 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, embedder
attachGuest(event, embedderFrameId, elementInstanceId, guestInstanceId, params)
})
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', function (event, focus, guestInstanceId) {
event.sender.emit('focus-change', {}, focus, guestInstanceId)
})
// Returns WebContents from its guest id.
const getGuest = function (guestInstanceId) {
const guestInstance = guestInstances[guestInstanceId]

View file

@ -25,6 +25,7 @@ var v8Util = process.atomBinding('v8_util')
v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())
// Use electron module after everything is ready.
const {ipcRenderer} = require('electron')
const {
warnAboutNodeWithRemoteContent,
@ -184,3 +185,15 @@ window.addEventListener('load', function loadHandler () {
window.removeEventListener('load', loadHandler)
})
// Report focus/blur events of webview to browser.
// Note that while Chromium content APIs have observer for focus/blur, they
// unfortunately do not work for webview.
if (process.guestInstanceId) {
window.addEventListener('focus', () => {
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, process.guestInstanceId)
})
window.addEventListener('blur', () => {
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, process.guestInstanceId)
})
}

View file

@ -24,6 +24,7 @@ const WEB_VIEW_EVENTS = {
'did-navigate': ['url', 'httpResponseCode', 'httpStatusText'],
'did-frame-navigate': ['url', 'httpResponseCode', 'httpStatusText', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
'did-navigate-in-page': ['url', 'isMainFrame', 'frameProcessId', 'frameRoutingId'],
'focus-change': ['focus', 'guestInstanceId'],
'close': [],
'crashed': [],
'gpu-crashed': [],
@ -55,6 +56,8 @@ const dispatchEvent = function (webView, eventName, eventKey, ...args) {
webView.dispatchEvent(domEvent)
if (eventName === 'load-commit') {
webView.onLoadCommit(domEvent)
} else if (eventName === 'focus-change') {
webView.onFocusChange(domEvent)
}
}

View file

@ -30,6 +30,7 @@ class WebViewImpl {
v8Util.setHiddenValue(this.webviewNode, 'internal', this)
this.elementAttached = false
this.beforeFirstNavigation = true
this.hasFocus = false
// Check for removed attributes.
for (const attributeName of removedAttributes) {
@ -41,13 +42,21 @@ class WebViewImpl {
// on* Event handlers.
this.on = {}
// Create internal iframe element.
this.internalElement = this.createInternalElement()
const shadowRoot = this.webviewNode.attachShadow({mode: 'open'})
shadowRoot.innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>'
this.setupWebViewAttributes()
this.setupFocusPropagation()
this.viewInstanceId = getNextId()
shadowRoot.appendChild(this.internalElement)
// Provide access to contentWindow.
Object.defineProperty(this.webviewNode, 'contentWindow', {
get: () => {
return this.internalElement.contentWindow
},
enumerable: true
})
}
createInternalElement () {
@ -91,26 +100,6 @@ class WebViewImpl {
})
}
setupFocusPropagation () {
if (!this.webviewNode.hasAttribute('tabIndex')) {
// <webview> needs a tabIndex in order to be focusable.
// TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
// to allow <webview> to be focusable.
// See http://crbug.com/231664.
this.webviewNode.setAttribute('tabIndex', -1)
}
// Focus the BrowserPlugin when the <webview> takes focus.
this.webviewNode.addEventListener('focus', () => {
this.internalElement.focus()
})
// Blur the BrowserPlugin when the <webview> loses focus.
this.webviewNode.addEventListener('blur', () => {
this.internalElement.blur()
})
}
// This observer monitors mutations to attributes of the <webview> and
// updates the BrowserPlugin properties accordingly. In turn, updating
// a BrowserPlugin property will update the corresponding BrowserPlugin
@ -185,6 +174,15 @@ class WebViewImpl {
}
}
// Emits focus/blur events.
onFocusChange () {
const hasFocus = document.activeElement === this.webviewNode
if (hasFocus !== this.hasFocus) {
this.hasFocus = hasFocus
this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'))
}
}
onAttach (storagePartitionId) {
return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId)
}

View file

@ -1283,6 +1283,19 @@ describe('<webview> tag', function () {
expect(secondResizeEvent.newWidth).to.equal(newWidth)
expect(secondResizeEvent.newHeight).to.equal(newHeight)
})
it('emits focus event', async () => {
const domReadySignal = waitForEvent(webview, 'dom-ready')
webview.src = `file://${fixtures}/pages/a.html`
document.body.appendChild(webview)
await domReadySignal
const focusSignal = waitForEvent(webview, 'focus')
webview.contentWindow.focus()
await focusSignal
})
})
describe('zoom behavior', () => {