From 537674a0b392d4a035beae1bbd858ba1d96fd838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aleksandr=20=C5=A0mailov?= Date: Wed, 29 Jan 2020 09:53:08 +0200 Subject: [PATCH] Improve RTE performance when using base64 images (#7530) --- .../Templates/HtmlImageSourceParserTests.cs | 84 +++++++++++++++++-- .../Templates/HtmlImageSourceParser.cs | 33 +++++--- 2 files changed, 97 insertions(+), 20 deletions(-) diff --git a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs index 3bef495507..bce9bd4155 100644 --- a/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests/Templates/HtmlImageSourceParserTests.cs @@ -1,7 +1,6 @@ using Umbraco.Core.Logging; using Moq; using NUnit.Framework; -using Umbraco.Core.Services; using Umbraco.Tests.Testing.Objects.Accessors; using Umbraco.Web.Templates; using Umbraco.Web; @@ -13,12 +12,10 @@ using System; using System.Linq; using Umbraco.Core.Models; using Umbraco.Core; -using Umbraco.Web.PropertyEditors; +using System.Diagnostics; namespace Umbraco.Tests.Templates { - - [TestFixture] public class HtmlImageSourceParserTests { @@ -31,10 +28,9 @@ namespace Umbraco.Tests.Templates

"; - var logger = Mock.Of(); var umbracoContextAccessor = new TestUmbracoContextAccessor(); var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); - + var result = imageSourceParser.FindUdisFromDataAttributes(input).ToList(); Assert.AreEqual(2, result.Count); Assert.AreEqual(Udi.Parse("umb://media/D4B18427A1544721B09AC7692F35C264"), result[0]); @@ -44,7 +40,6 @@ namespace Umbraco.Tests.Templates [Test] public void Remove_Image_Sources() { - var logger = Mock.Of(); var umbracoContextAccessor = new TestUmbracoContextAccessor(); var imageSourceParser = new HtmlImageSourceParser(umbracoContextAccessor); @@ -111,10 +106,83 @@ namespace Umbraco.Tests.Templates

", result); - } + } + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Empty source is not updated with no data-udi set" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Empty source is updated with data-udi set" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Filled source is overwritten with data-udi set" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Attributes are persisted" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Source is trimmed and parameters are prefixed" + )] + [TestCase( + @"
", + ExpectedResult = @"
", + TestName = "Parameters are prefixed" + )] + [TestCase( + @"
+ + + +
", + ExpectedResult = + @"
+ + + +
", + TestName = "Multiple img tags are handled" + )] + [Category("Ensure image sources")] + public string Ensure_ImageSources_Processing(string sourceHtml) + { + var fakeMediaUrl = "/media/1001/image.jpg"; + var parser = new HtmlImageSourceParser((guid) => fakeMediaUrl); + var actual = parser.EnsureImageSources(sourceHtml); + + return actual; + } + + [Category("Ensure image sources")] + [Test] + public void Ensure_Large_Html_Is_Processed_Quickly() + { + int symbolCount = 25000; + int maxMsToRun = 200; + + var longText = new string('*', symbolCount); + var text = $@""; + + var fakeMediaUrl = "/media/1001/image.jpg"; + var parser = new HtmlImageSourceParser((guid) => fakeMediaUrl); + + var timer = new Stopwatch(); + timer.Start(); + var actual = parser.EnsureImageSources(text); + timer.Stop(); + + Assert.IsTrue(timer.ElapsedMilliseconds <= maxMsToRun); } } } diff --git a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs index b0d6980ef3..dcc5318b89 100644 --- a/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs +++ b/src/Umbraco.Web/Templates/HtmlImageSourceParser.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text.RegularExpressions; using Umbraco.Core; @@ -7,18 +8,29 @@ namespace Umbraco.Web.Templates public sealed class HtmlImageSourceParser { - public HtmlImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor) + public HtmlImageSourceParser(Func getMediaUrl) { - _umbracoContextAccessor = umbracoContextAccessor; + this._getMediaUrl = getMediaUrl; } - private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)([^""]*""[^>]*data-udi="")([^""]*)(""[^>]*>)", + public HtmlImageSourceParser(IUmbracoContextAccessor umbracoContextAccessor) + { + if (umbracoContextAccessor?.UmbracoContext?.UrlProvider == null) + { + return; + } + + _getMediaUrl = (guid) => umbracoContextAccessor.UmbracoContext.UrlProvider.GetMediaUrl(guid); + } + + private static readonly Regex ResolveImgPattern = new Regex(@"(]*src="")([^""\?]*)((?:\?[^""]*)?""[^>]*data-udi="")([^""]*)(""[^>]*>)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - private readonly IUmbracoContextAccessor _umbracoContextAccessor; private static readonly Regex DataUdiAttributeRegex = new Regex(@"data-udi=\\?(?:""|')(?umb://[A-z0-9\-]+/[A-z0-9]+)\\?(?:""|')", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); + private readonly Func _getMediaUrl; + /// /// Parses out media UDIs from an html string based on 'data-udi' html attributes /// @@ -45,14 +57,13 @@ namespace Umbraco.Web.Templates /// Umbraco image tags are identified by their data-udi attributes public string EnsureImageSources(string text) { - // don't attempt to proceed without a context - if (_umbracoContextAccessor?.UmbracoContext?.UrlProvider == null) + // no point in doing any processing if we don't have + // a function to retrieve Urls + if (_getMediaUrl == null) { return text; } - var urlProvider = _umbracoContextAccessor.UmbracoContext.UrlProvider; - return ResolveImgPattern.Replace(text, match => { // match groups: @@ -66,7 +77,7 @@ namespace Umbraco.Web.Templates { return match.Value; } - var mediaUrl = urlProvider.GetMediaUrl(guidUdi.Guid); + var mediaUrl = _getMediaUrl(guidUdi.Guid); if (mediaUrl == null) { // image does not exist - we could choose to remove the image entirely here (return empty string), @@ -86,7 +97,5 @@ namespace Umbraco.Web.Templates public string RemoveImageSources(string text) // see comment in ResolveMediaFromTextString for group reference => ResolveImgPattern.Replace(text, "$1$3$4$5"); - - } }