@@ -73,73 +73,92 @@ void RegisterIFramePermissionRequestedHandler()
7373 Callback<ICoreWebView2FramePermissionRequestedEventHandler >(
7474 [ this] (ICoreWebView2Frame* sender,
7575 ICoreWebView2PermissionRequestedEventArgs* args) -> HRESULT {
76- COREWEBVIEW2_PERMISSION_KIND kind =
77- COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION;
78- BOOL userInitiated = FALSE;
79- wil::unique_cotaskmem_string uri;
80-
81- CHECK_FAILURE (args->get_PermissionKind(&kind));
82- CHECK_FAILURE(args->get_IsUserInitiated(&userInitiated));
83- CHECK_FAILURE(args->get_Uri(&uri));
84-
85- auto cachedKey = std::tuple<
86- std::wstring, COREWEBVIEW2_PERMISSION_KIND, BOOL>(
87- std::wstring (uri.get()), kind, userInitiated);
88-
89- auto cachedPermission =
90- m_cachedPermissions.find(cachedKey);
91- if (cachedPermission != m_cachedPermissions.end())
92- {
93- bool allow = cachedPermission->second;
94- if (allow)
76+ // We avoid potential reentrancy from running a message loop
77+ // in the permission requested event handler by showing the
78+ // dialog via lambda run asynchronously outside of this event
79+ // handler.
80+ auto showDialog = [ this, args] {
81+ COREWEBVIEW2_PERMISSION_KIND kind =
82+ COREWEBVIEW2_PERMISSION_KIND_UNKNOWN_PERMISSION;
83+ BOOL userInitiated = FALSE;
84+ wil::unique_cotaskmem_string uri;
85+
86+ CHECK_FAILURE (args->get_PermissionKind(&kind));
87+ CHECK_FAILURE(args->get_IsUserInitiated(&userInitiated));
88+ CHECK_FAILURE(args->get_Uri(&uri));
89+
90+ auto cachedKey = std::tuple<
91+ std::wstring, COREWEBVIEW2_PERMISSION_KIND, BOOL>(
92+ std::wstring (uri.get()), kind, userInitiated);
93+
94+ auto cachedPermission =
95+ m_cachedPermissions.find(cachedKey);
96+ if (cachedPermission != m_cachedPermissions.end())
9597 {
96- CHECK_FAILURE (args->put_State(
97- COREWEBVIEW2_PERMISSION_STATE_ALLOW));
98+ bool allow = cachedPermission->second;
99+ if (allow)
100+ {
101+ CHECK_FAILURE (args->put_State(
102+ COREWEBVIEW2_PERMISSION_STATE_ALLOW));
103+ }
104+ else
105+ {
106+ CHECK_FAILURE(args->put_State(
107+ COREWEBVIEW2_PERMISSION_STATE_DENY));
108+ }
109+
110+ PutHandled (args);
111+ return S_OK;
98112 }
99- else
113+
114+ std::wstring message =
115+ L"An iframe has requested device permission for ";
116+ message += NameOfPermissionKind(kind);
117+ message += L" to the website at ";
118+ message += uri.get();
119+ message += L"?\n\n";
120+ message += L"Do you want to grant permission?\n";
121+ message +=
122+ (userInitiated
123+ ? L"This request came from a user gesture."
124+ : L"This request did not come from a user "
125+ L"gesture.");
126+
127+ int response = MessageBox(
128+ nullptr, message.c_str(), L"Permission Request",
129+ MB_YESNOCANCEL | MB_ICONWARNING);
130+
131+ COREWEBVIEW2_PERMISSION_STATE state =
132+ COREWEBVIEW2_PERMISSION_STATE_DEFAULT;
133+
134+ if (response == IDYES)
135+ {
136+ m_cachedPermissions[cachedKey] = true;
137+ state = COREWEBVIEW2_PERMISSION_STATE_ALLOW;
138+ }
139+ else if (response == IDNO)
100140 {
101- CHECK_FAILURE(args->put_State(
102- COREWEBVIEW2_PERMISSION_STATE_DENY)) ;
141+ m_cachedPermissions[cachedKey] = false;
142+ state = COREWEBVIEW2_PERMISSION_STATE_DENY;
103143 }
104144
145+ CHECK_FAILURE (args->put_State(state));
146+
105147 PutHandled (args);
106148 return S_OK;
107- }
108-
109- std::wstring message =
110- L"An iframe has requested device permission for ";
111- message += NameOfPermissionKind(kind);
112- message += L" to the website at ";
113- message += uri.get();
114- message += L"?\n\n";
115- message += L"Do you want to grant permission?\n";
116- message +=
117- (userInitiated
118- ? L"This request came from a user gesture."
119- : L"This request did not come from a user "
120- L"gesture.");
121-
122- int response = MessageBox(
123- nullptr, message.c_str(), L"Permission Request",
124- MB_YESNOCANCEL | MB_ICONWARNING);
125-
126- COREWEBVIEW2_PERMISSION_STATE state =
127- COREWEBVIEW2_PERMISSION_STATE_DEFAULT;
128-
129- if (response == IDYES)
130- {
131- m_cachedPermissions[cachedKey] = true;
132- state = COREWEBVIEW2_PERMISSION_STATE_ALLOW;
133- }
134- else if (response == IDNO)
135- {
136- m_cachedPermissions[cachedKey] = false;
137- state = COREWEBVIEW2_PERMISSION_STATE_DENY;
138- }
139-
140- CHECK_FAILURE (args->put_State(state));
141-
142- PutHandled (args);
149+ };
150+
151+ // Obtain a deferral for the event so that the CoreWebView2
152+ // doesn't examine the properties we set on the event args until
153+ // after we call the Complete method asynchronously later.
154+ wil::com_ptr<ICoreWebView2Deferral> deferral;
155+ CHECK_FAILURE (args->GetDeferral(&deferral));
156+
157+ m_appWindow->RunAsync([deferral, showDialog]() {
158+ showDialog ();
159+ CHECK_FAILURE(deferral->Complete());
160+ });
161+
143162 return S_OK;
144163 }).Get(),
145164 &m_PermissionRequestedToken));
@@ -201,51 +220,74 @@ void RegisterIFramePermissionRequestedHandler()
201220{
202221 m_webview.CoreWebView2.FrameCreated += (sender, frameCreatedArgs) =>
203222 {
204- frameCreatedArgs.Frame.PermissionRequested += (frameSender, permissionArgs) =>
223+ // Checking for runtime support of CoreWebView2Frame.PermissionRequested
224+ try
205225 {
206- var cachedKey = Tuple.Create(permissionArgs.Uri,
207- permissionArgs.PermissionKind, permissionArgs.IsUserInitiated);
208-
209- if (m_cachedPermissions.ContainsKey(cachedKey))
226+ frameCreatedArgs.Frame.PermissionRequested += (frameSender, permissionArgs) =>
210227 {
211- permissionArgs.State = m_cachedPermissions[cachedKey]
212- ? CoreWebView2PermissionState.Allow
213- : CoreWebView2PermissionState.Deny;
214-
215- permissionArgs.Handled = true;
216- return;
217- }
218-
219- string message = "An iframe has requested device permission for ";
220- message += NameOfPermissionKind(permissionArgs.PermissionKind);
221- message += " to the website at ";
222- message += permissionArgs.Uri;
223- message += "\n\n";
224- message += "Do you want to grant permission?\n";
225- message +=
226- (permissionArgs.IsUserInitiated
227- ? "This request came from a user gesture."
228- : "This request did not come from a user gesture.");
229-
230- var selection = MessageBox.Show(message, "iframe PermissionRequest",
231- MessageBoxButton.YesNoCancel);
232- if (selection == MessageBoxResult.Yes)
233- {
234- permissionArgs.State = CoreWebView2PermissionState.Allow;
235- m_cachedPermissions[cachedKey] = true;
236- }
237- else if (selection == MessageBoxResult.No)
238- {
239- permissionArgs.State = CoreWebView2PermissionState.Deny;
240- m_cachedPermissions[cachedKey] = false;
241- }
242- else
243- {
244- permissionArgs.State = CoreWebView2PermissionState.Default;
245- }
228+ // Developer can obtain a deferral for the event so that the CoreWebView2
229+ // doesn't examine the properties we set on the event args until
230+ // after the deferral completes asynchronously.
231+ CoreWebView2Deferral deferral = args.GetDeferral();
232+
233+ // We avoid potential reentrancy from running a message loop
234+ // in the permission requested event handler by showing the
235+ // dialog asynchronously.
236+ System.Threading.SynchronizationContext.Current.Post((_) => {
237+ using (deferral)
238+ {
239+ var cachedKey = Tuple.Create(permissionArgs.Uri,
240+ permissionArgs.PermissionKind, permissionArgs.IsUserInitiated);
241+
242+ if (m_cachedPermissions.ContainsKey(cachedKey))
243+ {
244+ permissionArgs.State = m_cachedPermissions[cachedKey]
245+ ? CoreWebView2PermissionState.Allow
246+ : CoreWebView2PermissionState.Deny;
247+
248+ PutHandled(permissionArgs);
249+ return;
250+ }
251+
252+ string message = "An iframe has requested device permission for ";
253+ message += NameOfPermissionKind(permissionArgs.PermissionKind);
254+ message += " to the website at ";
255+ message += permissionArgs.Uri;
256+ message += "\n\n";
257+ message += "Do you want to grant permission?\n";
258+ message +=
259+ (permissionArgs.IsUserInitiated
260+ ? "This request came from a user gesture."
261+ : "This request did not come from a user gesture.");
262+
263+ var selection = MessageBox.Show(message, "iframe PermissionRequest",
264+ MessageBoxButton.YesNoCancel);
265+
266+ permissionArgs.State = CoreWebView2PermissionState.Default;
267+
268+ if (selection == MessageBoxResult.Yes)
269+ {
270+ permissionArgs.State = CoreWebView2PermissionState.Allow;
271+ m_cachedPermissions[cachedKey] = true;
272+ }
273+ else if (selection == MessageBoxResult.No)
274+ {
275+ permissionArgs.State = CoreWebView2PermissionState.Deny;
276+ m_cachedPermissions[cachedKey] = false;
277+ }
278+
279+ PutHandled(permissionArgs);
280+ }
281+ }, null);
282+
246283
247- permissionArgs.Handled = true;
248- };
284+ };
285+ }
286+ catch (NotImplementedException exception)
287+ {
288+ MessageBox.Show(this, "Frame Permission Requested Failed: " + exception.Message,
289+ "Frame Permission Requested");
290+ }
249291 };
250292}
251293
@@ -269,6 +311,29 @@ string NameOfPermissionKind(CoreWebView2PermissionKind kind)
269311 return "Unknown resources";
270312 }
271313}
314+
315+ void PutHandled(CoreWebView2PermissionEventArgs args)
316+ {
317+ // In the case of an iframe requesting permission, the default behavior is
318+ // to first raise the PermissionRequested event off of the CoreWebView2Frame
319+ // and invoke it's handlers, and then raise the event off the CoreWebView2
320+ // and invoke it's handlers. However, If we set Handled to true on the
321+ // CoreWebView2Frame event handler, then we will not raise the
322+ // PermissionRequested event off the CoreWebView2.
323+ try
324+ {
325+ // NotImplementedException could be thrown if underlying runtime did not
326+ // implement Handled. However, we only run this code after checking if
327+ // CoreWebView2Frame.PermissionRequested exists, and both exist together,
328+ // so it would not be a problem.
329+ permissionArgs.Handled = true;
330+ }
331+ catch(NotImplementedException)
332+ {
333+ MessageBox.Show(this, "Put Handled Failed: " + exception.Message,
334+ "Frame Permission Requested Handled");
335+ }
336+ }
272337```
273338
274339# API Details
0 commit comments