1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.chrome.browser.omnibox; 6 7 import android.net.Uri; 8 import android.text.Spanned; 9 import android.text.TextUtils; 10 11 import androidx.annotation.Nullable; 12 13 import org.chromium.base.CollectionUtil; 14 import org.chromium.components.embedder_support.util.UrlConstants; 15 import org.chromium.content_public.common.ContentUrlConstants; 16 17 import java.util.HashSet; 18 19 /** 20 * Encapsulates all data that is necessary for the URL bar to display its contents. 21 */ 22 public class UrlBarData { 23 /** 24 * The URL schemes that should be displayed complete with path. 25 */ 26 public static final HashSet<String> UNSUPPORTED_SCHEMES_TO_SPLIT = 27 CollectionUtil.newHashSet(ContentUrlConstants.ABOUT_SCHEME, UrlConstants.DATA_SCHEME, 28 UrlConstants.FILE_SCHEME, UrlConstants.FTP_SCHEME, UrlConstants.INLINE_SCHEME, 29 UrlConstants.JAVASCRIPT_SCHEME, UrlConstants.CHROME_SCHEME); 30 /** 31 * URI schemes that ContentView can handle. 32 * 33 * Copied from UrlUtilities.java. UrlUtilities uses a URI to check for schemes, which 34 * is more strict than Uri and causes the path stripping to fail. 35 * 36 * The following additions have been made: "chrome", "ftp". 37 */ 38 private static final HashSet<String> ACCEPTED_SCHEMES = CollectionUtil.newHashSet( 39 ContentUrlConstants.ABOUT_SCHEME, UrlConstants.DATA_SCHEME, UrlConstants.FILE_SCHEME, 40 UrlConstants.FTP_SCHEME, UrlConstants.HTTP_SCHEME, UrlConstants.HTTPS_SCHEME, 41 UrlConstants.INLINE_SCHEME, UrlConstants.JAVASCRIPT_SCHEME, UrlConstants.CHROME_SCHEME); 42 // Unicode "Left-To-Right Mark" (LRM) character. 43 private static final char LRM = '\u200E'; 44 45 /** 46 * Represents an empty URL bar. 47 */ 48 public static final UrlBarData EMPTY = forNonUrlText(""); 49 forUrl(String url)50 public static UrlBarData forUrl(String url) { 51 return forUrlAndText(url, url, null); 52 } 53 forNonUrlText(String displayText)54 public static UrlBarData forNonUrlText(String displayText) { 55 return create(null, displayText, 0, 0, null); 56 } 57 forUrlAndText(String url, String displayText)58 public static UrlBarData forUrlAndText(String url, String displayText) { 59 return forUrlAndText(url, displayText, null); 60 } 61 forUrlAndText( String url, CharSequence displayText, @Nullable String editingText)62 public static UrlBarData forUrlAndText( 63 String url, CharSequence displayText, @Nullable String editingText) { 64 int pathSearchOffset = 0; 65 String displayTextStr = displayText.toString(); 66 String scheme = Uri.parse(displayTextStr).getScheme(); 67 68 if (!TextUtils.isEmpty(scheme)) { 69 if (UNSUPPORTED_SCHEMES_TO_SPLIT.contains(scheme)) { 70 return create(url, displayText, 0, displayText.length(), editingText); 71 } 72 if (UrlConstants.BLOB_SCHEME.equals(scheme)) { 73 int innerSchemeSearchOffset = 74 findFirstIndexAfterSchemeSeparator(displayText, scheme.length()); 75 Uri innerUri = Uri.parse(displayTextStr.substring(innerSchemeSearchOffset)); 76 String innerScheme = innerUri.getScheme(); 77 // Substitute the scheme to allow for proper display of end of inner origin. 78 if (!TextUtils.isEmpty(innerScheme)) { 79 scheme = innerScheme; 80 } 81 } 82 if (ACCEPTED_SCHEMES.contains(scheme)) { 83 pathSearchOffset = findFirstIndexAfterSchemeSeparator( 84 displayText, displayTextStr.indexOf(scheme) + scheme.length()); 85 } 86 } 87 int pathOffset = -1; 88 if (pathSearchOffset < displayText.length()) { 89 pathOffset = displayTextStr.indexOf('/', pathSearchOffset); 90 } 91 if (pathOffset == -1) return create(url, displayText, 0, displayText.length(), editingText); 92 93 // If the '/' is the last character and the beginning of the path, then just drop 94 // the path entirely. 95 if (pathOffset == displayText.length() - 1) { 96 return create( 97 url, displayTextStr.subSequence(0, pathOffset), 0, pathOffset, editingText); 98 } 99 100 return create(url, displayText, 0, pathOffset, editingText); 101 } 102 create(@ullable String url, CharSequence displayText, int originStartIndex, int originEndIndex, @Nullable String editingText)103 public static UrlBarData create(@Nullable String url, CharSequence displayText, 104 int originStartIndex, int originEndIndex, @Nullable String editingText) { 105 return new UrlBarData(url, displayText, originStartIndex, originEndIndex, editingText); 106 } 107 findFirstIndexAfterSchemeSeparator( CharSequence input, int searchStartIndex)108 private static int findFirstIndexAfterSchemeSeparator( 109 CharSequence input, int searchStartIndex) { 110 for (int index = searchStartIndex; index < input.length(); index++) { 111 char c = input.charAt(index); 112 if (c != ':' && c != '/') return index; 113 } 114 return input.length(); 115 } 116 117 /** 118 * The canonical URL that is shown in the URL bar, or null if it currently does not correspond 119 * to a URL (for example, showing suggestion text). 120 */ 121 public final @Nullable String url; 122 123 /** 124 * The text that should be shown in the URL bar. This can be a {@link Spanned} that contains 125 * formatting to highlight parts of the display text. 126 */ 127 public final CharSequence displayText; 128 129 /** 130 * The text that should replace the display text when editing the contents of the URL bar, 131 * or null to use the {@link #displayText} when editing. 132 */ 133 public final @Nullable String editingText; 134 135 /** 136 * The character index in {@link #displayText} where the origin starts. This is required to 137 * ensure that the end of the origin is not scrolled out of view for long hostnames. 138 */ 139 public final int originStartIndex; 140 141 /** 142 * The character index in {@link #displayText} where the origin ends. This is required to 143 * ensure that the end of the origin is not scrolled out of view for long hostnames. 144 */ 145 public final int originEndIndex; 146 147 /** 148 * @return The text for editing, falling back to the display text if the former is null. 149 */ getEditingOrDisplayText()150 public CharSequence getEditingOrDisplayText() { 151 return editingText != null ? editingText : displayText; 152 } 153 UrlBarData(@ullable String url, CharSequence displayText, int originStartIndex, int originEndIndex, @Nullable String editingText)154 private UrlBarData(@Nullable String url, CharSequence displayText, int originStartIndex, 155 int originEndIndex, @Nullable String editingText) { 156 this.url = url; 157 this.displayText = displayText; 158 this.originStartIndex = originStartIndex; 159 this.originEndIndex = originEndIndex; 160 this.editingText = editingText; 161 } 162 } 163