fix infinite loops and remove public apis (#5954)
authorShane Neuville <shane94@hotmail.com>
Wed, 24 Apr 2019 22:25:12 +0000 (16:25 -0600)
committerGitHub <noreply@github.com>
Wed, 24 Apr 2019 22:25:12 +0000 (16:25 -0600)
* fix infinite loops and remove public apis

* - remove comments, fix location to not have implicit

* force uri to be relative on ios when no scheme specified

* address PR comments

* add error message

* reformat absolute uris

* additional suggestions

* Update Xamarin.Forms.Core/Shell/ShellNavigationState.cs

Co-Authored-By: PureWeen <shane94@hotmail.com>
* Update Xamarin.Forms.Core/Shell/ShellUriHandler.cs

Co-Authored-By: PureWeen <shane94@hotmail.com>
* Update Xamarin.Forms.Core/Shell/ShellUriHandler.cs

Co-Authored-By: PureWeen <shane94@hotmail.com>
* Update Xamarin.Forms.Core/Shell/ShellUriHandler.cs

Co-Authored-By: PureWeen <shane94@hotmail.com>
Xamarin.Forms.Core.UnitTests/ShellTestBase.cs
Xamarin.Forms.Core.UnitTests/ShellTests.cs
Xamarin.Forms.Core.UnitTests/ShellUriHandlerTests.cs
Xamarin.Forms.Core/Routing.cs
Xamarin.Forms.Core/Shell/IShellItemController.cs
Xamarin.Forms.Core/Shell/IShellSectionController.cs
Xamarin.Forms.Core/Shell/Shell.cs
Xamarin.Forms.Core/Shell/ShellItem.cs
Xamarin.Forms.Core/Shell/ShellNavigationState.cs
Xamarin.Forms.Core/Shell/ShellSection.cs
Xamarin.Forms.Core/Shell/ShellUriHandler.cs

index c8cb062..010cc62 100644 (file)
@@ -24,7 +24,7 @@ namespace Xamarin.Forms.Core.UnitTests
 
                }
 
-               protected Uri CreateUri(string uri) => new Uri(uri, UriKind.RelativeOrAbsolute);
+               protected Uri CreateUri(string uri) => ShellUriHandler.CreateUri(uri);
 
                protected ShellSection MakeSimpleShellSection(string route, string contentRoute)
                {
index 495647f..045a825 100644 (file)
@@ -94,11 +94,11 @@ namespace Xamarin.Forms.Core.UnitTests
                        shell.Items.Add(one);
                        shell.Items.Add(two);
 
-                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/one/tabone/content/"));
+                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/one/tabone/content"));
 
                        shell.GoToAsync(new ShellNavigationState("app:///s/two/tabfour/"));
 
-                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tabfour/content/"));
+                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tabfour/content"));
                }
 
                [Test]
@@ -161,14 +161,14 @@ namespace Xamarin.Forms.Core.UnitTests
 
                        await shell.GoToAsync("app:///s/two/tab21/");
 
-                       await shell.GoToAsync("/tab22");
-                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tab22/content/"));
+                       await shell.GoToAsync("/tab22", false, true);
+                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tab22/content"));
 
-                       await shell.GoToAsync("tab21");
-                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tab21/content/"));
+                       await shell.GoToAsync("tab21", false, true);
+                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tab21/content"));
 
-                       await shell.GoToAsync("/tab23");
-                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tab23/content/"));
+                       await shell.GoToAsync("/tab23", false, true);
+                       Assert.That(shell.CurrentState.Location.ToString(), Is.EqualTo("app:///s/two/tab23/content"));
 
                        /*
                         * removing support for .. notation for now
index c6fc643..3ad5999 100644 (file)
@@ -17,13 +17,57 @@ namespace Xamarin.Forms.Core.UnitTests
                }
 
                [Test]
-               public async Task GlobalRegisterAbsoluteMatching()
+               public async Task RouteWithGlobalPageRoute()
+               {
+
+                       var shell = new Shell() { RouteScheme = "app", Route= "xaminals", RouteHost = "thehost" };
+                       var item1 = CreateShellItem(asImplicit: true, shellItemRoute: "animals", shellSectionRoute: "domestic", shellContentRoute: "dogs");
+                       var item2 = CreateShellItem(asImplicit: true, shellItemRoute: "animals", shellSectionRoute: "domestic", shellContentRoute: "cats");
+
+                       shell.Items.Add(item1);
+                       shell.Items.Add(item2);
+
+                       Routing.RegisterRoute("catdetails", typeof(ContentPage));
+                       await shell.GoToAsync("//cats/catdetails?name=3");
+
+                       Assert.AreEqual("app://thehost/xaminals/animals/domestic/cats/catdetails", shell.CurrentState.Location.ToString());
+               }
+
+               [Test]
+               public async Task AbsoluteRoutingToPage()
+               {
+
+                       var shell = new Shell() { RouteScheme = "app", Route = "xaminals", RouteHost = "thehost" };
+                       var item1 = CreateShellItem(asImplicit: true, shellItemRoute: "animals", shellSectionRoute: "domestic", shellContentRoute: "dogs");
+                       shell.Items.Add(item1);
+
+                       Routing.RegisterRoute("catdetails", typeof(ContentPage));
+
+                       Assert.That(async () => await shell.GoToAsync($"//catdetails"), Throws.Exception);
+               }
+
+
+               [Test]
+               public async Task LocationRemovesImplicit()
+               {
+
+                       var shell = new Shell() { RouteScheme = "app" };
+                       var item1 = CreateShellItem(asImplicit: true, shellContentRoute: "rootlevelcontent1");
+
+                       shell.Items.Add(item1);
+
+                       Assert.AreEqual("app:///rootlevelcontent1", shell.CurrentState.Location.ToString());
+               }
+
+
+               [Test]
+               public async Task GlobalRegisterAbsoluteMatching()      
                {
                        var shell = new Shell() { RouteScheme = "app", Route = "shellroute" };
                        Routing.RegisterRoute("/seg1/seg2/seg3", typeof(object));
-                       var request = ShellUriHandler.GetNavigationRequest(shell, CreateUri("app://seg1/seg2/seg3"));
+                       var request = ShellUriHandler.GetNavigationRequest(shell, CreateUri("/seg1/seg2/seg3"));
 
-                       Assert.AreEqual("/seg1/seg2/seg3", request.Request.ShortUri.ToString());
+                       Assert.AreEqual("app:///shellroute/seg1/seg2/seg3", request.Request.FullUri.ToString());
                }
 
                [Test]
@@ -54,7 +98,7 @@ namespace Xamarin.Forms.Core.UnitTests
                        shell.Items.Add(item1);
                        shell.Items.Add(item2);
                        await shell.GoToAsync("//item1/section1/rootlevelcontent1");
-                       var request = ShellUriHandler.GetNavigationRequest(shell, CreateUri("section1/edit"));
+                       var request = ShellUriHandler.GetNavigationRequest(shell, CreateUri("section1/edit"), true);
 
                        Assert.AreEqual(1, request.Request.GlobalRoutes.Count);
                        Assert.AreEqual("item1/section1/edit", request.Request.GlobalRoutes.First());
@@ -123,7 +167,7 @@ namespace Xamarin.Forms.Core.UnitTests
                        shell.Items.Add(item1);
 
                        await shell.GoToAsync("//rootlevelcontent1");
-                       var request = ShellUriHandler.GetNavigationRequest(shell, CreateUri("edit"));
+                       var request = ShellUriHandler.GetNavigationRequest(shell, CreateUri("edit"), true);
 
                        Assert.AreEqual("section1/edit", request.Request.GlobalRoutes.First());
                }
@@ -140,8 +184,8 @@ namespace Xamarin.Forms.Core.UnitTests
                        shell.Items.Add(item1);
 
                        await shell.GoToAsync("//rootlevelcontent1");
-                       var location = shell.CurrentState.Location;
-                       await shell.GoToAsync("edit");
+                       var location = shell.CurrentState.FullLocation;
+                       await shell.GoToAsync("edit", false, true);
 
                        Assert.AreEqual(editShellContent, shell.CurrentItem.CurrentItem.CurrentItem);
                }
@@ -213,6 +257,50 @@ namespace Xamarin.Forms.Core.UnitTests
 
 
                [Test]
+               public async Task AbsoluteNavigationToRelativeWithGlobal()
+               {
+                       var shell = new Shell() { RouteScheme = "app", RouteHost = "xamarin.com", Route = "xaminals" };
+
+                       var item1 = CreateShellItem(asImplicit: true, shellContentRoute: "dogs");
+                       var item2 = CreateShellItem(asImplicit: true, shellSectionRoute: "domestic", shellContentRoute: "cats", shellItemRoute: "animals");
+
+                       shell.Items.Add(item1);
+                       shell.Items.Add(item2);
+
+                       Routing.RegisterRoute("catdetails", typeof(ContentPage));
+                       await shell.GoToAsync($"app://xamarin.com/xaminals/animals/domestic/cats/catdetails?name=domestic");
+
+                       Assert.AreEqual(
+                               "app://xamarin.com/xaminals/animals/domestic/cats/catdetails",
+                               shell.CurrentState.FullLocation.ToString()
+                               );
+               }
+
+               [Test]
+               public async Task RelativeNavigationWithRoute()
+               {
+                       var shell = new Shell() { RouteScheme = "app", RouteHost = "xamarin.com", Route = "xaminals" };
+
+                       var item1 = CreateShellItem(asImplicit: true, shellContentRoute: "dogs");
+                       var item2 = CreateShellItem(asImplicit: true, shellSectionRoute: "domestic", shellContentRoute: "cats", shellItemRoute: "animals");
+
+                       shell.Items.Add(item1);
+                       shell.Items.Add(item2);
+
+                       Routing.RegisterRoute("catdetails", typeof(ContentPage));
+                       Assert.That(async () => await shell.GoToAsync($"cats/catdetails?name=domestic"), Throws.Exception);
+
+                       // once relative routing with a stack is fixed then we can remove the above exception check and add below back in
+                       // await shell.GoToAsync($"cats/catdetails?name=domestic")
+                       //Assert.AreEqual(
+                       //      "app://xamarin.com/xaminals/animals/domestic/cats/catdetails",
+                       //      shell.CurrentState.Location.ToString()
+                       //      );
+
+               }
+
+
+               [Test]
                public async Task ConvertToStandardFormat()
                {
                        var shell = new Shell() { RouteScheme = "app", Route = "shellroute", RouteHost = "host" };
@@ -237,7 +325,8 @@ namespace Xamarin.Forms.Core.UnitTests
                                CreateUri("app://shellroute/path"),
                                CreateUri("app:/shellroute/path"),
                                CreateUri("app://host/shellroute/path"),
-                               CreateUri("app:/host/shellroute/path")
+                               CreateUri("app:/host/shellroute/path"),
+                               CreateUri("app:/host/shellroute\\path")
                        };
 
 
index 6a54744..be1efd7 100644 (file)
@@ -10,6 +10,7 @@ namespace Xamarin.Forms
                static Dictionary<string, RouteFactory> s_routes = new Dictionary<string, RouteFactory>();
 
                internal const string ImplicitPrefix = "IMPL_";
+               const string _pathSeparator = "/";
 
                internal static string GenerateImplicitRoute(string source)
                {
@@ -42,7 +43,7 @@ namespace Xamarin.Forms
                        return bindable.GetType().Name + ++s_routeCount;
                }
 
-               public static string[] GetRouteKeys()
+               internal static string[] GetRouteKeys()
                {
                        string[] keys = new string[s_routes.Count];
                        s_routes.Keys.CopyTo(keys, 0);
@@ -84,9 +85,26 @@ namespace Xamarin.Forms
                        return $"{source}/";
                }
 
+               internal static Uri RemoveImplicit(Uri uri)
+               {
+                       uri = ShellUriHandler.FormatUri(uri);
+
+                       if (!uri.IsAbsoluteUri)
+                               return uri;
+
+                       string[] parts = uri.OriginalString.TrimEnd(_pathSeparator[0]).Split(_pathSeparator[0]);
+
+                       List<string> toKeep = new List<string>();
+                       for (int i = 0; i < parts.Length; i++)
+                               if (!IsImplicit(parts[i]))
+                                       toKeep.Add(parts[i]);
+
+                       return new Uri(string.Join(_pathSeparator, toKeep));
+               }
+
                public static string FormatRoute(List<string> segments)
                {
-                       var route = FormatRoute(String.Join("/", segments));
+                       var route = FormatRoute(String.Join(_pathSeparator, segments));
                        return route;
                }
 
@@ -128,7 +146,7 @@ namespace Xamarin.Forms
                static void ValidateRoute(string route)
                {
                        if (string.IsNullOrWhiteSpace(route))
-                               throw new ArgumentNullException("Route cannot be an empty string");
+                               throw new ArgumentNullException(nameof(route), "Route cannot be an empty string");
 
                        var uri = new Uri(route, UriKind.RelativeOrAbsolute);
 
index 0ca8839..f394943 100644 (file)
@@ -6,8 +6,6 @@ namespace Xamarin.Forms
 {
        public interface IShellItemController : IElementController
        {
-               Task GoToPart(NavigationRequest navigationRequest, Dictionary<string, string> queryData);
-
                bool ProposeSection(ShellSection shellSection, bool setValue = true);
        }
 }
\ No newline at end of file
index 6e51040..de745eb 100644 (file)
@@ -15,8 +15,6 @@ namespace Xamarin.Forms
 
                void AddDisplayedPageObserver(object observer, Action<Page> callback);
 
-               Task GoToPart(NavigationRequest request, Dictionary<string, string> queryData);
-
                bool RemoveContentInsetObserver(IShellContentInsetObserver observer);
 
                bool RemoveDisplayedPageObserver(object observer);
index bae08e6..0945642 100644 (file)
@@ -363,12 +363,10 @@ namespace Xamarin.Forms
                                                var content = section.Items[k];
 
                                                string longUri = $"{RouteScheme}://{RouteHost}/{Routing.GetRoute(this)}/{Routing.GetRoute(item)}/{Routing.GetRoute(section)}/{Routing.GetRoute(content)}";
-                                               string shortUri = $"{RouteScheme}://{RouteHost}/{Routing.GetRoutePathIfNotImplicit(this)}{Routing.GetRoutePathIfNotImplicit(item)}{Routing.GetRoutePathIfNotImplicit(section)}{Routing.GetRoutePathIfNotImplicit(content)}";
 
                                                longUri = longUri.TrimEnd('/');
-                                               shortUri = shortUri.TrimEnd('/');
 
-                                               routes.Add(new RequestDefinition(longUri, shortUri, item, section, content, new List<string>()));
+                                               routes.Add(new RequestDefinition(longUri, item, section, content, new List<string>()));
                                        }
                                }
                        }
@@ -376,7 +374,12 @@ namespace Xamarin.Forms
                        return routes;
                }
 
-               public async Task GoToAsync(ShellNavigationState state, bool animate = true)
+               public Task GoToAsync(ShellNavigationState state, bool animate = true)
+               {
+                       return GoToAsync(state, animate, false);
+               }
+
+               internal async Task GoToAsync(ShellNavigationState state, bool animate, bool enableRelativeShellRoutes)
                {
                        // FIXME: This should not be none, we need to compute the delta and set flags correctly
                        var accept = ProposeNavigation(ShellNavigationSource.Unknown, state, true);
@@ -385,7 +388,7 @@ namespace Xamarin.Forms
 
                        _accumulateNavigatedEvents = true;
 
-                       var navigationRequest = ShellUriHandler.GetNavigationRequest(this, state.Location);
+                       var navigationRequest = ShellUriHandler.GetNavigationRequest(this, state.FullLocation, enableRelativeShellRoutes);
                        var uri = navigationRequest.Request.FullUri;
                        var queryString = navigationRequest.Query;
                        var queryData = ParseQueryString(queryString);
@@ -419,7 +422,7 @@ namespace Xamarin.Forms
                                parts.RemoveAt(0);
 
                                if (parts.Count > 0)
-                                       await ((IShellItemController)shellItem).GoToPart(navigationRequest, queryData);
+                                       await shellItem.GoToPart(navigationRequest, queryData);
                        }
                        else
                        {
@@ -502,29 +505,20 @@ namespace Xamarin.Forms
                        if (shellItem != null)
                        {
                                var shellItemRoute = shellItem.Route;
-                               //if (!shellItemRoute.StartsWith(Routing.ImplicitPrefix, StringComparison.Ordinal))
-                               {
-                                       stateBuilder.Append(shellItemRoute);
-                                       stateBuilder.Append("/");
-                               }
+                               stateBuilder.Append(shellItemRoute);
+                               stateBuilder.Append("/");
 
                                if (shellSection != null)
                                {
                                        var shellSectionRoute = shellSection.Route;
-                                       //if (!shellSectionRoute.StartsWith(Routing.ImplicitPrefix, StringComparison.Ordinal))
-                                       {
-                                               stateBuilder.Append(shellSectionRoute);
-                                               stateBuilder.Append("/");
-                                       }
+                                       stateBuilder.Append(shellSectionRoute);
+                                       stateBuilder.Append("/");
 
                                        if (shellContent != null)
                                        {
                                                var shellContentRoute = shellContent.Route;
-                                               //if (!shellContentRoute.StartsWith(Routing.ImplicitPrefix, StringComparison.Ordinal))
-                                               {
-                                                       stateBuilder.Append(shellContentRoute);
-                                                       stateBuilder.Append("/");
-                                               }
+                                               stateBuilder.Append(shellContentRoute);
+                                               stateBuilder.Append("/");
                                        }
 
                                        if (!stackAtRoot)
index a4cff83..7163c8f 100644 (file)
@@ -27,7 +27,7 @@ namespace Xamarin.Forms
 
                #region IShellItemController
 
-               Task IShellItemController.GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
+               internal Task GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
                {
                        var shellSection = request.Request.Section;
 
@@ -39,7 +39,7 @@ namespace Xamarin.Forms
                        if (CurrentItem != shellSection)
                                SetValueFromRenderer(CurrentItemProperty, shellSection);
 
-                       return ((IShellSectionController)shellSection).GoToPart(request, queryData);
+                       return shellSection.GoToPart(request, queryData);
                }
 
                bool IShellItemController.ProposeSection(ShellSection shellSection, bool setValue)
index 93e80b2..0e8c64f 100644 (file)
@@ -7,12 +7,33 @@ namespace Xamarin.Forms
        [DebuggerDisplay("Location = {Location}")]
        public class ShellNavigationState
        {
-               public Uri Location { get; set; }
+               Uri _fullLocation;
+
+               internal Uri FullLocation
+               {
+                       get => _fullLocation;
+                       set
+                       {
+                               _fullLocation = value;
+                               Location = Routing.RemoveImplicit(value);
+                       }
+               }
+
+               public Uri Location
+               {
+                       get;
+                       private set;
+               }
 
                public ShellNavigationState() { }
-               public ShellNavigationState(string location) => Location = new Uri(location, UriKind.RelativeOrAbsolute);
-               public ShellNavigationState(Uri location) => Location = location;
+               public ShellNavigationState(string location)
+               {
+                       FullLocation = ShellUriHandler.CreateUri(location);
+
+               }
+
+               public ShellNavigationState(Uri location) => FullLocation = location;
                public static implicit operator ShellNavigationState(Uri uri) => new ShellNavigationState(uri);
                public static implicit operator ShellNavigationState(string value) => new ShellNavigationState(value);
        }
-}
\ No newline at end of file
+}
index 6d46577..41a5b8f 100644 (file)
@@ -68,7 +68,7 @@ namespace Xamarin.Forms
                        callback(DisplayedPage);
                }
 
-               Task IShellSectionController.GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
+               internal Task GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
                {
                        ShellContent shellContent = request.Request.Content;
 
index d9be121..285a389 100644 (file)
@@ -9,19 +9,36 @@ namespace Xamarin.Forms
 
        internal class ShellUriHandler
        {
-               static readonly char[] _pathSeparator = { '/', '\\' };
+               static readonly char[] _pathSeparators = { '/', '\\' };
+               const string _pathSeparator = "/";
 
-               static Uri FormatUri(Uri path)
+               internal static Uri FormatUri(Uri path)
                {
                        if (path.IsAbsoluteUri)
-                               return path;
+                               return new Uri(FormatUri(path.OriginalString), UriKind.Absolute);
 
                        return new Uri(FormatUri(path.OriginalString), UriKind.Relative);
                }
 
-               static string FormatUri(string path)
+               internal static string FormatUri(string path)
                {
-                       return path.Replace("\\", "/");
+                       return path.Replace(_pathSeparators[1], _pathSeparator[0]);
+               }
+
+               internal static Uri CreateUri(string path)
+               {
+                       path = FormatUri(path);
+
+                       // on iOS if the uri starts with // it'll instantiate as absolute with
+                       // file: as the default scheme where as android just crashes
+                       // so this checks if it starts with / and just forces relative
+                       if (path.StartsWith(_pathSeparator, StringComparison.Ordinal))
+                               return new Uri(path, UriKind.Relative);
+
+                       if (Uri.TryCreate(path, UriKind.Absolute, out Uri result))
+                               return result;
+
+                       return new Uri(path, UriKind.Relative);
                }
 
                public static Uri ConvertToStandardFormat(Shell shell, Uri request)
@@ -33,7 +50,7 @@ namespace Xamarin.Forms
                        else
                                pathAndQuery = request.OriginalString;
 
-                       var segments = new List<string>(pathAndQuery.Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries));
+                       var segments = new List<string>(pathAndQuery.Split(_pathSeparators, StringSplitOptions.RemoveEmptyEntries));
 
 
                        if (segments[0] != shell.RouteHost)
@@ -42,27 +59,27 @@ namespace Xamarin.Forms
                        if (segments[1] != shell.Route)
                                segments.Insert(1, shell.Route);
 
-                       var path = String.Join("/", segments.ToArray());
+                       var path = String.Join(_pathSeparator, segments.ToArray());
                        string uri = $"{shell.RouteScheme}://{path}";
 
                        return new Uri(uri);
                }
 
-               public static NavigationRequest GetNavigationRequest(Shell shell, Uri uri)
+               internal static NavigationRequest GetNavigationRequest(Shell shell, Uri uri, bool enableRelativeShellRoutes = false)
                {
                        uri = FormatUri(uri);
                        // figure out the intent of the Uri
                        NavigationRequest.WhatToDoWithTheStack whatDoIDo = NavigationRequest.WhatToDoWithTheStack.PushToIt;
                        if (uri.IsAbsoluteUri)
                                whatDoIDo = NavigationRequest.WhatToDoWithTheStack.ReplaceIt;
-                       else if (uri.OriginalString.StartsWith("//") || uri.OriginalString.StartsWith("\\\\"))
+                       else if (uri.OriginalString.StartsWith("//", StringComparison.Ordinal) || uri.OriginalString.StartsWith("\\\\", StringComparison.Ordinal))
                                whatDoIDo = NavigationRequest.WhatToDoWithTheStack.ReplaceIt;
                        else
                                whatDoIDo = NavigationRequest.WhatToDoWithTheStack.PushToIt;
 
                        Uri request = ConvertToStandardFormat(shell, uri);
 
-                       var possibleRouteMatches = GenerateRoutePaths(shell, request, uri);
+                       var possibleRouteMatches = GenerateRoutePaths(shell, request, uri, enableRelativeShellRoutes);
 
 
                        if (possibleRouteMatches.Count == 0)
@@ -85,8 +102,7 @@ namespace Xamarin.Forms
                        var theWinningRoute = possibleRouteMatches[0];
                        RequestDefinition definition =
                                new RequestDefinition(
-                                       ConvertToStandardFormat(shell, new Uri(theWinningRoute.PathFull, UriKind.RelativeOrAbsolute)),
-                                       new Uri(theWinningRoute.PathNoImplicit, UriKind.RelativeOrAbsolute),
+                                       ConvertToStandardFormat(shell, CreateUri(theWinningRoute.PathFull)),
                                        theWinningRoute.Item,
                                        theWinningRoute.Section,
                                        theWinningRoute.Content,
@@ -100,20 +116,25 @@ namespace Xamarin.Forms
                internal static List<RouteRequestBuilder> GenerateRoutePaths(Shell shell, Uri request)
                {
                        request = FormatUri(request);
-                       return GenerateRoutePaths(shell, request, request);
+                       return GenerateRoutePaths(shell, request, request, false);
                }
 
-               internal static List<RouteRequestBuilder> GenerateRoutePaths(Shell shell, Uri request, Uri originalRequest)
+               internal static List<RouteRequestBuilder> GenerateRoutePaths(Shell shell, Uri request, Uri originalRequest, bool enableRelativeShellRoutes)
                {
-                       request = FormatUri(request);
-                       originalRequest = FormatUri(originalRequest);
-
                        var routeKeys = Routing.GetRouteKeys();
                        for (int i = 0; i < routeKeys.Length; i++)
                        {
+                               if (routeKeys[i] == originalRequest.OriginalString)
+                               {
+                                       var builder = new RouteRequestBuilder(routeKeys[i], routeKeys[i], null, new string[] { routeKeys[i] });
+                                       return new List<RouteRequestBuilder> { builder };
+                               }
                                routeKeys[i] = FormatUri(routeKeys[i]);
                        }
 
+                       request = FormatUri(request);
+                       originalRequest = FormatUri(originalRequest);
+
                        List<RouteRequestBuilder> possibleRoutePaths = new List<RouteRequestBuilder>();
                        if (!request.IsAbsoluteUri)
                                request = ConvertToStandardFormat(shell, request);
@@ -121,22 +142,23 @@ namespace Xamarin.Forms
                        string localPath = request.LocalPath;
 
                        bool relativeMatch = false;
-                       if (!originalRequest.IsAbsoluteUri && !originalRequest.OriginalString.StartsWith("/") && !originalRequest.OriginalString.StartsWith("\\"))
+                       if (!originalRequest.IsAbsoluteUri &&
+                               !originalRequest.OriginalString.StartsWith("//", StringComparison.Ordinal))
                                relativeMatch = true;
 
-                       var segments = localPath.Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries);
+                       var segments = localPath.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
 
                        if (!relativeMatch)
                        {
                                for (int i = 0; i < routeKeys.Length; i++)
                                {
                                        var route = routeKeys[i];
-                                       var uri = ConvertToStandardFormat(shell, new Uri(route, UriKind.RelativeOrAbsolute));
-                                       // Todo is this supported?
+                                       var uri = ConvertToStandardFormat(shell, CreateUri(route));
                                        if (uri.Equals(request))
                                        {
-                                               var builder = new RouteRequestBuilder(route, route, null, segments);
-                                               return new List<RouteRequestBuilder> { builder };
+                                               throw new Exception($"Global routes currently cannot be the only page on the stack, so absolute routing to global routes is not supported. For now, just navigate to: {originalRequest.OriginalString.Replace("//","")}");
+                                               //var builder = new RouteRequestBuilder(route, route, null, segments);
+                                               //return new List<RouteRequestBuilder> { builder };
                                        }
                                }
                        }
@@ -160,24 +182,42 @@ namespace Xamarin.Forms
 
                                while (currentLocation.Shell != null)
                                {
-                                       List<RouteRequestBuilder> pureRoutesMatch = new List<RouteRequestBuilder>();
-                                       List<RouteRequestBuilder> pureGlobalRoutesMatch = new List<RouteRequestBuilder>();
+                                       var pureRoutesMatch = new List<RouteRequestBuilder>();
+                                       var pureGlobalRoutesMatch = new List<RouteRequestBuilder>();
 
-                                       SearchPath(currentLocation.LowestChild, null, segments, pureRoutesMatch, 0);
-                                       SearchPath(currentLocation.LowestChild, null, segments, pureGlobalRoutesMatch, 0, ignoreGlobalRoutes: false);
-                                       pureRoutesMatch = GetBestMatches(pureRoutesMatch);
-                                       pureGlobalRoutesMatch = GetBestMatches(pureGlobalRoutesMatch);
+                                       //currently relative routes to shell routes isn't supported as we aren't creating navigation stacks
+                                       if (enableRelativeShellRoutes)
+                                       {
+                                               SearchPath(currentLocation.LowestChild, null, segments, pureRoutesMatch, 0);
+                                               ExpandOutGlobalRoutes(pureRoutesMatch, routeKeys);
+                                               pureRoutesMatch = GetBestMatches(pureRoutesMatch);
+                                               if (pureRoutesMatch.Count > 0)
+                                               {
+                                                       return pureRoutesMatch;
+                                               }
+                                       }
 
-                                       if (pureRoutesMatch.Count > 0)
-                                               return pureRoutesMatch;
 
+                                       SearchPath(currentLocation.LowestChild, null, segments, pureGlobalRoutesMatch, 0, ignoreGlobalRoutes: false);
+                                       ExpandOutGlobalRoutes(pureGlobalRoutesMatch, routeKeys);
+                                       pureGlobalRoutesMatch = GetBestMatches(pureGlobalRoutesMatch);
                                        if (pureGlobalRoutesMatch.Count > 0)
+                                       {
+                                               // currently relative routes to shell routes isn't supported as we aren't creating navigation stacks
+                                               // So right now we will just throw an exception so that once this is implemented
+                                               // GotoAsync doesn't start acting inconsistently and all of a suddent starts creating routes
+                                               if (!enableRelativeShellRoutes && pureGlobalRoutesMatch[0].SegmentsMatched.Count > 0)
+                                               {
+                                                       throw new Exception($"Relative routing to shell elements is currently not supported. Try prefixing your uri with ///: ///{originalRequest}");
+                                               }
+
                                                return pureGlobalRoutesMatch;
+                                       }
 
                                        currentLocation.Pop();
                                }
 
-                               string searchPath = String.Join("/", segments);
+                               string searchPath = String.Join(_pathSeparator, segments);
 
                                if (routeKeys.Contains(searchPath))
                                {
@@ -209,45 +249,50 @@ namespace Xamarin.Forms
                                        return bestMatches;
 
                                bestMatches.Clear();
-                               foreach (var possibleRoutePath in possibleRoutePaths)
+                               ExpandOutGlobalRoutes(possibleRoutePaths, routeKeys);
+                       }
+
+                       possibleRoutePaths = GetBestMatches(possibleRoutePaths);
+                       return possibleRoutePaths;
+               }
+
+               internal static void ExpandOutGlobalRoutes(List<RouteRequestBuilder> possibleRoutePaths, string[] routeKeys)
+               {
+                       foreach (var possibleRoutePath in possibleRoutePaths)
+                       {
+                               while (routeKeys.Contains(possibleRoutePath.NextSegment) || routeKeys.Contains(possibleRoutePath.RemainingPath))
                                {
-                                       while (routeKeys.Contains(possibleRoutePath.NextSegment) || routeKeys.Contains(possibleRoutePath.RemainingPath))
+                                       if (routeKeys.Contains(possibleRoutePath.NextSegment))
+                                               possibleRoutePath.AddGlobalRoute(possibleRoutePath.NextSegment, possibleRoutePath.NextSegment);
+                                       else
+                                               possibleRoutePath.AddGlobalRoute(possibleRoutePath.RemainingPath, possibleRoutePath.RemainingPath);
+                               }
+
+                               while (!possibleRoutePath.IsFullMatch)
+                               {
+                                       NodeLocation nodeLocation = new NodeLocation();
+                                       nodeLocation.SetNode(possibleRoutePath.LowestChild);
+                                       List<RouteRequestBuilder> pureGlobalRoutesMatch = new List<RouteRequestBuilder>();
+                                       while (nodeLocation.Shell != null && pureGlobalRoutesMatch.Count == 0)
                                        {
-                                               if(routeKeys.Contains(possibleRoutePath.NextSegment))
-                                                       possibleRoutePath.AddGlobalRoute(possibleRoutePath.NextSegment, possibleRoutePath.NextSegment);
-                                               else
-                                                       possibleRoutePath.AddGlobalRoute(possibleRoutePath.RemainingPath, possibleRoutePath.RemainingPath);
+                                               SearchPath(nodeLocation.LowestChild, null, possibleRoutePath.RemainingSegments, pureGlobalRoutesMatch, 0, ignoreGlobalRoutes: false);
+                                               nodeLocation.Pop();
                                        }
 
-                                       while (!possibleRoutePath.IsFullMatch)
+                                       // nothing found or too many things found
+                                       if (pureGlobalRoutesMatch.Count != 1 || pureGlobalRoutesMatch[0].GlobalRouteMatches.Count == 0)
                                        {
-                                               NodeLocation nodeLocation = new NodeLocation();
-                                               nodeLocation.SetNode(possibleRoutePath.LowestChild);
-                                               List<RouteRequestBuilder> pureGlobalRoutesMatch = new List<RouteRequestBuilder>();
-                                               while (nodeLocation.Shell != null && pureGlobalRoutesMatch.Count == 0)
-                                               {
-                                                       SearchPath(nodeLocation.LowestChild, null, possibleRoutePath.RemainingSegments, pureGlobalRoutesMatch, 0, ignoreGlobalRoutes: false);
-                                                       nodeLocation.Pop();
-                                               }
-
-                                               // nothing found or too many things found
-                                               if (pureGlobalRoutesMatch.Count != 1)
-                                               {
-                                                       break;
-                                               }
+                                               break;
+                                       }
 
 
-                                               for (var i = 0; i < pureGlobalRoutesMatch[0].GlobalRouteMatches.Count; i++)
-                                               {
-                                                       var match = pureGlobalRoutesMatch[0];
-                                                       possibleRoutePath.AddGlobalRoute(match.GlobalRouteMatches[i], match.SegmentsMatched[i]);
-                                               }
+                                       for (var i = 0; i < pureGlobalRoutesMatch[0].GlobalRouteMatches.Count; i++)
+                                       {
+                                               var match = pureGlobalRoutesMatch[0];
+                                               possibleRoutePath.AddGlobalRoute(match.GlobalRouteMatches[i], match.SegmentsMatched[i]);
                                        }
                                }
                        }
-
-                       possibleRoutePaths = GetBestMatches(possibleRoutePaths);
-                       return possibleRoutePaths;
                }
 
                internal static List<RouteRequestBuilder> GetBestMatches(List<RouteRequestBuilder> possibleRoutePaths)
@@ -341,7 +386,7 @@ namespace Xamarin.Forms
                                if (Content != null && !Routing.IsImplicit(Content))
                                        paths.Add(Content.Route);
 
-                               string uri = String.Join("/", paths);
+                               string uri = String.Join(_pathSeparator, paths);
                                return new Uri($"{Shell.RouteScheme}://{uri}");
                        }
 
@@ -485,10 +530,10 @@ namespace Xamarin.Forms
                        for (var i = 0; i < keys.Length; i++)
                        {
                                var key = FormatUri(keys[i]);
-                               if (key.StartsWith("/") && !(node is Shell))
+                               if (key.StartsWith(_pathSeparator, StringComparison.Ordinal) && !(node is Shell))
                                        continue;
 
-                               var segments = key.Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries);
+                               var segments = key.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
 
                                if (segments[0] == route)
                                {
@@ -511,7 +556,7 @@ namespace Xamarin.Forms
                        {
                                get
                                {
-                                       var segments = _path.Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries).ToList().Skip(1).ToList();
+                                       var segments = _path.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList().Skip(1).ToList();
 
                                        if (segments.Count == 0)
                                                return new object[0];
@@ -526,7 +571,7 @@ namespace Xamarin.Forms
                        {
                                get
                                {
-                                       var segments = _path.Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries);
+                                       var segments = _path.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
 
                                        if (segments.Length == 0)
                                                return string.Empty;
@@ -539,7 +584,7 @@ namespace Xamarin.Forms
                        {
                                get
                                {
-                                       var segments = _path.Split(_pathSeparator, StringSplitOptions.RemoveEmptyEntries).ToList().Skip(1).ToList();
+                                       var segments = _path.Split(_pathSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList().Skip(1).ToList();
 
                                        if (segments.Count == 0)
                                                return true;
@@ -669,7 +714,7 @@ namespace Xamarin.Forms
                                if (nextMatch >= _allSegments.Length)
                                        return null;
 
-                               return Routing.FormatRoute(String.Join("/", _allSegments.Skip(nextMatch)));
+                               return Routing.FormatRoute(String.Join(_uriSeparator, _allSegments.Skip(nextMatch)));
                        }
                }
                public string[] RemainingSegments
@@ -686,7 +731,7 @@ namespace Xamarin.Forms
 
                string MakeUriString(List<string> segments)
                {
-                       if (segments[0].StartsWith("/") || segments[0].StartsWith("\\"))
+                       if (segments[0].StartsWith(_uriSeparator, StringComparison.Ordinal) || segments[0].StartsWith("\\", StringComparison.Ordinal))
                                return String.Join(_uriSeparator, segments);
 
                        return $"//{String.Join(_uriSeparator, segments)}";
@@ -704,7 +749,7 @@ namespace Xamarin.Forms
 
 
        [DebuggerDisplay("RequestDefinition = {Request}, StackRequest = {StackRequest}")]
-       public class NavigationRequest
+       internal class NavigationRequest
        {
                public enum WhatToDoWithTheStack
                {
@@ -728,25 +773,23 @@ namespace Xamarin.Forms
 
 
        [DebuggerDisplay("Full = {FullUri}, Short = {ShortUri}")]
-       public class RequestDefinition
+       internal class RequestDefinition
        {
-               public RequestDefinition(Uri fullUri, Uri shortUri, ShellItem item, ShellSection section, ShellContent content, List<string> globalRoutes)
+               public RequestDefinition(Uri fullUri, ShellItem item, ShellSection section, ShellContent content, List<string> globalRoutes)
                {
                        FullUri = fullUri;
-                       ShortUri = shortUri;
                        Item = item;
                        Section = section;
                        Content = content;
                        GlobalRoutes = globalRoutes;
                }
 
-               public RequestDefinition(string fullUri, string shortUri, ShellItem item, ShellSection section, ShellContent content, List<string> globalRoutes) :
-                       this(new Uri(fullUri, UriKind.Absolute), new Uri(shortUri, UriKind.Absolute), item, section, content, globalRoutes)
+               public RequestDefinition(string fullUri, ShellItem item, ShellSection section, ShellContent content, List<string> globalRoutes) :
+                       this(new Uri(fullUri, UriKind.Absolute), item, section, content, globalRoutes)
                {
                }
 
                public Uri FullUri { get; }
-               public Uri ShortUri { get; }
                public ShellItem Item { get; }
                public ShellSection Section { get; }
                public ShellContent Content { get; }