Skip to content

Commit 581e122

Browse files
authored
Update CDPSessionIdSupport.md
1 parent fd6407d commit 581e122

File tree

1 file changed

+57
-45
lines changed

1 file changed

+57
-45
lines changed

specs/CDPSessionIdSupport.md

Lines changed: 57 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@ sessionId support for DevToolsProtocol method call and event
44
# Background
55
A web page can have multiple DevToolsProtocol targets. Besides the default target for the top page, there are separate targets for iframes from different origin and web workers.
66

7-
The underlying DevToolsProtocol supports interaction with other targets by attaching to them and then using sessionId of the attachment in DevToolsProtocol method call message.
8-
The received DevToolsProtocol event messages also have sessionId field to indicate which target the event comes from.
7+
The underlying DevToolsProtocol supports interaction with other targets by calling the `Target.setAutoAttach` method or the `Target.attachToTarget` method with an explicit targetId which returns a sessionId. The sessionId is also part of `Target.attachedToTarget` event message. Regardless of how the sessionId is obtained, it can be used
8+
in subsequent DevToolsProtocol methods to indicate which session (and therefore which target) the method applies to.
99
See [DevToolsProtocol Target domain](https://chromedevtools.github.io/devtools-protocol/tot/Target/) for more details.
1010

11+
The DevToolsProtocol event messages also have sessionId field to indicate which target the event comes from.
12+
1113
However, the current WebView2 DevToolsProtocol APIs like [CallDevToolsProtocolMethod](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2#calldevtoolsprotocolmethod)
1214
and [DevToolsProtocolEventReceived](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/win32/icorewebview2devtoolsprotocoleventreceivedeventargs)
1315
doesn't support sessionId.
1416

15-
To support interaction with different parts of the page, we are adding support for specifying sessionId for DevToolsProtocol method call API
16-
and retrieving sessionId for received DevToolsProtocol events.
17-
17+
To support interaction with different parts of the page, we allow apps to specify a sessionId when calling DevToolsProtocol methods, as well as passing a sessionId to DevToolsProtocol event handlers, with empty string sessionId representing the default target of the top page.
18+
1819
# Conceptual pages (How To)
1920

20-
To use the sessionId support, you must attach to targets with with `flatten` set as `true` when calling `Target.attachToTarget` or `Target.setAutoAttach`.
21+
To use the sessionId support, you must attach to targets with with `flatten` set as `true` when calling `Target.attachToTarget` or `Target.setAutoAttach`. Setting `flatten` as `false` is not supported. If attaching to a target with `flatten` set as `false`, the complete handler will never be invoked when calling DevToolsProtocol methods, and no events will be received from that session.
2122

22-
You can listen to `Target.attachedToTarget` and `Target.detachedFromTarget` events to manage the sessionId for targets, and listen to `Target.targetInfoChanged` event to update target info like url of a target.
23+
You can listen to `Target.attachedToTarget` and `Target.detachedFromTarget` events to manage the sessionId for targets, and listen to `Target.targetInfoChanged` event to update target info like url of a target. To filter to specific frames or workers, you could use type, url and title of the target info.
2324

2425
There is also some nuance for DevToolsProtocol's target management. If you are interested in only top page and iframes from different origins on the page, it will be simple and straight forward. All related methods and events like `Target.getTargets`, `Target.attachToTarget`, and `Target.targetCreated` event work as expected.
2526

@@ -34,6 +35,8 @@ To summarize, there are two ways of finding the targets and neither covers all 3
3435
| Target.getTargets, Target.setDiscoverTargets, Target.targetCreated, Target.attachToTarget || ||
3536
| Target.setAutoAttach, Target.attachedToTarget ||| |
3637

38+
Also note that all of these methods apply only to direct children. To interact with grandchildren (like a different orign iframe inside a different origin iframe), you have to repeat recursively through the child targets.
39+
3740
# Examples
3841

3942
The example below illustrates how to collect messages logged by console.log calls by JavaScipt code from various parts of the web page, including dedicated web worker.
@@ -46,8 +49,8 @@ void MyApp::AttachConsoleLogHandlers()
4649
// The sessions and targets descriptions are tracked by 2 maps:
4750
// SessionId to TargetId map:
4851
// std::map<std::wstring, std::wstring> m_devToolsSessionMap;
49-
// TargetId to description map, where description is "<target type>,<target url>".
50-
// std::map<std::wstring, std::wstring> m_devToolsTargetInfoMap;
52+
// TargetId to description label map, where the lable is "<target type>,<target url>".
53+
// std::map<std::wstring, std::wstring> m_devToolsTargetLabelMap;
5154
// GetJSONStringField is a helper function that can retrieve a string field from a json message.
5255

5356
wil::com_ptr<ICoreWebView2DevToolsProtocolEventReceiver> receiver;
@@ -58,26 +61,28 @@ void MyApp::AttachConsoleLogHandlers()
5861
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
5962
[this](
6063
ICoreWebView2* sender,
61-
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT
64+
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) noexcept -> HRESULT
6265
{
6366
// Get console.log message details and which target it comes from
6467
wil::unique_cotaskmem_string parameterObjectAsJson;
6568
CHECK_FAILURE(args->get_ParameterObjectAsJson(&parameterObjectAsJson));
66-
std::wstring eventSource;
69+
std::wstring eventSourceLabel;
6770
std::wstring eventDetails = parameterObjectAsJson.get();
6871
wil::com_ptr<ICoreWebView2DevToolsProtocolEventReceivedEventArgs2> args2;
6972
if (SUCCEEDED(args->QueryInterface(IID_PPV_ARGS(&args2))))
7073
{
7174
wil::unique_cotaskmem_string sessionId;
7275
CHECK_FAILURE(args2->get_SessionId(&sessionId));
73-
if (sessionId.get() && *sessionId.get())
76+
if (*sessionId.get())
7477
{
7578
std::wstring targetId = m_devToolsSessionMap[sessionId.get()];
76-
eventSource = m_devToolsTargetDescriptionMap[targetId];
79+
eventSourceLabel = m_devToolsTargetLabelMap[targetId];
7780
}
81+
// else, leave eventSourceLabel as empty string for the default target of top page.
7882
}
79-
// App code to log these events.
80-
LogConsoleLogMessage(eventSource, eventDetails);
83+
// App code to log these events. Empty string eventSourceLabel means the default target of top page.
84+
// Note that eventSourceLabel is just a label string, no parsing is attempted in LogConsoleLogMessage.
85+
LogConsoleLogMessage(eventSourceLabel, eventDetails);
8186
return S_OK;
8287
})
8388
.Get(),
@@ -91,21 +96,22 @@ void MyApp::AttachConsoleLogHandlers()
9196
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
9297
[this](
9398
ICoreWebView2* sender,
94-
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT
99+
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) noexcept -> HRESULT
95100
{
96101
// A new target is attached, add its info to maps.
97102
wil::unique_cotaskmem_string jsonMessage;
98103
CHECK_FAILURE(args->get_ParameterObjectAsJson(&jsonMessage));
104+
// Note that the sessionId and targetId are there even if the WebView2 Runtime version is old.
99105
std::wstring sessionId = GetJSONStringField(jsonMessage.get(), L"sessionId");
100106
std::wstring targetId = GetJSONStringField(jsonMessage.get(), L"targetId");
101107
m_devToolsSessionMap[sessionId] = targetId;
102108
std::wstring type = GetJSONStringField(jsonMessage.get(), L"type");
103109
std::wstring url = GetJSONStringField(jsonMessage.get(), L"url");
104-
m_devToolsTargetDescriptionMap[targetId] = type + L"," + url;
110+
m_devToolsTargetLabelMap.insert_or_assign(targetId, type + L"," + url);
105111
wil::com_ptr<ICoreWebView2_10> webview2 = m_webView.try_query<ICoreWebView2_10>();
106112
if (webview2)
107113
{
108-
// Auto attach to targets further created from this target.
114+
// Auto-attach to targets further created from this target (identified by its session ID).
109115
webview2->CallDevToolsProtocolMethodForSession(
110116
sessionId.c_str(), L"Target.setAutoAttach",
111117
LR"({"autoAttach":true,"waitForDebuggerOnStart":false,"flatten":true})",
@@ -125,17 +131,16 @@ void MyApp::AttachConsoleLogHandlers()
125131
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
126132
[this](
127133
ICoreWebView2* sender,
128-
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT
134+
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) noexcept -> HRESULT
129135
{
130136
// A target is detached, remove it from the maps.
131137
wil::unique_cotaskmem_string jsonMessage;
132138
CHECK_FAILURE(args->get_ParameterObjectAsJson(&jsonMessage));
133139
std::wstring sessionId = GetJSONStringField(jsonMessage.get(), L"sessionId");
134140
auto session = m_devToolsSessionMap.find(sessionId);
135-
if (session != m_devToolsSessionMap.end())
141+
if (m_devToolsSessionMap.Remove(sessionId, out string targetId))
136142
{
137-
m_devToolsTargetDescriptionMap.erase(session->second);
138-
m_devToolsSessionMap.erase(session);
143+
m_devToolsTargetLabelMap.erase(session->second);
139144
}
140145
return S_OK;
141146
})
@@ -148,19 +153,20 @@ void MyApp::AttachConsoleLogHandlers()
148153
Callback<ICoreWebView2DevToolsProtocolEventReceivedEventHandler>(
149154
[this](
150155
ICoreWebView2* sender,
151-
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) -> HRESULT
156+
ICoreWebView2DevToolsProtocolEventReceivedEventArgs* args) noexcept -> HRESULT
152157
{
153158
// A target's info like url changed, update it in the target description map.
154159
wil::unique_cotaskmem_string jsonMessage;
155160
CHECK_FAILURE(args->get_ParameterObjectAsJson(&jsonMessage));
161+
// Note that the targetId are there even if the WebView2 Runtime version is old.
156162
std::wstring targetId = GetJSONStringField(jsonMessage.get(), L"targetId");
157-
if (m_devToolsTargetDescriptionMap.find(targetId) !=
158-
m_devToolsTargetDescriptionMap.end())
163+
if (m_devToolsTargetLabelMap.find(targetId) !=
164+
m_devToolsTargetLabelMap.end())
159165
{
160166
// This is a target that we are interested in, update description.
161167
std::wstring type = GetJSONStringField(jsonMessage.get(), L"type");
162168
std::wstring url = GetJSONStringField(jsonMessage.get(), L"url");
163-
m_devToolsTargetDescriptionMap[targetId] = type + L"," + url;
169+
m_devToolsTargetLabelMap[targetId] = type + L"," + url;
164170
}
165171
return S_OK;
166172
})
@@ -174,25 +180,31 @@ void MyApp::AttachConsoleLogHandlers()
174180
LR"({"autoAttach":true,"waitForDebuggerOnStart":false,"flatten":true})", nullptr);
175181
}
176182
```
177-
## WinRT and .NET
183+
## WinRT and .NET
184+
Note that the sample code uses new APIs without try-catch, so it only works with latest version of Edge WebView2 Runtime.
178185
```c#
179186
// The sessions and targets descriptions are tracked by 2 dictionaries:
180187
// SessionId to TargetId dictionary: m_devToolsSessionMap;
181-
// TargetId to description dictionary, where description is "<target type>,<target url>": m_devToolsTargetInfoMap
188+
// TargetId to description label dictionary, where the label is "<target type>,<target url>": m_devToolsTargetLabelMap
182189
// GetJSONStringField is a helper function that can retrieve a string field from a json message.
183190
184191
private void CoreWebView2_ConsoleAPICalled(CoreWebView2 sender, CoreWebView2DevToolsProtocolEventReceivedEventArgs args)
185192
{
186193
// Figure out which target the console.log comes from
187-
string eventSource;
194+
string eventSourceLabel;
188195
string sessionId = args.SessionId;
189196
if (sessionId.Length > 0)
190197
{
191-
string targetId = m_devToolsSessionMap[sessionId];
192-
eventSource = m_devToolsTargetDescriptionMap[targetId];
198+
string targetId = m_devToolsSessionMap[sessionId];
199+
eventSourceLabel = m_devToolsTargetLabelMap[targetId];
200+
}
201+
else
202+
{ // empty string means the default target of top page.
203+
eventSourceLabel = "";
193204
}
194-
// App code to log these events.
195-
LogConsoleLogMessage(eventSource, args.ParameterObjectAsJson);
205+
// App code to log these events. Empty string eventSourceLabel means the default target of top page.
206+
// Note that eventSourceLabel is just a label string, no parsing is attempted in LogConsoleLogMessage.
207+
LogConsoleLogMessage(eventSourceLabel, args.ParameterObjectAsJson);
196208
}
197209

198210
private void CoreWebView2_AttachedToTarget(CoreWebView2 sender, CoreWebView2DevToolsProtocolEventReceivedEventArgs args)
@@ -204,13 +216,12 @@ private void CoreWebView2_AttachedToTarget(CoreWebView2 sender, CoreWebView2DevT
204216
m_devToolsSessionMap[sessionId] = targetId;
205217
string type = GetJSONStringField(jsonMessage, L"type");
206218
string url = GetJSONStringField(jsonMessage, L"url");
207-
m_devToolsTargetDescriptionMap[targetId] = $"{type",{url}";
208-
// Auto attach to targets further created from this target.
209-
_ = m_webview.CallDevToolsProtocolMethodAsync("Target.setAutoAttach",
210-
@"{""autoAttach"":true,""waitForDebuggerOnStart"":false,""flatten"":true}",
211-
sessionId);
219+
m_devToolsTargetLabelMap[targetId] = $"{type",{url}";
220+
// Auto-attach to targets further created from this target (identified by its session ID).
221+
_ = m_webview.CallDevToolsProtocolMethodForSessionAsync("Target.setAutoAttach", sessionId,
222+
@"{""autoAttach"":true,""waitForDebuggerOnStart"":false,""flatten"":true}");
212223
// Enable Runtime events to receive Runtime.consoleAPICalled from the target, which is triggered by console.log calls.
213-
m_webview.CallDevToolsProtocolMethodAsync("Runtime.enable", "{}", sessionId);
224+
m_webview.CallDevToolsProtocolMethodForSessionAsync(sessionId, "Runtime.enable", "{}");
214225
}
215226
216227
private void CoreWebView2_DetachedFromTarget(CoreWebView2 sender, CoreWebView2DevToolsProtocolEventReceivedEventArgs args)
@@ -220,7 +231,7 @@ private void CoreWebView2_DetachedFromTarget(CoreWebView2 sender, CoreWebView2De
220231
string sessionId = GetJSONStringField(jsonMessage, L"sessionId");
221232
if (m_devToolsSessionMap.ContainsKey(sessionId))
222233
{
223-
m_devToolsTargetDescriptionMap.Remove(m_devToolsSessionMap[sessionId]);
234+
m_devToolsTargetLabelMap.Remove(m_devToolsSessionMap[sessionId]);
224235
m_devToolsSessionMap.Remove(sessionId);
225236
}
226237
}
@@ -230,12 +241,12 @@ private void CoreWebView2_TargetInfoChanged(CoreWebView2 sender, CoreWebView2Dev
230241
// A target's info like url changed, update it in the target description map.
231242
string jsonMessage = args.ParameterObjectAsJson;
232243
string targetId = GetJSONStringField(jsonMessage, L"targetId");
233-
if (m_devToolsTargetDescriptionMap.ContainsKey(targetId))
244+
if (m_devToolsTargetLabelMap.ContainsKey(targetId))
234245
{
235246
// This is a target that we are interested in, update description.
236247
string type = GetJSONStringField(jsonMessage, L"type");
237248
string url = GetJSONStringField(jsonMessage, L"url");
238-
m_devToolsTargetDescriptionMap[targetId] = type + L"," + url;
249+
m_devToolsTargetLabelMap[targetId] = type + L"," + url;
239250
}
240251
}
241252
private void AddConsoleLogHandlers()
@@ -261,8 +272,9 @@ interface ICoreWebView2_10 : IUnknown {
261272
/// There could be multiple `DevToolsProtocol` targets in a WebView.
262273
/// Besides the top level page, iframes from different origin and web workers
263274
/// are also separate targets. Attaching to these targets allows interaction with them.
264-
/// These attachments to the targets are sessions and are identified by sessionId.
265-
/// To use this API, you should set `flatten` parameter to true when calling
275+
/// When the DevToolsProtocol is attached to a target, the connection is identified
276+
/// by a sessionId.
277+
/// To use this API, you must set `flatten` parameter to true when calling
266278
/// `Target.attachToTarget` or `Target.setAutoAttach` `DevToolsProtocol` method.
267279
/// Using `Target.setAutoAttach` is recommended as that would allow you to attach
268280
/// to dedicated worker target, which is not discoverable via other APIs like
@@ -311,7 +323,7 @@ namespace Microsoft.Web.WebView2.Core
311323
{
312324
// ICoreWebView2_10 members
313325
// This is an overload for: public async Task<string> CallDevToolsProtocolMethodAsync(string methodName, string parametersAsJson);
314-
public async Task<string> CallDevToolsProtocolMethodAsync(string methodName, string parametersAsJson, string sessionId);
326+
public async Task<string> CallDevToolsProtocolMethodForSessionAsync(string sessionId, string methodName, string parametersAsJson);
315327
}
316328
}
317329

0 commit comments

Comments
 (0)