Skip to content

Commit 9405f24

Browse files
authored
Create SharedBuffer.md
1 parent a51796a commit 9405f24

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

specs/SharedBuffer.md

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
Shared Buffer Between Native Application Code and Script
2+
===
3+
4+
# Background
5+
For some advanced scenarios, there is a need to exchange large data between WebView application process and trusted web pages that are considered as part of the app. Some examples:
6+
- Web page generates large data, passes to native side to be further process or fed to other parts of the app or OS. Example: https://github.com/MicrosoftEdge/WebView2Feedback/issues/89.
7+
- Native side generates large data for the web side to consume. The data might or might not come directly from files. Example: https://github.com/MicrosoftEdge/WebView2Feedback/issues/1005.
8+
To support these scenarios, we are adding an Edge WebView2 API to support shared buffer between app and renderer process, based on shared memory from the OS.
9+
10+
The application code can use the APIs to create a shared buffer object, and share to scripts as [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer) object.
11+
Then both the native application code and the script will be able to access the same memory.
12+
13+
# Conceptual pages (How To)
14+
15+
Besides using the memory address directly, the shared buffer object can be accessed from native side via an IStream* object that you can get from the shared object.
16+
17+
When the application code calls `ShareBufferToScript`, the script side will receive a `SharedBufferReceived` event containing the buffer as an `ArrayBuffer` object.
18+
After receiving the shared buffer object, it can access it the same way as any other ArrayBuffer object, including transering to a web worker to process the data on
19+
the worker thread.
20+
21+
As shared buffer normally represent a large memory, instead of waiting for garbage collection to release the memory along with the owning objects, the application
22+
should try to release the buffer as soon as it doesn't need access to it. This can be done by calling Close() method on the shared buffer object, or
23+
`chrome.webview.releaseSharedBuffer` from script.
24+
25+
As the memory could contain sensitive information and corrupted memory could crash the application, the application should only share buffer with trusted sites.
26+
27+
The application can use other messaging channel like `PostWebMessageAsJson` and `chrome.webview.postMessage` to inform the other side the desire to have a shared
28+
buffer and the status of the buffer (data is produced or consumed).
29+
30+
# Examples
31+
32+
The example below illustrates how to send data from application to script for one time read only consumption.
33+
34+
The script code will look like this:
35+
```
36+
window.onload = function () {
37+
window.chrome.webview.addEventListener("SharedBufferReceived", e => {
38+
SharedBufferReceived(e);
39+
});
40+
}
41+
42+
function SharedBufferReceived(e) {
43+
if (e.data && e.data.read_only) {
44+
// This is the one time read only buffer
45+
let one_time_shared_buffer = e.sharedBuffer;
46+
// Consume the data from the buffer
47+
DisplaySharedBufferData(one_time_shared_buffer);
48+
// Release the buffer after consuming the data.
49+
chrome.webview.releaseSharedBuffer(one_time_shared_buffer);
50+
}
51+
}
52+
```
53+
## Win32 C++
54+
```cpp
55+
56+
wil::com_ptr<ICoreWebView2StagingEnvironment2> environment;
57+
CHECK_FAILURE(
58+
m_appWindow->GetWebViewEnvironment()->QueryInterface(IID_PPV_ARGS(&environment)));
59+
60+
wil::com_ptr<ICoreWebView2StagingSharedBuffer> sharedBuffer;
61+
CHECK_FAILURE(environment->CreateSharedBuffer(bufferSize, &sharedBuffer));
62+
// Fill data into the shared memory via IStream.
63+
wil::com_ptr<IStream> stream;
64+
CHECK_FAILURE(sharedBuffer->GetAccessStream(&stream));
65+
CHECK_FAILURE(stream->Write(data, dataSize, nullptr));
66+
PCWSTR additionalDataAsJson = L"{\"read_only\":true}";
67+
if (forFrame)
68+
{
69+
m_webviewFrame->ShareBufferToScript(
70+
sharedBuffer.get(), /*isReadOnlyToScript*/TRUE, additionalDataAsJson);
71+
}
72+
else
73+
{
74+
m_webView->ShareBufferToScript(
75+
sharedBuffer.get(), /*isReadOnlyToScript*/TRUE, additionalDataAsJson);
76+
}
77+
// Explicitly close the one time shared buffer to ensure that the resource is released timely.
78+
sharedBuffer->Close();
79+
80+
```
81+
## WinRT and .NET
82+
```c#
83+
var sharedBuffer = WebViewEnvironment.CreateSharedBuffer(bufferSize);
84+
// Fill data using access Stream
85+
using (Stream stream = sharedBuffer.GetAccessStream())
86+
{
87+
using (StreamWriter writer = new StreamWriter(stream))
88+
{
89+
writer.Write(bufferData);
90+
}
91+
}
92+
string additionalDataAsJson = "{\"read_only\":true}";
93+
if (forFrame)
94+
{
95+
m_webviewFrame.ShareBufferToScript(sharedBuffer, /*isReadOnlyToScript*/true, additionalDataAsJson);
96+
}
97+
else
98+
{
99+
m_webview.ShareBufferToScript(sharedBuffer, /*isReadOnlyToScript*/true, additionalDataAsJson);
100+
}
101+
// Explicitly close the one time shared buffer to ensure that the resource is released timely.
102+
sharedBuffer.Close();
103+
```
104+
105+
# API Details
106+
## Win32 C++
107+
```
108+
interface ICoreWebView2StagingEnvironment2 : IUnknown {
109+
/// Create a shared memory based buffer with the specifid size in bytes.
110+
/// The buffer can be shared with web contents in WebView by calling
111+
/// `ShareBufferToScript` on `CoreWebView2` or `CoreWebViewFrame` object.
112+
/// Once shared, the same content of the buffer will be accessible from both
113+
/// the app process and script in WebView. Modification to the content will be visible
114+
/// to all parties that have access to the buffer.
115+
/// For 32bit application, the creation will fail with E_INVALIDARG if `size` is larger than 4GB.
116+
HRESULT CreateSharedBuffer(
117+
[in] UINT64 size,
118+
[out, retval] ICoreWebView2StagingSharedBuffer** shared_buffer);
119+
}
120+
121+
interface ICoreWebView2StagingSharedBuffer : IUnknown {
122+
/// The size of the shared buffer in bytes.
123+
[propget] HRESULT Size([out, retval] UINT64* value);
124+
125+
/// The memory address of the shared buffer.
126+
[propget] HRESULT Buffer([out, retval] BYTE** value);
127+
128+
/// Get an IStream object that can be used to access the shared buffer.
129+
HRESULT GetAccessStream([out, retval] IStream** value);
130+
131+
/// The file mapping handle of the shared memory of the buffer.
132+
/// Normal app should use `Buffer` or `GetAccessStream` to get memory address
133+
/// or IStream object to access the buffer.
134+
/// For advanced scenario, you could duplicate this handle to another application
135+
/// process and create a mapping from the duplicated handle in that process to access
136+
/// the buffer from that separate process.
137+
[propget] HRESULT Handle([out, retval] HANDLE* value);
138+
139+
/// Release the backing shared memory. The application should call this API when no
140+
/// access to the buffer is needed any more, to ensure that the underlying resources
141+
/// are released timely even if the shared buffer object itself is not released due to some leaked
142+
/// reference.
143+
/// After the shared buffer is closed, accessing properties of the object will fail with
144+
/// `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`. Operations like Read or Write on the IStream objects
145+
/// returned from `GetAccessStream` will fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
146+
/// `ShareBufferToScript` will also fail with `HRESULT_FROM_WIN32(ERROR_INVALID_STATE)`.
147+
///
148+
/// The script code should call `chrome.webview.releaseSharedBuffer` with
149+
/// the shared buffer as the parameter to release underlying resources as soon
150+
/// as it does not need access the shared buffer any more.
151+
/// When script tries to access the buffer after calling `chrome.webview.releaseSharedBuffer`,
152+
/// JavaScript `TypeError` exception will be raised complaining about accessing a detached ArrayBuffer,
153+
/// the same exception when trying to access a transferred ArrayBuffer.
154+
HRESULT Close();
155+
}
156+
157+
interface ICoreWebView2Staging7 : IUnknown {
158+
/// Share a shared buffer object with script of the main frame in the WebView.
159+
/// The script will receive a `SharedBufferReceived` event from chrome.webview.
160+
/// The event arg for that event will have the following properties:
161+
/// `sharedBuffer`: an ArrayBuffer object with the backing content from the shared buffer.
162+
/// `data`: an object representing `additionalDataAsJson`. This property will be `undefined`
163+
/// if `additionalDataAsJson` is nullptr or empty string.
164+
/// `source`: with a value set as `chrome.webview` object.
165+
/// If `isReadOnlyToScript` is true, the script will only have read access to the buffer.
166+
/// If the script tries to modify the content in a read only buffer, it will cause an access violation
167+
/// in WebView renderer process and crash the renderer process.
168+
/// If the shared buffer is already closed, the API will fail with HRESULT_FROM_WIN32(ERROR_INVALID_STATE).
169+
///
170+
/// The script code should call `chrome.webview.releaseSharedBuffer` with
171+
/// the shared buffer as the parameter to release underlying resources as soon
172+
/// as it does not need access the shared buffer any more.
173+
///
174+
/// Sharing a buffer to script has security risk. You should only share buffer with trusted site.
175+
///
176+
HRESULT ShareBufferToScript(
177+
[in] ICoreWebView2StagingSharedBuffer* sharedBuffer,
178+
[in] BOOL isReadOnlyToScript,
179+
[in] LPCWSTR additionalDataAsJson);
180+
}
181+
182+
interface ICoreWebView2StagingFrame2 : IUnknown {
183+
/// Share a shared buffer object with script of the iframe in the WebView.
184+
/// The script will receive a `SharedBufferReceived` event from chrome.webview.
185+
/// The event arg for that event will have the following properties:
186+
/// `sharedBuffer`: an ArrayBuffer object with the backing content from the shared buffer.
187+
/// `data`: an object representing `additionalDataAsJson`. This property will be `undefined`
188+
/// if `additionalDataAsJson` is nullptr or empty string.
189+
/// `source`: with a value set as `chrome.webview` object.
190+
/// If `isReadOnlyToScript` is true, the script will only have read access to the buffer.
191+
/// If the script tries to modify the content in a read only buffer, it will cause an access violation
192+
/// in WebView renderer process and crash the renderer process.
193+
/// If the shared buffer is already closed, the API will fail with HRESULT_FROM_WIN32(ERROR_INVALID_STATE).
194+
///
195+
/// The script code should call `chrome.webview.releaseSharedBuffer` with
196+
/// the shared buffer as the parameter to release underlying resources as soon
197+
/// as it does not need access the shared buffer any more.
198+
///
199+
/// Sharing a buffer to script has security risk. You should only share buffer with trusted site.
200+
///
201+
HRESULT ShareBufferToScript(
202+
[in] ICoreWebView2StagingSharedBuffer* sharedBuffer,
203+
[in] BOOL isReadOnlyToScript,
204+
[in] LPCWSTR additionalDataAsJson);
205+
}
206+
207+
```
208+
209+
## .NET
210+
```c#
211+
namespace Microsoft.Web.WebView2.Core
212+
{
213+
214+
class CoreWebView2Environment
215+
{
216+
public CoreWebView2SharedBuffer CreateSharedBuffer(ulong Size);
217+
}
218+
219+
class CoreWebView2SharedBuffer
220+
{
221+
public ulong Size { get; };
222+
public IntPtr Buffer { get; };
223+
public IntPtr Handle { get; };
224+
public Stream GetAccessStream();
225+
void Close();
226+
}
227+
228+
runtimeclass CoreWebView2
229+
{
230+
public void ShareBufferToScript(CoreWebView2SharedBuffer sharedBuffer, bool isReadOnlyToScript, string additionalDataAsJson);
231+
}
232+
233+
class CoreWebView2Frame
234+
{
235+
public void ShareBufferToScript(CoreWebView2SharedBuffer sharedBuffer, bool isReadOnlyToScript, string additionalDataAsJson);
236+
}
237+
}
238+
239+
```
240+
## WinRT
241+
```c#
242+
namespace Microsoft.Web.WebView2.Core
243+
{
244+
245+
runtimeclass CoreWebView2Environment
246+
{
247+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2StagingEnvironment2")]
248+
{
249+
CoreWebView2SharedBuffer CreateSharedBuffer(UInt64 Size);
250+
}
251+
}
252+
253+
runtimeclass CoreWebView2SharedBuffer
254+
{
255+
UInt64 Size { get; };
256+
Windows.Storage.Streams.IRandomAccessStream GetAccessStream();
257+
void Close();
258+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2SharedBuffer_Manual")]
259+
{
260+
Windows.Foundation.IMemoryBufferReference Buffer { get; };
261+
}
262+
}
263+
264+
runtimeclass CoreWebView2
265+
{
266+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2Staging7")]
267+
{
268+
void ShareBufferToScript(CoreWebView2SharedBuffer sharedBuffer, Boolean isReadOnlyToScript, String additionalDataAsJson);
269+
}
270+
}
271+
272+
runtimeclass CoreWebView2Frame
273+
{
274+
[interface_name("Microsoft.Web.WebView2.Core.ICoreWebView2StagingFrame2")]
275+
{
276+
void ShareBufferToScript(CoreWebView2SharedBuffer sharedBuffer, Boolean isReadOnlyToScript, String additionalDataAsJson);
277+
}
278+
}
279+
}
280+
281+
```

0 commit comments

Comments
 (0)