Skip to content

Commit 6750146

Browse files
authored
Update SharedBuffer.md
1 parent 4f98055 commit 6750146

File tree

1 file changed

+232
-48
lines changed

1 file changed

+232
-48
lines changed

specs/SharedBuffer.md

Lines changed: 232 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ Besides using the memory address directly, the shared buffer object can be acces
2828
from native side via an IStream* object that you can get from the shared buffer object.
2929

3030
When the application code calls `PostSharedBufferToScript`, the script side will
31-
receive a `SharedBufferReceived` event containing the buffer as an `ArrayBuffer` object.
32-
After receiving the shared buffer object, it can access it the same way as any other
31+
receive a `sharedbufferreceived` event that you can get from it the buffer as an `ArrayBuffer` object.
32+
After getting the shared buffer, it can access it the same way as any other
3333
ArrayBuffer object, including transferring to a web worker to process the data on
3434
the worker thread.
3535

@@ -60,10 +60,10 @@ The script code will look like this:
6060
6161
function SharedBufferReceived(e) {
6262
if (e.additionalData && e.additionalData.contosoBufferKind == "contosoDisplayBuffer") {
63-
let displayBuffer = e.getBuffer();
63+
let displayBuffer = e.buffer;
6464
// Consume the data from the buffer (in the form of an ArrayBuffer)
65-
let view = new UInt32Array(displayBuffer);
66-
DisplaySharedBufferData(view);
65+
let displayBufferArray = new Uint8Array(displayBuffer);
66+
DisplaySharedBufferData(displayBufferArray);
6767
// Release the buffer after consuming the data.
6868
chrome.webview.releaseBuffer(displayBuffer);
6969
}
@@ -73,50 +73,211 @@ The script code will look like this:
7373
```cpp
7474

7575
wil::com_ptr<ICoreWebView2SharedBuffer> sharedBuffer;
76-
CHECK_FAILURE(webviewEnvironment->CreateSharedBuffer(bufferSize, &sharedBuffer));
77-
// Fill data into the shared memory via IStream.
78-
wil::com_ptr<IStream> stream;
79-
CHECK_FAILURE(sharedBuffer->OpenStream(&stream));
80-
CHECK_FAILURE(stream->Write(data, dataSize, nullptr));
76+
CHECK_FAILURE(m_webviewEnvironment->CreateSharedBuffer(dataSize, &sharedBuffer));
77+
BYTE* buffer;
78+
CHECK_FAILURE(sharedBuffer->get_Buffer(&buffer));
79+
// Fill buffer with data.
80+
memcpy_s(buffer, dataSize, data, dataSize);
8181
PCWSTR additionalDataAsJson = L"{\"contosoBufferKind\":\"contosoDisplayBuffer\"}";
82-
if (forFrame)
83-
{
84-
m_webviewFrame->PostSharedBufferToScript(
85-
sharedBuffer.get(), COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_ONLY, additionalDataAsJson);
86-
}
87-
else
88-
{
89-
m_webView->PostSharedBufferToScript(
90-
sharedBuffer.get(), COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_ONLY, additionalDataAsJson);
91-
}
92-
// Explicitly close the one time shared buffer to ensure that the resource is released timely.
82+
m_webView->PostSharedBufferToScript(
83+
sharedBuffer.get(), COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_ONLY, additionalDataAsJson);
84+
85+
// The buffer automatically releases any resources if all references to it are released.
86+
// As we are doing here, you could also explicitly close it when you don't access the buffer
87+
// any more.to ensure that the resource is released timely even if there are some reference got leaked.
9388
sharedBuffer->Close();
9489
9590
```
96-
## WinRT and .NET
91+
## .NET
9792
```c#
98-
using (CoreWebView2SharedBuffer sharedBuffer = WebViewEnvironment.CreateSharedBuffer(bufferSize))
93+
using (CoreWebView2SharedBuffer sharedBuffer = WebViewEnvironment.CreateSharedBuffer(dataSize))
9994
{
100-
// Fill data using access Stream
101-
using (Stream stream = sharedBuffer.OpenStream())
95+
// Fill buffer with data.
96+
unsafe
10297
{
103-
using (StreamWriter writer = new StreamWriter(stream))
104-
{
105-
writer.Write(data);
98+
byte* buffer = (byte*)(sharedBuffer.Buffer.ToPointer());
99+
ulong dataToCopy = dataSize;
100+
while (dataToCopy-- > 0) {
101+
*buffer++ = *data++;
106102
}
107-
}
103+
}
108104
string additionalDataAsJson = "{\"contosoBufferKind\":\"contosoDisplayBuffer\"}";
109-
if (forFrame)
110-
{
111-
m_webviewFrame.PostSharedBufferToScript(sharedBuffer, CoreWebView2SharedBufferAccess.ReadOnly, additionalDataAsJson);
112-
}
113-
else
105+
m_webview.PostSharedBufferToScript(sharedBuffer, CoreWebView2SharedBufferAccess.ReadOnly, additionalDataAsJson);
106+
}
107+
```
108+
## WinRT
109+
```c#
110+
using (CoreWebView2SharedBuffer sharedBuffer = WebViewEnvironment.CreateSharedBuffer(dataSize))
111+
{
112+
// Fill buffer with data.
113+
unsafe
114114
{
115-
m_webview.PostSharedBufferToScript(sharedBuffer, CoreWebView2SharedBufferAccess.ReadOnly, additionalDataAsJson);
116-
}
115+
using (IMemoryBufferReference reference = sharedBuffer.Buffer)
116+
{
117+
byte* buffer;
118+
uint capacity;
119+
((IMemoryBufferByteAccess)reference).GetBuffer(out buffer, out capacity);
120+
byte* buffer = (byte*)(sharedBuffer.Buffer.ToPointer());
121+
ulong dataToCopy = dataSize;
122+
while (dataToCopy-- > 0) {
123+
*buffer++ = *data++;
124+
}
125+
}
126+
}
127+
string additionalDataAsJson = "{\"contosoBufferKind\":\"contosoDisplayBuffer\"}";
128+
m_webview.PostSharedBufferToScript(sharedBuffer, CoreWebView2SharedBufferAccess.ReadOnly, additionalDataAsJson);
117129
}
118130
```
119131

132+
The example below illustrates how to use a shared buffer to send data from application to script in an iframe multiple times.
133+
134+
The script code will look like this:
135+
```
136+
let displayBuffer;
137+
let displayBufferArray;
138+
window.onload = function () {
139+
window.chrome.webview.addEventListener("sharedbufferreceived", e => {
140+
if (e.additionalData && e.additionalData.contosoBufferKind == "contosoDisplayBuffer") {
141+
// Release potential previous buffer to ensure that the underlying resource can be released timely.
142+
if (displayBuffer)
143+
chrome.webview.releaseBuffer(displayBuffer);
144+
// Hold the shared buffer and the typed array view of it.
145+
displayBuffer = e.buffer;
146+
displayBufferArray = new Uint8Array(displayBuffer);
147+
}
148+
});
149+
window.chrome.webview.addEventListener("message", e => {
150+
if (e.data == "DisplayBufferUpdated") {
151+
// Consume the updated data
152+
DisplaySharedBufferData(displayBufferArray);
153+
// Notify the application that the data has been consumed
154+
window.chrome.webview.postMessage("DisplayBufferConsumed");
155+
} else if (e.data = "ReleaseDisplayBuffer") {
156+
// Release the buffer, don't need it anymore.
157+
chrome.webview.releaseBuffer(displayBuffer);
158+
// Clear variables holding the buffer.
159+
displayBuffer = undefined;
160+
displayBufferArray = undefined;
161+
}
162+
});
163+
}
164+
165+
```
166+
## Win32 C++
167+
```cpp
168+
169+
void EnsureSharedBuffer(UINT64 bufferSize)
170+
{
171+
if (m_sharedBuffer && m_bufferSize >= bufferSize)
172+
return;
173+
// Close previous buffer if we have one.
174+
if (m_sharedBuffer)
175+
m_sharedBuffer->Close();
176+
CHECK_FAILURE(webviewEnvironment->CreateSharedBuffer(bufferSize, &m_sharedBuffer));
177+
CHECK_FAILURE(m_sharedBuffer->get_Size(&m_bufferSize);
178+
PCWSTR additionalDataAsJson = L"{\"contosoBufferKind\":\"contosoDisplayBuffer\"}";
179+
m_webviewFrame->PostSharedBufferToScript(
180+
m_sharedBuffer.get(), COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_ONLY, additionalDataAsJson);
181+
}
182+
183+
void ReleaseDisplayBuffer()
184+
{
185+
if (m_sharedBuffer)
186+
{
187+
// Explicitly Close the shared buffer so that we don't have to wait for
188+
// GC for the underlying shared memory to be released.
189+
m_sharedBuffer->Close();
190+
m_sharedBuffer = nullptr;
191+
CHECK_FAILURE(m_webviewFrame->PostWebMessage(L"ReleaseDisplayBuffer"));
192+
}
193+
}
194+
195+
void UpdateDisplayBuffer()
196+
{
197+
EnsureSharedBuffer(m_dataSize);
198+
// Fill data into the shared memory via IStream.
199+
wil::com_ptr<IStream> stream;
200+
CHECK_FAILURE(sharedBuffer->OpenStream(&stream));
201+
// The sample code assumes that the data itself contains info about the size
202+
// of data to consume and we don't have to add it into the shared buffer or
203+
// as part of the web message.
204+
CHECK_FAILURE(stream->Write(m_data, m_dataSize, nullptr));
205+
CHECK_FAILURE(m_webviewFrame->PostWebMessage(L"DisplayBufferUpdated"));
206+
}
207+
208+
void OnFrameWebMessageReceived(PCWSTR message)
209+
{
210+
std::wstring DisplayBufferConsumedMessage(L"DisplayBufferConsumed");
211+
if (DisplayBufferConsumedMessage == message)
212+
{
213+
if (m_hasMoreDataToSend)
214+
{
215+
UpdateDisplayBuffer();
216+
}
217+
else
218+
{
219+
ReleaseDisplayBuffer();
220+
}
221+
}
222+
}
223+
224+
```
225+
## WinRT and .NET
226+
```c#
227+
void EnsureSharedBuffer(ulong bufferSize)
228+
{
229+
if (m_sharedBuffer && m_sharedBuffer.Size >= bufferSize)
230+
return;
231+
// Dispose previous buffer if we have one.
232+
if (m_sharedBuffer)
233+
m_sharedBuffer.Dispose();
234+
m_sharedBuffer = WebviewEnvironment.CreateSharedBuffer(bufferSize);
235+
string additionalDataAsJson = "{\"contosoBufferKind\":\"contosoDisplayBuffer\"}";
236+
m_webviewFrame.PostSharedBufferToScript(
237+
m_sharedBuffer, CoreWebView2SharedBufferAccess.ReadOnly, additionalDataAsJson);
238+
}
239+
240+
void ReleaseDisplayBuffer()
241+
{
242+
if (m_sharedBuffer)
243+
{
244+
// Explicitly dispose the shared buffer so that we don't have to wait for
245+
// GC for the underlying shared memory to be released.
246+
m_sharedBuffer.Dispose();
247+
m_sharedBuffer = null;
248+
CHECK_FAILURE(m_webviewFrame.PostWebMessage("ReleaseDisplayBuffer"));
249+
}
250+
}
251+
252+
void UpdateDisplayBuffer()
253+
{
254+
EnsureSharedBuffer(m_dataSize);
255+
// Fill data using access Stream
256+
using (Stream stream = m_sharedBuffer.OpenStream())
257+
{
258+
using (StreamWriter writer = new StreamWriter(stream))
259+
{
260+
writer.Write(m_data);
261+
}
262+
}
263+
}
264+
265+
void OnFrameWebMessageReceived(string message)
266+
{
267+
if (message == "DisplayBufferConsumed")
268+
{
269+
if (m_hasMoreDataToSend)
270+
{
271+
UpdateDisplayBuffer();
272+
}
273+
else
274+
{
275+
ReleaseDisplayBuffer();
276+
}
277+
}
278+
}
279+
```
280+
120281
# API Details
121282
## Win32 C++
122283
```
@@ -127,6 +288,8 @@ interface ICoreWebView2Environment11 : IUnknown {
127288
/// Once shared, the same content of the buffer will be accessible from both
128289
/// the app process and script in WebView. Modification to the content will be visible
129290
/// to all parties that have access to the buffer.
291+
/// The shared buffer is presented to the script as ArrayBuffer. All JavaScript APIs
292+
/// that works for ArrayBuffer including Atomics APIs can be used on it.
130293
HRESULT CreateSharedBuffer(
131294
[in] UINT64 size,
132295
[out, retval] ICoreWebView2SharedBuffer** shared_buffer);
@@ -145,7 +308,7 @@ interface ICoreWebView2SharedBuffer : IUnknown {
145308
/// Returns a handle to the file mapping object that backs this shared buffer.
146309
/// The returned handle is owned by the shared buffer object. You should not
147310
/// call CloseHandle on it.
148-
/// Normal app should use `Buffer` or `GetStream` to get memory address
311+
/// Normal app should use `Buffer` or `OpenStream` to get memory address
149312
/// or IStream object to access the buffer.
150313
/// For advanced scenarios, you could use file-mapping APIs to obtain other views
151314
/// or duplicate this handle to another application process and create a view from
@@ -156,10 +319,11 @@ interface ICoreWebView2SharedBuffer : IUnknown {
156319
/// access to the buffer is needed any more, to ensure that the underlying resources
157320
/// are released timely even if the shared buffer object itself is not released due to
158321
/// some leaked reference.
159-
/// After the shared buffer is closed, accessing properties of the object will fail with
160-
/// `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`. Operations like Read or Write on the IStream
161-
/// objects returned from `GetStream` will fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
162-
/// `PostSharedBufferToScript` will also fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
322+
/// After the shared buffer is closed, the buffer address and file mapping handle previously
323+
/// obtained becomes invalid and cannot be used anymore. Accessing properties of the object
324+
/// will fail with `RO_E_CLOSED`. Operations like Read or Write on the IStream objects returned
325+
/// from `OpenStream` will fail with `RO_E_CLOSED`. `PostSharedBufferToScript` will also
326+
/// fail with `RO_E_CLOSED`.
163327
///
164328
/// The script code should call `chrome.webview.releaseBuffer` with
165329
/// the shared buffer as the parameter to release underlying resources as soon
@@ -185,8 +349,8 @@ typedef enum COREWEBVIEW2_SHARED_BUFFER_ACCESS {
185349
interface ICoreWebView2_14 : IUnknown {
186350
/// Share a shared buffer object with script of the main frame in the WebView.
187351
/// The script will receive a `SharedBufferReceived` event from chrome.webview.
188-
/// The event arg for that event will have the following methods and properties:
189-
/// `getBuffer()`: returns an ArrayBuffer object with the backing content from the shared buffer.
352+
/// The event arg for that event will have the following properties:
353+
/// `buffer()`: an ArrayBuffer object with the backing content from the shared buffer.
190354
/// `additionalData`: an object as the result of parsing `additionalDataAsJson` as JSON string.
191355
/// This property will be `undefined` if `additionalDataAsJson` is nullptr or empty string.
192356
/// `source`: with a value set as `chrome.webview` object.
@@ -195,11 +359,15 @@ interface ICoreWebView2_14 : IUnknown {
195359
/// If `access` is COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_ONLY, the script will only have read access to the buffer.
196360
/// If the script tries to modify the content in a read only buffer, it will cause an access
197361
/// violation in WebView renderer process and crash the renderer process.
198-
/// If the shared buffer is already closed, the API will fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
362+
/// If the shared buffer is already closed, the API will fail with `RO_E_CLOSED`.
199363
///
200364
/// The script code should call `chrome.webview.releaseBuffer` with
201365
/// the shared buffer as the parameter to release underlying resources as soon
202366
/// as it does not need access to the shared buffer any more.
367+
/// The application can post the same shared buffer object to multiple web pages or iframes, or
368+
/// post to the same web page or iframe multiple times. Each `PostSharedBufferToScript` will
369+
/// create a separate ArrayBuffer object with its own view of the memory and is separately
370+
/// released. The underlying shared memory will be released when all the views are released.
203371
///
204372
/// Sharing a buffer to script has security risk. You should only share buffer with trusted site.
205373
/// If a buffer is shared to a untrusted site, possible sensitive information could be leaked.
@@ -215,20 +383,24 @@ interface ICoreWebView2Frame4 : IUnknown {
215383
/// Share a shared buffer object with script of the iframe in the WebView.
216384
/// The script will receive a `SharedBufferReceived` event from chrome.webview.
217385
/// The event arg for that event will have the following properties:
218-
/// `sharedBuffer`: an ArrayBuffer object with the backing content from the shared buffer.
219-
/// `data`: an object as the result of parsing `additionalDataAsJson` as JSON string.
386+
/// `buffer()`: an ArrayBuffer object with the backing content from the shared buffer.
387+
/// `additionalData`: an object as the result of parsing `additionalDataAsJson` as JSON string.
220388
/// This property will be `undefined` if `additionalDataAsJson` is nullptr or empty string.
221389
/// `source`: with a value set as `chrome.webview` object.
222390
/// If a string is provided as `additionalDataAsJson` but it is not a valid JSON string,
223391
/// the API will fail with `E_INVALIDARG`.
224392
/// If `access` is COREWEBVIEW2_SHARED_BUFFER_ACCESS_READ_ONLY, the script will only have read access to the buffer.
225393
/// If the script tries to modify the content in a read only buffer, it will cause an access
226394
/// violation in WebView renderer process and crash the renderer process.
227-
/// If the shared buffer is already closed, the API will fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
395+
/// If the shared buffer is already closed, the API will fail with `RO_E_CLOSED`.
228396
///
229397
/// The script code should call `chrome.webview.releaseBuffer` with
230398
/// the shared buffer as the parameter to release underlying resources as soon
231399
/// as it does not need access to the shared buffer any more.
400+
/// The application can post the same shared buffer object to multiple web pages or iframes, or
401+
/// post to the same web page or iframe multiple times. Each `PostSharedBufferToScript` will
402+
/// create a separate ArrayBuffer object with its own view of the memory and is separately
403+
/// released. The underlying shared memory will be released when all the views are released.
232404
///
233405
/// Sharing a buffer to script has security risk. You should only share buffer with trusted site.
234406
/// If a buffer is shared to a untrusted site, possible sensitive information could be leaked.
@@ -259,7 +431,7 @@ namespace Microsoft.Web.WebView2.Core
259431
/// The raw memory address of the buffer.
260432
/// You can cast it to pointer to real data types like byte* to access the memory
261433
/// from `unsafe` code region.
262-
/// Normal app should use `GetStream` to get a Stream object to access the buffer.
434+
/// Normal app should use `OpenStream` to get a Stream object to access the buffer.
263435
public IntPtr Buffer { get; };
264436

265437
/// The native file mapping handle of the shared memory of the buffer.
@@ -274,6 +446,12 @@ namespace Microsoft.Web.WebView2.Core
274446
void Close();
275447
}
276448

449+
enum CoreWebView2SharedBufferAccess
450+
{
451+
ReadOnly = 0,
452+
ReadWrite = 1
453+
}
454+
277455
runtimeclass CoreWebView2
278456
{
279457
public void PostSharedBufferToScript(
@@ -319,6 +497,12 @@ namespace Microsoft.Web.WebView2.Core
319497
// Note that we are not exposing Handle from WinRT API.
320498
}
321499

500+
enum CoreWebView2SharedBufferAccess
501+
{
502+
ReadOnly = 0,
503+
ReadWrite = 1
504+
}
505+
322506
runtimeclass CoreWebView2
323507
{
324508
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2_14")]

0 commit comments

Comments
 (0)