diff options
author | Ben Wells <benwells@chromium.org> | 2015-04-21 14:00:34 +1000 |
---|---|---|
committer | Ben Wells <benwells@chromium.org> | 2015-04-21 04:01:55 +0000 |
commit | 5aba40cebe8c915c114f3d6ffd10a0c29f0f77d8 (patch) | |
tree | 65f984cbda4cecb17d8acb9da6b3cbbdbcb0debb | |
parent | 2fb57e2b1cb54c8bcbb33936ec9f3c5552cf0376 (diff) | |
download | chromium_src-5aba40cebe8c915c114f3d6ffd10a0c29f0f77d8.zip chromium_src-5aba40cebe8c915c114f3d6ffd10a0c29f0f77d8.tar.gz chromium_src-5aba40cebe8c915c114f3d6ffd10a0c29f0f77d8.tar.bz2 |
Truncate long URL fragments in Android page info popup
This replaces percent-encoding whitespace as a method to combat the use
of crafted URL fragments to inject messages into the page info popup. By
truncating the URL so that at most two lines of the fragment are shown,
we prevent lengthy messages from being injected while minimising the
effect on most URLs.
BUG=466351
Review URL: https://codereview.chromium.org/1077483002
Merged from trunk.
TBR=tedchoc@chromium.org
Cr-Commit-Position: refs/heads/master@{#325358}
(cherry picked from commit 5bf8a2dccf4777c5f065d918d1d9a2bbaa013f9f)
Review URL: https://codereview.chromium.org/1061533006
Cr-Commit-Position: refs/branch-heads/2357@{#175}
Cr-Branched-From: 59d4494849b405682265ed5d3f5164573b9a939b-refs/heads/master@{#323860}
-rw-r--r-- | chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java | 82 | ||||
-rw-r--r-- | chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java | 40 |
2 files changed, 41 insertions, 81 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java index 4badd78..1c67e4f 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java @@ -9,7 +9,6 @@ import android.content.Context; import android.content.DialogInterface; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import android.net.Uri; import android.text.Layout; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -75,12 +74,20 @@ public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList } } + /** + * A TextView which truncates and displays a URL such that the origin is always visible. + * The URL can be expanded by clicking on the it. + */ public static class ElidedUrlTextView extends TextView { // The number of lines to display when the URL is truncated. This number // should still allow the origin to be displayed. NULL before // setUrlAfterLayout() is called. private Integer mTruncatedUrlLinesToDisplay; + // The number of lines to display when the URL is expanded. This should be enough to display + // at most two lines of the fragment if there is one in the URL. + private Integer mFullLinesToDisplay; + // If true, the text view will show the truncated text. If false, it // will show the full, expanded text. private boolean mIsShowingTruncatedText = true; @@ -102,33 +109,51 @@ public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList mCurrentMaxLines = maxlines; } + /** + * Find the number of lines of text which must be shown in order to display the character at + * a given index. + */ + private int getLineForIndex(int index) { + Layout layout = getLayout(); + int endLine = 0; + while (endLine < layout.getLineCount() && layout.getLineEnd(endLine) < index) { + endLine++; + } + // Since endLine is an index, add 1 to get the number of lines. + return endLine + 1; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMaxLines(Integer.MAX_VALUE); super.onMeasure(widthMeasureSpec, heightMeasureSpec); assert mProfile != null : "setProfile() must be called before layout."; + String urlText = getText().toString(); // Lay out the URL in a StaticLayout that is the same size as our final // container. - Layout layout = getLayout(); - int originEndIndex = - OmniboxUrlEmphasizer.getOriginEndIndex(getText().toString(), mProfile); + int originEndIndex = OmniboxUrlEmphasizer.getOriginEndIndex(urlText, mProfile); // Find the range of lines containing the origin. - int originEndLineIndex = 0; - while (originEndLineIndex < layout.getLineCount() - && layout.getLineEnd(originEndLineIndex) < originEndIndex) { - originEndLineIndex++; - } + int originEndLine = getLineForIndex(originEndIndex); // Display an extra line so we don't accidentally hide the origin with // ellipses - int lastLineIndexToDisplay = originEndLineIndex + 1; + mTruncatedUrlLinesToDisplay = originEndLine + 1; - // Since lastLineToDisplay is an index, add 1 to get the maximum number - // of lines. This will always be at least 2 lines (when the origin is - // fully contained on line 0). - mTruncatedUrlLinesToDisplay = lastLineIndexToDisplay + 1; + // Find the line where the fragment starts. Since # is a reserved character, it is safe + // to just search for the first # to appear in the url. + int fragmentStartIndex = urlText.indexOf('#'); + if (fragmentStartIndex == -1) fragmentStartIndex = urlText.length(); + + int fragmentStartLine = getLineForIndex(fragmentStartIndex); + mFullLinesToDisplay = fragmentStartLine + 1; + + // If there is no origin (according to OmniboxUrlEmphasizer), make sure the fragment is + // still hidden correctly. + if (mFullLinesToDisplay < mTruncatedUrlLinesToDisplay) { + mTruncatedUrlLinesToDisplay = mFullLinesToDisplay; + } if (updateMaxLines()) super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -153,7 +178,7 @@ public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList } private boolean updateMaxLines() { - int maxLines = Integer.MAX_VALUE; + int maxLines = mFullLinesToDisplay; if (mIsShowingTruncatedText) maxLines = mTruncatedUrlLinesToDisplay; if (maxLines != mCurrentMaxLines) { setMaxLines(maxLines); @@ -165,10 +190,6 @@ public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList private static final int MAX_TABLET_DIALOG_WIDTH_DP = 400; - private static final char FIRST_UNICODE_WHITESPACE = '\u2000'; - private static final char FINAL_UNICODE_WHITESPACE = '\u200F'; - private static final char UNICODE_NBSP = '\u00A0'; - private final Context mContext; private final Profile mProfile; private final WebContents mWebContents; @@ -298,8 +319,7 @@ public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList mSecurityLevel = ToolbarModel.getSecurityLevelForWebContents(mWebContents); mDeprecatedSHA1Present = ToolbarModel.isDeprecatedSHA1Present(mWebContents); - String displayUrl = prepareUrlForDisplay(mFullUrl); - SpannableStringBuilder urlBuilder = new SpannableStringBuilder(displayUrl); + SpannableStringBuilder urlBuilder = new SpannableStringBuilder(mFullUrl); OmniboxUrlEmphasizer.emphasizeUrl(urlBuilder, mContext.getResources(), mProfile, mSecurityLevel, mIsInternalPage, true); mUrlTitle.setText(urlBuilder); @@ -310,26 +330,6 @@ public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedList } /** - * Percent-encodes suspicious Unicode whitespace characters in a URL so that it can be safely - * displayed. - */ - public static String prepareUrlForDisplay(String urlStr) { - StringBuilder urlBuilder = new StringBuilder(); - for (int i = 0; i < urlStr.length(); i++) { - char c = urlStr.charAt(i); - if ((c >= FIRST_UNICODE_WHITESPACE - && c <= FINAL_UNICODE_WHITESPACE) - || c == ' ' - || c == UNICODE_NBSP) { - urlBuilder.append(Uri.encode(Character.toString(c))); - } else { - urlBuilder.append(c); - } - } - return urlBuilder.toString(); - } - - /** * Sets the visibility of the lower area of the dialog (containing the permissions and 'Site * Settings' button). * diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java deleted file mode 100644 index e1f605b..0000000 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/WebsiteSettingsPopupTest.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.chrome.browser; - -import android.test.suitebuilder.annotation.SmallTest; - -import junit.framework.TestCase; - -/** - * Tests for WebsiteSettingsPopup - */ -public class WebsiteSettingsPopupTest extends TestCase { - @SmallTest - public void testPrepareUrlForDisplay() { - assertEquals("Encode suspicious message", - WebsiteSettingsPopup.prepareUrlForDisplay( - "http://example.com/# WARNING \u00A0Chrome has detected malware on your" - + " device!"), - "http://example.com/#%20%20WARNING%20%20%C2%A0Chrome%20has%20detected%20malware%20" - + "on%20your%20device!"); - assertEquals("Do not encode valid Unicode fragment", - WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#Düsseldorf"), - "http://example.com/#Düsseldorf"); - assertEquals("Encode fragment with spaces", - WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#hi how are you"), - "http://example.com/#hi%20how%20are%20you"); - assertEquals("Encode fragment with Unicode whitespace", - WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#em\u2003space"), - "http://example.com/#em%E2%80%83space"); - assertEquals("Do not encode reserved URI characters or valid Unicode", - WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/?q=a#Düsseldorf," - + " Germany"), - "http://example.com/?q=a#Düsseldorf,%20Germany"); - assertEquals("Preserve characters from supplementary Unicode planes", - WebsiteSettingsPopup.prepareUrlForDisplay("http://example.com/#\uD835\uDC9Cstral"), - "http://example.com/#\uD835\uDC9Cstral"); - } -} |