Skip to content

Commit b30f37b

Browse files
committed
Merge branch 'feature/PB-45984_Fix-webviews-synchronization-can-lead-to-black-screen' into 'release'
PB-45984 - Fix: webviews synchronization can lead to black screen See merge request passbolt/desktop/passbolt-windows!260
2 parents 3040119 + 941b082 commit b30f37b

35 files changed

+186
-160459
lines changed

passbolt/Controllers/MainController.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,9 @@ public async Task BackgroundNavigationStarting(WebView2 sender, CoreWebView2Navi
101101

102102
await this.LoadWebviews();
103103
this.SetWebviewSettings(webviewBackground);
104+
} else
105+
{
106+
WebviewOrchestratorService.Instance.SetRenderedStatus(false);
104107
}
105108
//When credentials are saved from import and we navigate to auth application we init the trusted domain to check API calls
106109
if (currentAccountMetaData == null && this.backgroundNavigationService.IsAuthApplication(args.Uri))
@@ -134,6 +137,9 @@ public async Task RenderedNavigationStarting(WebView2 sender, CoreWebView2Naviga
134137
if (args.Uri == this.blankPage)
135138
{
136139
this.SetWebviewSettings(webviewRendered);
140+
} else if(!MfaService.Instance.IsMfaUrls(args.Uri))
141+
{
142+
WebviewOrchestratorService.Instance.SetRenderedStatus(false);
137143
}
138144

139145
//Check if the Mfa is completed
@@ -314,6 +320,11 @@ protected void WebMessageReceived(object sender, CoreWebView2WebMessageReceivedE
314320
}
315321
else if (renderedNavigationService.canNavigate(webviewSender.Source))
316322
{
323+
if (ipc.topic == AllowedTopics.RENDERED_READY)
324+
{
325+
WebviewOrchestratorService.Instance.SetRenderedStatus(true);
326+
backgroundTopic.ProcessPendingMessages();
327+
}
317328
renderedTopic.ProceedMessage(ipc);
318329
}
319330
}

passbolt/Models/Messaging/AllowedTopics.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ private static void InitTopics()
8383
topics.AddRange(ListHelper.GetClassContantsToList(typeof(RbacTopics)));
8484
topics.AddRange(ListHelper.GetClassContantsToList(typeof(ResourceTopics)));
8585
topics.AddRange(ListHelper.GetClassContantsToList(typeof(ResourceTypeTopics)));
86-
topics.AddRange(ListHelper.GetClassContantsToList(typeof(ResourceTypeTopics)));
8786
topics.AddRange(ListHelper.GetClassContantsToList(typeof(RoleTopics)));
8887
topics.AddRange(ListHelper.GetClassContantsToList(typeof(SecretTopics)));
8988
topics.AddRange(ListHelper.GetClassContantsToList(typeof(SettingTopics)));

passbolt/Models/Messaging/Webviews/BackgroundTopic.cs

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,15 @@ public class BackgroundTopic : WebviewTopic
4444
private string pendingRequestId;
4545
private RbacService rbacService;
4646
private CookiesManager cookiesManager;
47+
private List<IPC> pendingMessages;
4748

4849
public BackgroundTopic(WebView2 background, WebView2 rendered, LocalFolderService localFolderService, BackgroundWebviewService backgroundWebviewService) : base(background, rendered, localFolderService, backgroundWebviewService)
4950
{
5051
credentialLockerService = new CredentialLockerService();
5152
passphrase = null;
5253
this.rbacService = new RbacService();
5354
cookiesManager = CookiesManager.Instance;
55+
pendingMessages = new List<IPC>();
5456
}
5557

5658
/// <summary>
@@ -71,8 +73,12 @@ public async override void ProceedMessage(IPC ipc)
7173
}
7274
else
7375
{
74-
//Basic behaviour
75-
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(AllowedTopics.BACKGROUND_READY)));
76+
WebviewOrchestratorService.Instance.SetBackgroundStatus(true);
77+
if(WebviewOrchestratorService.Instance.AreAllReady())
78+
{
79+
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(AllowedTopics.BACKGROUND_READY)));
80+
background.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(AllowedTopics.RENDERED_READY)));
81+
}
7682
if (passphrase != null)
7783
{
7884
background.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(AllowedTopics.BACKGROUND_STORE_PASSPHRASE, passphrase)));
@@ -142,13 +148,22 @@ public async override void ProceedMessage(IPC ipc)
142148
await downloadService.Download(ipc);
143149
break;
144150
case LocalStorageTopics.BACKGROUND_LOCALSTORAGE_UPDATE:
145-
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(LocalStorageTopics.RENDERED_LOCALSTORAGE_UPDATE, SerializationHelper.SerializeToJson(ipc.message))));
151+
if(this.canProceedMessage(ipc))
152+
{
153+
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(LocalStorageTopics.RENDERED_LOCALSTORAGE_UPDATE, SerializationHelper.SerializeToJson(ipc.message))));
154+
}
146155
break;
147156
case LocalStorageTopics.BACKGROUND_LOCALSTORAGE_DELETE:
148-
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(LocalStorageTopics.RENDERED_LOCALSTORAGE_DELETE, (string)ipc.message)));
157+
if (this.canProceedMessage(ipc))
158+
{
159+
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(LocalStorageTopics.RENDERED_LOCALSTORAGE_DELETE, (string)ipc.message)));
160+
}
149161
break;
150162
case LocalStorageTopics.BACKGROUND_LOCALSTORAGE_CLEAR:
151-
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(LocalStorageTopics.RENDERED_LOCALSTORAGE_CLEAR)));
163+
if (this.canProceedMessage(ipc))
164+
{
165+
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(LocalStorageTopics.RENDERED_LOCALSTORAGE_CLEAR)));
166+
}
152167
break;
153168
case AuthenticationTopics.LOG_OUT:
154169
this.currentIndexBackground = "index-auth.html";
@@ -175,7 +190,10 @@ public async override void ProceedMessage(IPC ipc)
175190
{
176191
AllowedTopics.AddRequestId(ipc.requestId);
177192
}
178-
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(ipc));
193+
if (this.canProceedMessage(ipc))
194+
{
195+
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(ipc));
196+
}
179197
break;
180198
case AllowedTopics.BACKGROUND_CLIPBOARD_SET_TEXT:
181199
DataPackage dataPackage = new DataPackage();
@@ -193,6 +211,7 @@ public async override void ProceedMessage(IPC ipc)
193211
AllowedTopics.RemovePendingRequest(ipc.topic);
194212
}
195213
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(ipc));
214+
196215
break;
197216
}
198217
}
@@ -213,6 +232,24 @@ public async Task RedirectToWorkspace()
213232
background.Source = new Uri(UriBuilderHelper.BuildHostUri(configuration.backgroundUrl, "/Background/index-workspace.html"));
214233
}
215234

235+
/// <summary>
236+
/// Process all pending messages when Rendered webview becomes ready
237+
/// </summary>
238+
public void ProcessPendingMessages()
239+
{
240+
if (pendingMessages.Count > 0)
241+
{
242+
var messages = new List<IPC>(pendingMessages);
243+
244+
foreach (var message in messages)
245+
{
246+
ProceedMessage(message);
247+
}
248+
249+
pendingMessages.Clear();
250+
}
251+
}
252+
216253
/// <summary>
217254
/// Proceed a pending request Id
218255
/// </summary>
@@ -240,5 +277,22 @@ public void mapResponse(IPC ipc, string topic)
240277
ipc.message = controls;
241278
}
242279
}
280+
281+
/// <summary>
282+
/// Check if the rendered webview is listening and if not we add the ipc message as pending
283+
/// This method should be added before calling each topic calling the rendered webview
284+
/// </summary>
285+
/// <param name="ipc"></param>
286+
/// <returns bool></returns>
287+
private bool canProceedMessage(IPC ipc)
288+
{
289+
if (!WebviewOrchestratorService.Instance.IsRenderedReady() && ipc.topic != AllowedTopics.BACKGROUND_READY)
290+
{
291+
pendingMessages.Add(ipc);
292+
return false;
293+
}
294+
295+
return true;
296+
}
243297
}
244298
}

passbolt/Models/Messaging/Webviews/RenderedTopic.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,15 @@ public override async void ProceedMessage(IPC ipc)
7575
{
7676
new UnauthorizedTopicException("Rendered webview");
7777
return;
78+
}
79+
else if (ipc.topic == AllowedTopics.RENDERED_READY)
80+
{
81+
WebviewOrchestratorService.Instance.SetRenderedStatus(true);
82+
if (WebviewOrchestratorService.Instance.AreAllReady())
83+
{
84+
rendered.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(AllowedTopics.BACKGROUND_READY)));
85+
background.CoreWebView2.PostWebMessageAsJson(SerializationHelper.SerializeToJson(new IPC(AllowedTopics.RENDERED_READY)));
86+
}
7887
}
7988
else if (CanOpenToBrowser(ipc))
8089
{
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/**
2+
* Passbolt ~ Open source password manager for teams
3+
* Copyright (c) 2023 Passbolt SA (https://www.passbolt.com)
4+
*
5+
* Licensed under GNU Affero General Public License version 3 of the or any later version.
6+
* For full copyright and license information, please see the LICENSE.txt
7+
* Redistributions of files must retain the above copyright notice.
8+
*
9+
* @copyright Copyright (c) 2025 Passbolt SA (https://www.passbolt.com)
10+
* @license https://opensource.org/licenses/AGPL-3.0 AGPL License
11+
* @link https://www.passbolt.com Passbolt(tm)
12+
* @since 2.5.0
13+
*/
14+

15+
using System;
16+
17+
namespace passbolt.Services.WebviewService
18+
{
19+
public sealed class WebviewOrchestratorService
20+
{
21+
private bool Rendered;
22+
private bool Background;
23+
24+
private static readonly Lazy<WebviewOrchestratorService> _instance = new Lazy<WebviewOrchestratorService>(() => new WebviewOrchestratorService());
25+
26+
public static WebviewOrchestratorService Instance => _instance.Value;
27+
28+
private WebviewOrchestratorService()
29+
{
30+
Rendered = true;
31+
Background = true;
32+
}
33+
34+
/// <summary>
35+
/// Sets the Rendered webview status
36+
/// </summary>
37+
/// <param name="status">True if ready, false otherwise</param>
38+
public void SetRenderedStatus(bool status)
39+
{
40+
Rendered = status;
41+
}
42+
43+
/// <summary>
44+
/// Sets the Background webview status
45+
/// </summary>
46+
/// <param name="status">True if ready, false otherwise</param>
47+
public void SetBackgroundStatus(bool status)
48+
{
49+
Background = status;
50+
}
51+
52+
/// <summary>
53+
/// Checks if the Rendered webview is ready
54+
/// </summary>
55+
/// <returns>True if the Rendered webview is ready</returns>
56+
public bool IsRenderedReady()
57+
{
58+
return Rendered;
59+
}
60+
61+
/// <summary>
62+
/// Checks if the Background webview is ready
63+
/// </summary>
64+
/// <returns>True if the Background webview is ready</returns>
65+
public bool IsBackgroundReady()
66+
{
67+
return Background;
68+
}
69+
70+
/// <summary>
71+
/// Checks if all webviews are ready
72+
/// </summary>
73+
/// <returns>True if all webviews are ready</returns>
74+
public bool AreAllReady()
75+
{
76+
return Rendered && Background;
77+
}
78+
}
79+
}

0 commit comments

Comments
 (0)