feat: add onclose method to MessagePort (#22532)

* feat: add onclose method to MessagePort

* more scope, more good

* de-flake GC test
This commit is contained in:
Jeremy Apthorp 2020-03-13 10:00:50 -07:00 committed by GitHub
parent 829d4815a9
commit 0c02d794c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 114 additions and 0 deletions

View file

@ -45,5 +45,9 @@ Returns:
Emitted when a MessagePortMain object receives a message. Emitted when a MessagePortMain object receives a message.
#### Event: 'close'
Emitted when the remote end of a MessagePortMain object becomes disconnected.
[`MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort [`MessagePort`]: https://developer.mozilla.org/en-US/docs/Web/API/MessagePort
[Channel Messaging API]: https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API [Channel Messaging API]: https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API

View file

@ -91,3 +91,4 @@ feat_enable_offscreen_rendering_with_viz_compositor.patch
delay_lock_the_protocol_scheme_registry.patch delay_lock_the_protocol_scheme_registry.patch
gpu_notify_when_dxdiag_request_fails.patch gpu_notify_when_dxdiag_request_fails.patch
feat_allow_embedders_to_add_observers_on_created_hunspell.patch feat_allow_embedders_to_add_observers_on_created_hunspell.patch
feat_add_onclose_to_messageport.patch

View file

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Wed, 4 Mar 2020 11:18:03 -0800
Subject: feat: add onclose to MessagePort
This adds the 'onclose' event to MessagePort. This is
[proposed](https://github.com/w3ctag/design-reviews/issues/269#issuecomment-407584290)
in w3c and has been discussed for years without conclusion. I'd like to
get this standardised, but in lieu of that, this makes MessagePort a
whole bunch more useful!
diff --git a/third_party/blink/renderer/core/messaging/message_port.cc b/third_party/blink/renderer/core/messaging/message_port.cc
index e2ddbafc9ea2836a302da481702e2922949ffe78..7da7071862e384bce65ecb52996b5287891a33f9 100644
--- a/third_party/blink/renderer/core/messaging/message_port.cc
+++ b/third_party/blink/renderer/core/messaging/message_port.cc
@@ -156,6 +156,7 @@ void MessagePort::close() {
Entangle(mojo::MessagePipe().handle0);
}
closed_ = true;
+ DispatchEvent(*Event::Create(event_type_names::kClose));
}
void MessagePort::Entangle(mojo::ScopedMessagePipeHandle handle) {
diff --git a/third_party/blink/renderer/core/messaging/message_port.h b/third_party/blink/renderer/core/messaging/message_port.h
index 2a08335398b30671a61aee0f1ebe060222a4f1ff..874aecb9c038f19cc03641a19ce51cf2f958d80c 100644
--- a/third_party/blink/renderer/core/messaging/message_port.h
+++ b/third_party/blink/renderer/core/messaging/message_port.h
@@ -119,6 +119,13 @@ class CORE_EXPORT MessagePort : public EventTargetWithInlineData,
return GetAttributeEventListener(event_type_names::kMessageerror);
}
+ void setOnclose(EventListener* listener) {
+ SetAttributeEventListener(event_type_names::kClose, listener);
+ }
+ EventListener* onclose() {
+ return GetAttributeEventListener(event_type_names::kClose);
+ }
+
// A port starts out its life entangled, and remains entangled until it is
// closed or is cloned.
bool IsEntangled() const { return !closed_ && !IsNeutered(); }
diff --git a/third_party/blink/renderer/core/messaging/message_port.idl b/third_party/blink/renderer/core/messaging/message_port.idl
index 6fab27fcdf1c333739b6ffe88b3cc4eed3301ee4..3f1f181d9b8a66997136f870f55c97c08294b6eb 100644
--- a/third_party/blink/renderer/core/messaging/message_port.idl
+++ b/third_party/blink/renderer/core/messaging/message_port.idl
@@ -40,4 +40,5 @@
// event handlers
attribute EventHandler onmessage;
attribute EventHandler onmessageerror;
+ attribute EventHandler onclose;
};

View file

@ -103,6 +103,12 @@ void MessagePort::Close() {
closed_ = true; closed_ = true;
if (!HasPendingActivity()) if (!HasPendingActivity())
Unpin(); Unpin();
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
v8::Local<v8::Object> self;
if (GetWrapper(isolate).ToLocal(&self))
gin_helper::EmitEvent(isolate, self, "close");
} }
void MessagePort::Entangle(mojo::ScopedMessagePipeHandle handle) { void MessagePort::Entangle(mojo::ScopedMessagePipeHandle handle) {
@ -196,6 +202,7 @@ void MessagePort::Pin() {
if (!pinned_.IsEmpty()) if (!pinned_.IsEmpty())
return; return;
v8::Isolate* isolate = v8::Isolate::GetCurrent(); v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
v8::Local<v8::Value> self; v8::Local<v8::Value> self;
if (GetWrapper(isolate).ToLocal(&self)) { if (GetWrapper(isolate).ToLocal(&self)) {
pinned_.Reset(isolate, self); pinned_.Reset(isolate, self);

View file

@ -278,6 +278,57 @@ describe('ipc module', () => {
expect(data).to.equal('a message') expect(data).to.equal('a message')
}) })
describe('close event', () => {
describe('in renderer', () => {
it('is emitted when the main process closes its end of the port', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } })
w.loadURL('about:blank')
await w.webContents.executeJavaScript(`(${function () {
const { ipcRenderer } = require('electron')
ipcRenderer.on('port', (e) => {
const [port] = e.ports
port.start();
(port as any).onclose = () => {
ipcRenderer.send('closed')
}
})
}})()`)
const { port1, port2 } = new MessageChannelMain()
w.webContents.postMessage('port', null, [port2])
port1.close()
await emittedOnce(ipcMain, 'closed')
})
it('is emitted when the other end of a port is garbage-collected', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } })
w.loadURL('about:blank')
await w.webContents.executeJavaScript(`(${async function () {
const { port2 } = new MessageChannel()
await new Promise(resolve => {
port2.start();
(port2 as any).onclose = resolve
process.electronBinding('v8_util').requestGarbageCollectionForTesting()
})
}})()`)
})
it('is emitted when the other end of a port is sent to nowhere', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } })
w.loadURL('about:blank')
ipcMain.once('do-a-gc', () => v8Util.requestGarbageCollectionForTesting())
await w.webContents.executeJavaScript(`(${async function () {
const { port1, port2 } = new MessageChannel()
await new Promise(resolve => {
port2.start();
(port2 as any).onclose = resolve
require('electron').ipcRenderer.postMessage('nobody-listening', null, [port1])
require('electron').ipcRenderer.send('do-a-gc')
})
}})()`)
})
})
})
describe('MessageChannelMain', () => { describe('MessageChannelMain', () => {
it('can be created', () => { it('can be created', () => {
const { port1, port2 } = new MessageChannelMain() const { port1, port2 } = new MessageChannelMain()