mUrlDirectionListener;
++
++ /**
++ * The gesture detector is used to detect long presses. Long presses require special treatment
++ * because the URL bar has custom touch event handling. See: {@link #onTouchEvent}.
++ */
++ private final GestureDetector mGestureDetector;
++
++ private final KeyboardHideHelper mKeyboardHideHelper;
++
++ private boolean mFocused;
++ private boolean mSuppressingTouchMoveEventsForThisTouch;
++ private MotionEvent mSuppressedTouchDownEvent;
++ private boolean mAllowFocus = true;
++
++ private boolean mPendingScroll;
++ private int mPreviousWidth;
++
++ @ScrollType
++ private int mPreviousScrollType;
++ private String mPreviousScrollText;
++ private int mPreviousScrollViewWidth;
++ private int mPreviousScrollResultXPosition;
++ private float mPreviousScrollFontSize;
++ private boolean mPreviousScrollWasRtl;
++
++ // Used as a hint to indicate the text may contain an ellipsize span. This will be true if an
++ // ellispize span was applied the last time the text changed. A true value here does not
++ // guarantee that the text does contain the span currently as newly set text may have cleared
++ // this (and it the value will only be recalculated after the text has been changed).
++ private boolean mDidEllipsizeTextHint;
++
++ /** A cached point for getting this view's location in the window. */
++ private final int[] mCachedLocation = new int[2];
++
++ /** The location of this view on the last ACTION_DOWN event. */
++ private float mDownEventViewTop;
++
++ /**
++ * The character index in the displayed text where the origin ends. This is required to
++ * ensure that the end of the origin is not scrolled out of view for long hostnames.
++ */
++ private int mOriginEndIndex;
++
++ @ScrollType
++ private int mScrollType;
++
++ /** What scrolling action should be taken after the URL bar text changes. **/
++ @IntDef({ScrollType.NO_SCROLL, ScrollType.SCROLL_TO_TLD, ScrollType.SCROLL_TO_BEGINNING})
++ @Retention(RetentionPolicy.SOURCE)
++ public @interface ScrollType {
++ int NO_SCROLL = 0;
++ int SCROLL_TO_TLD = 1;
++ int SCROLL_TO_BEGINNING = 2;
++ }
++
++ /**
++ * An optional string to use with AccessibilityNodeInfo to report text content.
++ * This is particularly important for auto-fill applications, such as password managers, that
++ * rely on AccessibilityNodeInfo data to apply related form-fill data.
++ */
++ private CharSequence mTextForAutofillServices;
++ protected boolean mRequestingAutofillStructure;
++
++ /**
++ * Delegate used to communicate with the content side and the parent layout.
++ */
++ public interface UrlBarDelegate {
++ /**
++ * @return The view to be focused on a backward focus traversal.
++ */
++ @Nullable
++ View getViewForUrlBackFocus();
++
++ /**
++ * @return Whether the keyboard should be allowed to learn from the user input.
++ */
++ boolean allowKeyboardLearning();
++
++ /**
++ * Called to notify that back key has been pressed while the URL bar has focus.
++ */
++ void backKeyPressed();
++
++ /**
++ * Called to notify that a tap or long press gesture has been detected.
++ * @param isLongPress Whether or not is a long press gesture.
++ */
++ void gestureDetected(boolean isLongPress);
++ }
++
++ /** Provides updates about the URL text changes. */
++ public interface UrlTextChangeListener {
++ /**
++ * Called when the text state has changed.
++ * @param textWithoutAutocomplete The url bar text without autocompletion.
++ * @param textWithAutocomplete The url bar text with autocompletion.
++ */
++ // TODO(crbug.com/1003080): Consider splitting these into two different callbacks.
++ void onTextChanged(String textWithoutAutocomplete, String textWithAutocomplete);
++ }
++
++ /** Delegate that provides the additional functionality to the textual context menus. */
++ interface UrlBarTextContextMenuDelegate {
++ /** @return The text to be pasted into the UrlBar. */
++ @NonNull
++ String getTextToPaste();
++
++ /**
++ * Gets potential replacement text to be used instead of the current selected text for
++ * cut/copy actions. If null is returned, the existing text will be cut or copied.
++ *
++ * @param currentText The current displayed text.
++ * @param selectionStart The selection start in the display text.
++ * @param selectionEnd The selection end in the display text.
++ * @return The text to be cut/copied instead of the currently selected text.
++ */
++ @Nullable
++ String getReplacementCutCopyText(String currentText, int selectionStart, int selectionEnd);
++ }
++
++ public UrlBar(Context context, AttributeSet attrs) {
++ super(context, attrs);
++ mUrlDirection = LAYOUT_DIRECTION_LOCALE;
++
++ // The URL Bar is derived from an text edit class, and as such is focusable by
++ // default. This means that if it is created before the first draw of the UI it
++ // will (as the only focusable element of the UI) get focus on the first draw.
++ // We react to this by greying out the tab area and bringing up the keyboard,
++ // which we don't want to do at startup. Prevent this by disabling focus until
++ // the first draw.
++ setFocusable(false);
++ setFocusableInTouchMode(false);
++
++ // The HTC Sense IME will attempt to autocomplete words in the Omnibox when Prediction is
++ // enabled. We want to disable this feature and rely on the Omnibox's implementation.
++ // Their IME does not respect ~TYPE_TEXT_FLAG_AUTO_COMPLETE nor any of the other InputType
++ // options I tried, but setting the filter variation prevents it. Sadly, it also removes
++ // the .com button, but the prediction was buggy as it would autocomplete words even when
++ // typing at the beginning of the omnibox text when other content was present (messing up
++ // what was previously there). See bug: http://b/issue?id=6200071
++ String defaultIme = Settings.Secure.getString(
++ getContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD);
++ if (defaultIme != null && defaultIme.contains("com.htc.android.htcime")) {
++ setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_FILTER);
++ }
++
++ mGestureDetector =
++ new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
++ @Override
++ public void onLongPress(MotionEvent e) {
++ if (mUrlBarDelegate == null) return;
++ mUrlBarDelegate.gestureDetected(true);
++ performLongClick();
++ }
++
++ @Override
++ public boolean onSingleTapUp(MotionEvent e) {
++ if (mUrlBarDelegate == null) return true;
++ requestFocus();
++ mUrlBarDelegate.gestureDetected(false);
++ return true;
++ }
++ }, ThreadUtils.getUiThreadHandler());
++ mGestureDetector.setOnDoubleTapListener(null);
++ mKeyboardHideHelper = new KeyboardHideHelper(this, new Runnable() {
++ @Override
++ public void run() {
++ if (mUrlBarDelegate != null) mUrlBarDelegate.backKeyPressed();
++ }
++ });
++
++ ApiCompatibilityUtils.disableSmartSelectionTextClassifier(this);
++ }
++
++ public void destroy() {
++ setAllowFocus(false);
++ mUrlBarDelegate = null;
++ setOnFocusChangeListener(null);
++ mTextContextMenuDelegate = null;
++ mUrlTextChangeListener = null;
++ mTextChangedListener = null;
++ }
++
++ /**
++ * Initialize the delegate that allows interaction with the Window.
++ */
++ public void setWindowDelegate(WindowDelegate windowDelegate) {
++ mKeyboardHideHelper.setWindowDelegate(windowDelegate);
++ }
++
++ /**
++ * Set the delegate to be used for text context menu actions.
++ */
++ public void setTextContextMenuDelegate(UrlBarTextContextMenuDelegate delegate) {
++ mTextContextMenuDelegate = delegate;
++ }
++
++ @Override
++ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
++ if (KeyEvent.KEYCODE_BACK == keyCode && event.getAction() == KeyEvent.ACTION_UP) {
++ mKeyboardHideHelper.monitorForKeyboardHidden();
++ }
++ return super.onKeyPreIme(keyCode, event);
++ }
++
++ /**
++ * See {@link AutocompleteEditText#setIgnoreTextChangesForAutocomplete(boolean)}.
++ *
++ * {@link #setDelegate(UrlBarDelegate)} must be called with a non-null instance prior to
++ * enabling autocomplete.
++ */
++ @Override
++ public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete) {
++ assert mUrlBarDelegate != null;
++ super.setIgnoreTextChangesForAutocomplete(ignoreAutocomplete);
++ }
++
++ @Override
++ protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
++ mFocused = focused;
++ super.onFocusChanged(focused, direction, previouslyFocusedRect);
++
++ if (focused) {
++ mPendingScroll = false;
++ }
++ fixupTextDirection();
++ }
++
++ /**
++ * Sets whether this {@link UrlBar} should be focusable.
++ */
++ public void setAllowFocus(boolean allowFocus) {
++ mAllowFocus = allowFocus;
++ setFocusable(allowFocus);
++ setFocusableInTouchMode(allowFocus);
++ }
++
++ /**
++ * Sets the {@link UrlBar}'s text direction based on focus and contents.
++ *
++ * Should be called whenever focus or text contents change.
++ */
++ private void fixupTextDirection() {
++ // When unfocused, force left-to-right rendering at the paragraph level (which is desired
++ // for URLs). Right-to-left runs are still rendered RTL, but will not flip the whole URL
++ // around. This is consistent with OmniboxViewViews on desktop. When focused, render text
++ // normally (to allow users to make non-URL searches and to avoid showing Android's split
++ // insertion point when an RTL user enters RTL text). Also render text normally when the
++ // text field is empty (because then it displays an instruction that is not a URL).
++ if (mFocused || length() == 0) {
++ setTextDirection(TEXT_DIRECTION_INHERIT);
++ } else {
++ setTextDirection(TEXT_DIRECTION_LTR);
++ }
++ // Always align to the same as the paragraph direction (LTR = left, RTL = right).
++ setTextAlignment(TEXT_ALIGNMENT_TEXT_START);
++ }
++
++ @Override
++ public void onWindowFocusChanged(boolean hasWindowFocus) {
++ super.onWindowFocusChanged(hasWindowFocus);
++ if (DEBUG) Log.i(TAG, "onWindowFocusChanged: " + hasWindowFocus);
++ if (hasWindowFocus) {
++ if (isFocused()) {
++ // Without the call to post(..), the keyboard was not getting shown when the
++ // window regained focus despite this being the final call in the view system
++ // flow.
++ post(new Runnable() {
++ @Override
++ public void run() {
++ KeyboardVisibilityDelegate.getInstance().showKeyboard(UrlBar.this);
++ }
++ });
++ }
++ }
++ }
++
++ @Override
++ public View focusSearch(int direction) {
++ if (mUrlBarDelegate != null && direction == View.FOCUS_BACKWARD
++ && mUrlBarDelegate.getViewForUrlBackFocus() != null) {
++ return mUrlBarDelegate.getViewForUrlBackFocus();
++ } else {
++ return super.focusSearch(direction);
++ }
++ }
++
++ @Override
++ protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
++ super.onTextChanged(text, start, lengthBefore, lengthAfter);
++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
++ // Due to crbug.com/1103555, Autofill had to be disabled on the UrlBar to work around
++ // an issue on Android Q+. With Autofill disabled, the Autofill compat mode no longer
++ // learns of changes to the UrlBar, which prevents it from cancelling the session if
++ // the domain changes. We restore this behavior by mimicking the relevant part of
++ // TextView.notifyListeningManagersAfterTextChanged().
++ // https://cs.android.com/android/platform/superproject/+/5d123b67756dffcfdebdb936ab2de2b29c799321:frameworks/base/core/java/android/widget/TextView.java;l=10618;drc=master;bpv=0
++ ApiHelperForO.notifyValueChangedForAutofill(this);
++ }
++ }
++
++ @Override
++ public boolean onTouchEvent(MotionEvent event) {
++ // This method contains special logic to enable long presses to be handled correctly.
++
++ // One piece of the logic is to suppress all ACTION_DOWN events received while the UrlBar is
++ // not focused, and only pass them to super.onTouchEvent() if it turns out we're about to
++ // perform a long press. Long pressing will not behave properly without sending this event,
++ // but if we always send it immediately, it will cause the keyboard to show immediately,
++ // whereas we want to wait to show it until after the URL focus animation finishes, to avoid
++ // performance issues on slow devices.
++
++ // The other piece of the logic is to suppress ACTION_MOVE events received after an
++ // ACTION_DOWN received while the UrlBar is not focused. This is because the UrlBar moves to
++ // the side as it's focusing, and a finger held still on the screen would therefore be
++ // interpreted as a drag selection.
++
++ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
++ getLocationInWindow(mCachedLocation);
++ mDownEventViewTop = mCachedLocation[1];
++ mSuppressingTouchMoveEventsForThisTouch = !mFocused;
++ }
++
++ if (!mFocused) {
++ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
++ mSuppressedTouchDownEvent = MotionEvent.obtain(event);
++ }
++ mGestureDetector.onTouchEvent(event);
++ return true;
++ }
++
++ if (event.getActionMasked() == MotionEvent.ACTION_UP
++ || event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
++ // Minor optimization to avoid unnecessarily holding onto a MotionEvent after the touch
++ // finishes.
++ mSuppressedTouchDownEvent = null;
++ }
++
++ if (mSuppressingTouchMoveEventsForThisTouch
++ && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
++ return true;
++ }
++
++ try {
++ return super.onTouchEvent(event);
++ } catch (NullPointerException e) {
++ // Working around a platform bug (b/25562038) that was fixed in N that can throw an
++ // exception during text selection. We just swallow the exception. The outcome is that
++ // the text selection handle doesn't show.
++
++ // If this happens on N or later, there's a different issue here that we might want to
++ // know about.
++ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) throw e;
++
++ Log.w(TAG, "Ignoring NPE in UrlBar#onTouchEvent.", e);
++ return true;
++ } catch (IndexOutOfBoundsException e) {
++ // Work around crash of unknown origin (https://crbug.com/837419).
++ Log.w(TAG, "Ignoring IndexOutOfBoundsException in UrlBar#onTouchEvent.", e);
++ return true;
++ }
++ }
++
++ @Override
++ public boolean performLongClick() {
++ if (!shouldPerformLongClick()) return false;
++
++ releaseSuppressedTouchDownEvent();
++ return super.performLongClick();
++ }
++
++ /**
++ * @return Whether or not a long click should be performed.
++ */
++ private boolean shouldPerformLongClick() {
++ getLocationInWindow(mCachedLocation);
++
++ // If the view moved between the last down event, block the long-press.
++ return mDownEventViewTop == mCachedLocation[1];
++ }
++
++ private void releaseSuppressedTouchDownEvent() {
++ if (mSuppressedTouchDownEvent != null) {
++ super.onTouchEvent(mSuppressedTouchDownEvent);
++ mSuppressedTouchDownEvent = null;
++ }
++ }
++
++ @Override
++ public void onDraw(Canvas canvas) {
++ super.onDraw(canvas);
++
++ if (!mFirstDrawComplete) {
++ mFirstDrawComplete = true;
++
++ // We have now avoided the first draw problem (see the comment in
++ // the constructor) so we want to make the URL bar focusable so that
++ // touches etc. activate it.
++ setFocusable(mAllowFocus);
++ setFocusableInTouchMode(mAllowFocus);
++ }
++
++ // Notify listeners if the URL's direction has changed.
++ updateUrlDirection();
++ }
++
++ /**
++ * If the direction of the URL has changed, update mUrlDirection and notify the
++ * UrlDirectionListeners.
++ */
++ private void updateUrlDirection() {
++ Layout layout = getLayout();
++ if (layout == null) return;
++
++ int urlDirection;
++ if (length() == 0) {
++ urlDirection = LAYOUT_DIRECTION_LOCALE;
++ } else if (layout.getParagraphDirection(0) == Layout.DIR_LEFT_TO_RIGHT) {
++ urlDirection = LAYOUT_DIRECTION_LTR;
++ } else {
++ urlDirection = LAYOUT_DIRECTION_RTL;
++ }
++
++ if (urlDirection != mUrlDirection) {
++ mUrlDirection = urlDirection;
++ if (mUrlDirectionListener != null) {
++ mUrlDirectionListener.onResult(urlDirection);
++ }
++
++ // Ensure the display text is visible after updating the URL direction.
++ scrollDisplayText();
++ }
++ }
++
++ /**
++ * @return The text direction of the URL, e.g. LAYOUT_DIRECTION_LTR.
++ */
++ public int getUrlDirection() {
++ return mUrlDirection;
++ }
++
++ /**
++ * Sets the listener for changes in the url bar's layout direction. Also calls
++ * onUrlDirectionChanged() immediately on the listener.
++ *
++ * @param listener The UrlDirectionListener to receive callbacks when the url direction changes,
++ * or null to unregister any previously registered listener.
++ */
++ public void setUrlDirectionListener(Callback listener) {
++ mUrlDirectionListener = listener;
++ if (mUrlDirectionListener != null) {
++ mUrlDirectionListener.onResult(mUrlDirection);
++ }
++ }
++
++ /**
++ * Set the url delegate to handle communication from the {@link UrlBar} to the rest of the UI.
++ * @param delegate The {@link UrlBarDelegate} to be used.
++ */
++ public void setDelegate(UrlBarDelegate delegate) {
++ mUrlBarDelegate = delegate;
++ }
++
++ /**
++ * Set the listener to be notified when the URL text has changed.
++ * @param listener The listener to be notified.
++ */
++ public void setUrlTextChangeListener(UrlTextChangeListener listener) {
++ mUrlTextChangeListener = listener;
++ }
++
++ /**
++ * Set the listener to be notified when the view's text has changed.
++ * @param textChangedListener The listener to be notified.
++ */
++ public void setTextChangedListener(TextWatcher textChangedListener) {
++ if (ObjectsCompat.equals(mTextChangedListener, textChangedListener)) {
++ return;
++ } else if (mTextChangedListener != null) {
++ removeTextChangedListener(mTextChangedListener);
++ }
++
++ mTextChangedListener = textChangedListener;
++ addTextChangedListener(mTextChangedListener);
++ }
++
++ /**
++ * Set the text to report to Autofill services upon call to onProvideAutofillStructure.
++ */
++ public void setTextForAutofillServices(CharSequence text) {
++ mTextForAutofillServices = text;
++ }
++
++ @Override
++ public boolean onTextContextMenuItem(int id) {
++ if (mTextContextMenuDelegate == null) return super.onTextContextMenuItem(id);
++
++ if (id == android.R.id.paste) {
++ String pasteString = mTextContextMenuDelegate.getTextToPaste();
++ if (pasteString != null) {
++ int min = 0;
++ int max = getText().length();
++
++ if (isFocused()) {
++ final int selStart = getSelectionStart();
++ final int selEnd = getSelectionEnd();
++
++ min = Math.max(0, Math.min(selStart, selEnd));
++ max = Math.max(0, Math.max(selStart, selEnd));
++ }
++
++ Selection.setSelection(getText(), max);
++ getText().replace(min, max, pasteString);
++ onPaste();
++ }
++ return true;
++ }
++
++ if ((id == android.R.id.cut || id == android.R.id.copy)) {
++ if (id == android.R.id.cut) {
++ RecordUserAction.record("Omnibox.LongPress.Cut");
++ } else {
++ RecordUserAction.record("Omnibox.LongPress.Copy");
++ }
++ String currentText = getText().toString();
++ String replacementCutCopyText = mTextContextMenuDelegate.getReplacementCutCopyText(
++ currentText, getSelectionStart(), getSelectionEnd());
++ if (replacementCutCopyText == null) return super.onTextContextMenuItem(id);
++
++ setIgnoreTextChangesForAutocomplete(true);
++ setText(replacementCutCopyText);
++ setSelection(0, replacementCutCopyText.length());
++ setIgnoreTextChangesForAutocomplete(false);
++
++ boolean retVal = super.onTextContextMenuItem(id);
++
++ if (TextUtils.equals(getText(), replacementCutCopyText)) {
++ // Restore the old text if the operation did modify the text.
++ setIgnoreTextChangesForAutocomplete(true);
++ setText(currentText);
++
++ // Move the cursor to the end.
++ setSelection(getText().length());
++ setIgnoreTextChangesForAutocomplete(false);
++ }
++
++ return retVal;
++ }
++
++ if (id == android.R.id.shareText) {
++ RecordUserAction.record("Omnibox.LongPress.Share");
++ }
++
++ return super.onTextContextMenuItem(id);
++ }
++
++ /**
++ * Specified how text should be scrolled within the UrlBar.
++ *
++ * @param scrollType What type of scroll should be applied to the text.
++ * @param scrollToIndex The index that should be scrolled to, which only applies to
++ * {@link ScrollType#SCROLL_TO_TLD}.
++ */
++ public void setScrollState(@ScrollType int scrollType, int scrollToIndex) {
++ if (scrollType == ScrollType.SCROLL_TO_TLD) {
++ mOriginEndIndex = scrollToIndex;
++ } else {
++ mOriginEndIndex = 0;
++ }
++ mScrollType = scrollType;
++ scrollDisplayText();
++ }
++
++ /**
++ * Scrolls the omnibox text to a position determined by the current scroll type.
++ *
++ * @see #setScrollState(int, int)
++ */
++ private void scrollDisplayText() {
++ if (isLayoutRequested()) {
++ mPendingScroll = mScrollType != ScrollType.NO_SCROLL;
++ return;
++ }
++ scrollDisplayTextInternal(mScrollType);
++ }
++
++ /**
++ * Scrolls the omnibox text to the position specified, based on the {@link ScrollType}.
++ *
++ * @param scrollType What type of scroll to perform.
++ * SCROLL_TO_TLD: Scrolls the omnibox text to bring the TLD into view.
++ * SCROLL_TO_BEGINNING: Scrolls text that's too long to fit in the omnibox
++ * to the beginning so we can see the first character.
++ */
++ private void scrollDisplayTextInternal(@ScrollType int scrollType) {
++ mPendingScroll = false;
++
++ if (mFocused) return;
++
++ Editable text = getText();
++ if (TextUtils.isEmpty(text)) scrollType = ScrollType.SCROLL_TO_BEGINNING;
++
++ // Ensure any selection from the focus state is cleared.
++ setSelection(0);
++
++ float currentTextSize = getTextSize();
++ boolean currentIsRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
++
++ int measuredWidth = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight());
++ if (scrollType == mPreviousScrollType && TextUtils.equals(text, mPreviousScrollText)
++ && measuredWidth == mPreviousScrollViewWidth
++ // Font size is float but it changes in discrete range (eg small font, big font),
++ // therefore false negative using regular equality is unlikely.
++ && currentTextSize == mPreviousScrollFontSize
++ && currentIsRtl == mPreviousScrollWasRtl) {
++ scrollTo(mPreviousScrollResultXPosition, getScrollY());
++ return;
++ }
++
++ switch (scrollType) {
++ case ScrollType.SCROLL_TO_TLD:
++ scrollToTLD();
++ break;
++ case ScrollType.SCROLL_TO_BEGINNING:
++ scrollToBeginning();
++ break;
++ default:
++ // Intentional return to avoid clearing scroll state when no scroll was applied.
++ return;
++ }
++
++ mPreviousScrollType = scrollType;
++ mPreviousScrollText = text.toString();
++ mPreviousScrollViewWidth = measuredWidth;
++ mPreviousScrollFontSize = currentTextSize;
++ mPreviousScrollResultXPosition = getScrollX();
++ mPreviousScrollWasRtl = currentIsRtl;
++ }
++
++ /**
++ * Scrolls the omnibox text to show the very beginning of the text entered.
++ */
++ private void scrollToBeginning() {
++ Editable text = getText();
++ float scrollPos = 0f;
++ if (TextUtils.isEmpty(text)) {
++ if (getLayoutDirection() == LAYOUT_DIRECTION_RTL
++ && BidiFormatter.getInstance().isRtl(getHint())) {
++ // Compared to below that uses getPrimaryHorizontal(1) due to 0 returning an
++ // invalid value, if the text is empty, getPrimaryHorizontal(0) returns the actual
++ // max scroll amount.
++ scrollPos = (int) getLayout().getPrimaryHorizontal(0) - getMeasuredWidth();
++ }
++ } else if (BidiFormatter.getInstance().isRtl(text)) {
++ // RTL.
++ float endPointX = getLayout().getPrimaryHorizontal(text.length());
++ int measuredWidth = getMeasuredWidth();
++ float width = getLayout().getPaint().measureText(text.toString());
++ scrollPos = Math.max(0, endPointX - measuredWidth + width);
++ }
++ scrollTo((int) scrollPos, getScrollY());
++ }
++
++ /**
++ * Scrolls the omnibox text to bring the TLD into view.
++ */
++ private void scrollToTLD() {
++ Editable url = getText();
++ int measuredWidth = getMeasuredWidth() - (getPaddingLeft() + getPaddingRight());
++
++ Layout textLayout = getLayout();
++ assert getLayout().getLineCount() == 1;
++ final int originEndIndex = Math.min(mOriginEndIndex, url.length());
++ if (mOriginEndIndex > url.length()) {
++ // If discovered locally, please update crbug.com/859219 with the steps to reproduce.
++ assert false : "Attempting to scroll past the end of the URL: " + url + ", end index: "
++ + mOriginEndIndex;
++ }
++ float endPointX = textLayout.getPrimaryHorizontal(originEndIndex);
++ // Compare the position offset of the last character and the character prior to determine
++ // the LTR-ness of the final component of the URL.
++ float priorToEndPointX = url.length() == 1
++ ? 0
++ : textLayout.getPrimaryHorizontal(Math.max(0, originEndIndex - 1));
++
++ float scrollPos;
++ if (priorToEndPointX < endPointX) {
++ // LTR
++ scrollPos = Math.max(0, endPointX - measuredWidth);
++ } else {
++ // RTL
++
++ // To handle BiDirectional text, search backward from the two existing offsets to find
++ // the first LTR character. Ensure the final RTL component of the domain is visible
++ // above any of the prior LTR pieces.
++ int rtlStartIndexForEndingRun = originEndIndex - 1;
++ for (int i = originEndIndex - 2; i >= 0; i--) {
++ float indexOffsetDrawPosition = textLayout.getPrimaryHorizontal(i);
++ if (indexOffsetDrawPosition > endPointX) {
++ rtlStartIndexForEndingRun = i;
++ } else {
++ // getPrimaryHorizontal determines the index position for the next character
++ // based on the previous characters. In bi-directional text, the first RTL
++ // character following LTR text will have an LTR-appearing horizontal offset
++ // as it is based on the preceding LTR text. Thus, the start of the RTL
++ // character run will be after and including the first LTR horizontal index.
++ rtlStartIndexForEndingRun = Math.max(0, rtlStartIndexForEndingRun - 1);
++ break;
++ }
++ }
++ float width = textLayout.getPaint().measureText(
++ url.subSequence(rtlStartIndexForEndingRun, originEndIndex).toString());
++ if (width < measuredWidth) {
++ scrollPos = Math.max(0, endPointX + width - measuredWidth);
++ } else {
++ scrollPos = endPointX + measuredWidth;
++ }
++ }
++ scrollTo((int) scrollPos, getScrollY());
++ }
++
++ @Override
++ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
++ super.onLayout(changed, left, top, right, bottom);
++
++ if (mPendingScroll) {
++ scrollDisplayTextInternal(mScrollType);
++ } else if (mPreviousWidth != (right - left)) {
++ scrollDisplayTextInternal(mScrollType);
++ mPreviousWidth = right - left;
++ }
++ }
++
++ @Override
++ public boolean bringPointIntoView(int offset) {
++ // TextView internally attempts to keep the selection visible, but in the unfocused state
++ // this class ensures that the TLD is visible.
++ if (!mFocused) return false;
++ assert !mPendingScroll;
++
++ return super.bringPointIntoView(offset);
++ }
++
++ @Override
++ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
++ InputConnection connection = super.onCreateInputConnection(outAttrs);
++ if (mUrlBarDelegate == null || !mUrlBarDelegate.allowKeyboardLearning()) {
++ outAttrs.imeOptions |= EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING;
++ }
++ return connection;
++ }
++
++ @Override
++ public void setText(CharSequence text, BufferType type) {
++ if (DEBUG) Log.i(TAG, "setText -- text: %s", text);
++ super.setText(text, type);
++ fixupTextDirection();
++ }
++
++ private void limitDisplayableLength() {
++ // To limit displayable length we replace middle portion of the string with ellipsis.
++ // That affects only presentation of the text, and doesn't affect other aspects like
++ // copying to the clipboard, getting text with getText(), etc.
++ final int maxLength =
++ SysUtils.isLowEndDevice() ? MAX_DISPLAYABLE_LENGTH_LOW_END : MAX_DISPLAYABLE_LENGTH;
++
++ Editable text = getText();
++ int textLength = text.length();
++ if (textLength <= maxLength) {
++ if (mDidEllipsizeTextHint) {
++ EllipsisSpan[] spans = text.getSpans(0, textLength, EllipsisSpan.class);
++ if (spans != null && spans.length > 0) {
++ assert spans.length == 1 : "Should never apply more than a single EllipsisSpan";
++ for (int i = 0; i < spans.length; i++) {
++ text.removeSpan(spans[i]);
++ }
++ }
++ }
++ mDidEllipsizeTextHint = false;
++ return;
++ }
++
++ mDidEllipsizeTextHint = true;
++
++ int spanLeft = text.nextSpanTransition(0, textLength, EllipsisSpan.class);
++ if (spanLeft != textLength) return;
++
++ spanLeft = maxLength / 2;
++ text.setSpan(EllipsisSpan.INSTANCE, spanLeft, textLength - spanLeft,
++ Editable.SPAN_INCLUSIVE_EXCLUSIVE);
++ }
++
++ @Override
++ public Editable getText() {
++ if (mRequestingAutofillStructure) {
++ // crbug.com/1109186: mTextForAutofillServices must not be null here, but Autofill
++ // requests can be triggered before it is initialized.
++ return new SpannableStringBuilder(
++ mTextForAutofillServices != null ? mTextForAutofillServices : "");
++ }
++ return super.getText();
++ }
++
++ @Override
++ public CharSequence getAccessibilityClassName() {
++ // When UrlBar is used as a read-only TextView, force Talkback to pronounce it like
++ // TextView. Otherwise Talkback will say "Edit box, http://...". crbug.com/636988
++ if (isEnabled()) {
++ return super.getAccessibilityClassName();
++ } else {
++ return TextView.class.getName();
++ }
++ }
++
++ @Override
++ public void replaceAllTextFromAutocomplete(String text) {
++ if (DEBUG) Log.i(TAG, "replaceAllTextFromAutocomplete: " + text);
++ setText(text);
++ }
++
++ @Override
++ public void onAutocompleteTextStateChanged(boolean updateDisplay) {
++ if (DEBUG) {
++ Log.i(TAG, "onAutocompleteTextStateChanged: DIS[%b]", updateDisplay);
++ }
++ if (mUrlTextChangeListener == null) return;
++ if (updateDisplay) limitDisplayableLength();
++ // crbug.com/764749
++ Log.w(TAG, "Text change observed, triggering autocomplete.");
++
++ mUrlTextChangeListener.onTextChanged(
++ getTextWithoutAutocomplete(), getTextWithAutocomplete());
++ }
++
++ /**
++ * Span that displays ellipsis instead of the text. Used to hide portion of
++ * very large string to get decent performance from TextView.
++ */
++ private static class EllipsisSpan extends ReplacementSpan {
++ private static final String ELLIPSIS = "...";
++
++ public static final EllipsisSpan INSTANCE = new EllipsisSpan();
++
++ @Override
++ public int getSize(
++ Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
++ return (int) paint.measureText(ELLIPSIS);
++ }
++
++ @Override
++ public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top,
++ int y, int bottom, Paint paint) {
++ canvas.drawText(ELLIPSIS, x, y, paint);
++ }
++ }
++}
+diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
++++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+@@ -206,8 +206,7 @@ public abstract class UrlBar extends AutocompleteEditText {
+ // the first draw.
+ setFocusable(false);
+ setFocusableInTouchMode(false);
+- setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI
+- | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
++ setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
+
+ // The HTC Sense IME will attempt to autocomplete words in the Omnibox when Prediction is
+ // enabled. We want to disable this feature and rely on the Omnibox's implementation.
+--
+2.25.1