Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DYN-5208-DynamoRevit-Crash-Fix #13245

Merged
merged 6 commits into from
Aug 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public partial class DocumentationBrowserView : UserControl, IDisposable
private const string VIRTUAL_FOLDER_MAPPING = "appassets";
static readonly string HTML_IMAGE_PATH_PREFIX = @"http://";

internal string WebBrowserUserDataFolder { get; set; }
internal string FallbackDirectoryName { get; set; }

/// <summary>
Expand Down Expand Up @@ -116,6 +117,15 @@ protected virtual void Dispose(bool disposing)

async void InitializeAsync()
{
if (!string.IsNullOrEmpty(WebBrowserUserDataFolder))
{
//This indicates in which location will be created the WebView2 cache folder
documentationBrowser.CreationProperties = new CoreWebView2CreationProperties()
{
UserDataFolder = WebBrowserUserDataFolder
};
}

//Initialize the CoreWebView2 component otherwise we can't navigate to a web page
await documentationBrowser.EnsureCoreWebView2Async();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public class DocumentationBrowserViewExtension : ViewExtensionBase, IViewExtensi
private const string FALLBACK_DOC_DIRECTORY_NAME = "fallback_docs";
//these fields should only be directly set by tests.
internal DirectoryInfo fallbackDocPath;
internal DirectoryInfo webBrowserUserDataFolder;

internal DocumentationBrowserView BrowserView { get; private set; }
internal DocumentationBrowserViewModel ViewModel { get; private set; }
Expand Down Expand Up @@ -71,14 +72,29 @@ public override void Startup(ViewStartupParams viewStartupParams)

if (!string.IsNullOrEmpty(pathManager.HostApplicationDirectory))
{
var docsDir = new DirectoryInfo(Path.Combine(pathManager.HostApplicationDirectory, FALLBACK_DOC_DIRECTORY_NAME));
//when running over any host app like Revit, FormIt, Civil3D... the path to the fallback_docs can change.
//e.g. for Revit the variable HostApplicationDirectory = C:\Program Files\Autodesk\Revit 2023\AddIns\DynamoForRevit\Revit
//Then we need to remove the last folder from the path so we can find the fallback_docs directory.
var hostAppDirectory = Directory.GetParent(pathManager.HostApplicationDirectory).FullName;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We may want to double check on this if there is a better way. I will get back to you

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So for Sandbox, this pathManager.HostApplicationDirectory would be null? I think this only means this is not an in process integration, for case like FormIt, where it is out of process but still delivered in programe files, this may still not work

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QilongTang
For other products like FormIt or Civil not sure which is the path of the Dynamo "fallback_doc" folder, I followed the implementation that I found in PackageDocumentationManager.cs (check link below) then if the fallback_doc folder location is different for each host (Revit, Civil, Formit) then the implementation done in PackageDocumentationManager is not working or hostDynamoFallbackDocPath = null or there is another process creating the folder and moving all the md/jpg files to the HostApplicationDirectory path.

Should I install FormIt and Civil so I can test if images are shown in DocumentationBrowser?

if (!string.IsNullOrEmpty(pathManager.HostApplicationDirectory))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QilongTang now the WebView2 cache folder will be always created in AppData, see the next commit:
e101e8a

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RobertGlobant20 Is the comment above still accurate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QilongTang fixed comment about Revit in the next commit:
2729c08

var docsDir = new DirectoryInfo(Path.Combine(hostAppDirectory, FALLBACK_DOC_DIRECTORY_NAME));
fallbackDocPath = docsDir.Exists ? docsDir : null;
}

if(this.BrowserView != null)
//When executing Dynamo as Sandbox or inside any host like Revit, FormIt, Civil3D the WebView2 cache folder will be located in the AppData folder
var userDataDir = new DirectoryInfo(pathManager.UserDataDirectory);
webBrowserUserDataFolder = userDataDir.Exists ? userDataDir : null;

if (this.BrowserView == null) return;

if(fallbackDocPath != null)
{
this.BrowserView.FallbackDirectoryName = fallbackDocPath.FullName;
}

if(webBrowserUserDataFolder != null)
{
this.BrowserView.WebBrowserUserDataFolder = webBrowserUserDataFolder.FullName;
}
}

public override void Loaded(ViewLoadedParams viewLoadedParams)
Expand Down
16 changes: 16 additions & 0 deletions src/DynamoCoreWpf/UI/GuidedTour/GuidesManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,17 @@ private Step CreateStep(Step jsonStepInfo, HostControlInfo hostControlInfo, int
var formattedText = Res.ResourceManager.GetString(jsonStepInfo.StepContent.FormattedText);
var title = Res.ResourceManager.GetString(jsonStepInfo.StepContent.Title);

DirectoryInfo userDataFolder = null;
if (dynamoViewModel != null)
{
var pathManager = dynamoViewModel.Model.PathManager;

//When executing Dynamo as Sandbox or inside any host like Revit, FormIt, Civil3D the WebView2 cache folder will be located in the AppData folder
var docsDir = new DirectoryInfo(pathManager.UserDataDirectory);
userDataFolder = docsDir.Exists ? docsDir : null;

}

switch (jsonStepInfo.StepType)
{
case Step.StepTypes.TOOLTIP:
Expand All @@ -446,6 +457,11 @@ private Step CreateStep(Step jsonStepInfo, HostControlInfo hostControlInfo, int
Title = title
}
};
var popupWindow = newStep.stepUIPopup as PopupWindow;
if(popupWindow != null && hostControlInfo.HtmlPage != null && !string.IsNullOrEmpty(hostControlInfo.HtmlPage.FileName))
{
popupWindow.WebBrowserUserDataFolder = userDataFolder != null ? userDataFolder.FullName : string.Empty;
}
Copy link
Contributor

@QilongTang QilongTang Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this code here? Shouldn't this be set one time only at the Welcome step? Is it because all of our Welcome steps not leveraging WebView2?

Copy link
Contributor Author

@RobertGlobant20 RobertGlobant20 Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@QilongTang
1st question:

  • we need to set the WebBrowserUserDataFolder before calling the WebView2.EnsureCoreWebView2Async() method so WebView2 can set the cache folder in the right location, I put it here because in GuidesManager we have access to DynamoViewModel.Model.PathManager.

2nd question.

  • I did a change that will be set only for popups that use html, check the commit below.

3rd question.

  • Not all the Welcome steps use html then it will be set only for the ones using html (and by consequence WebView2).

commit:
b37dcc1

Copy link
Member

@mjkkirschner mjkkirschner Aug 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RobertGlobant20 are there any MSDN docs on how these webview2 startup options affect other instances of WebView2 in the same process?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjkkirschner MS documentation doesn't talk specifically about that but I think the startup options are specific for each WebView2 instance.
If you question is because guided tours and documentationbrowser will be using the same cache - user data folder (UDF) folder, MS Documentation says "It's possible for your host app to overlap them" but doesn't say anything about the consequences, I just opened the "User Interface" tour that opens the DocumentationBrowser at the same time than a Popup opened but I didn't see any problem (the Popup is not using html for this case), then once Notification Center is completed we will need to see what happen if we open the Notifications Center and the DocumentationBrowser at the same time.
https://docs.microsoft.com/en-us/microsoft-edge/webview2/concepts/user-data-folder?tabs=win32

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RobertGlobant20 thanks- I'm also curious because inside of Hosts like Revit, they will use WebView2 and so will the other addins running in the Host process. It's the wild west!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjkkirschner well, we asked Revit about it but for my understanding they are still in the process of adopting WebView2 then they haven't deal with this feature yet so if Revit team decide to use another cache folder in AppData for WebView2 we can evaluate a change in Dynamo to use the same than them, another thing is that we need to ask if this feature of enable/disable cache for WebView2 will be enabled in the future (since I didn't find anything about it).
Also I checked with AutoCAD team and they said they create the folder in the user profile (consider that Autocad is using C++).
Thanks

break;
case Step.StepTypes.SURVEY:
newStep = new Survey(hostControlInfo, jsonStepInfo.Width, jsonStepInfo.Height)
Expand Down
14 changes: 13 additions & 1 deletion src/DynamoCoreWpf/Utilities/ResourceUtilities.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Dynamo.Logging;
using Dynamo.Wpf.Properties;
using Dynamo.Wpf.UI.GuidedTour;
using Dynamo.Wpf.Utilities;
using Microsoft.Web.WebView2.Wpf;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -330,12 +331,23 @@ internal static object ExecuteJSFunction(UIElement MainWindow, HostControlInfo p
/// <param name="resourcesPath">Path of the resources that will be loaded into the HTML page</param>
/// <param name="fontStylePath">Path to the Font Style that will be used in some part of the HTML page</param>
/// <param name="localAssembly">Local Assembly in which the resource will be loaded</param>
internal static async void LoadWebBrowser(HtmlPage htmlPage, WebView2 webBrowserComponent, string resourcesPath, string fontStylePath, Assembly localAssembly)
/// <param name="userDataFolder">the folder that WebView2 will use for storing cache info</param>
internal static async void LoadWebBrowser(HtmlPage htmlPage, WebView2 webBrowserComponent, string resourcesPath, string fontStylePath, Assembly localAssembly, string userDataFolder = default(string))
{
var bodyHtmlPage = ResourceUtilities.LoadContentFromResources(htmlPage.FileName, localAssembly, false, false);

bodyHtmlPage = LoadResouces(bodyHtmlPage, htmlPage.Resources, resourcesPath);
bodyHtmlPage = LoadResourceAndReplaceByKey(bodyHtmlPage, "#fontStyle", fontStylePath);

if (!string.IsNullOrEmpty(userDataFolder))
{
//This indicates in which location will be created the WebView2 cache folder
webBrowserComponent.CreationProperties = new CoreWebView2CreationProperties()
{
UserDataFolder = userDataFolder
};
}

await webBrowserComponent.EnsureCoreWebView2Async();
// Context menu disabled
webBrowserComponent.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
Expand Down
7 changes: 6 additions & 1 deletion src/DynamoCoreWpf/Views/GuidedTour/PopupWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ public partial class PopupWindow : Popup
//Assembly path to the Resources folder
private const string resourcesPath = "Dynamo.Wpf.Views.GuidedTour.HtmlPages.Resources";

/// <summary>
/// This property will be hold the path of the WebView2 cache folder, the value will change based in if DynamoSandbox is executed or Dynamo is executed from a different host (like Revit, FormIt, Civil, etc).
/// </summary>
internal string WebBrowserUserDataFolder { get; set; }

public PopupWindow(PopupWindowViewModel viewModel, HostControlInfo hInfo)
{
InitializeComponent();
Expand Down Expand Up @@ -116,7 +121,7 @@ private async void InitWebView2Component()
contentGrid.Children.Add(webBrowserComponent);
Grid.SetRow(webBrowserComponent, 1);

ResourceUtilities.LoadWebBrowser(hostControlInfo.HtmlPage, webBrowserComponent, resourcesPath, mainFontStylePath, GetType().Assembly);
ResourceUtilities.LoadWebBrowser(hostControlInfo.HtmlPage, webBrowserComponent, resourcesPath, mainFontStylePath, GetType().Assembly, WebBrowserUserDataFolder);
}


Expand Down