Commit 91f7d6cc authored by Kent Tamura's avatar Kent Tamura Committed by Commit Bot

ValidationBubbleInRenderer: Adjust bubble arrow position.

This CL includes RTL support.

* If the UI locale is RTL, flip the bubble layout.
 - The default arrow position is on the right side.
 - The icon is on the right side.

* If the default position of the bubble arrow doesn't point at the anchor
  element rectangle, move the arrow position.

Implementation:
- Set <body dir=> for the UI locale.

- Found that it's difficult to adjust arrow position in RTL +
  position:relative. Switch inner arrow's position to 'absolute', and add spacer
  elements to secure height.

- Fix the icon margin in RTL.

- Drop layout tree dump from tests.

Bug: 739091
Change-Id: I3da7f9ed3a248d1bf5a95d77d3b93667e3bdf9da
Reviewed-on: https://chromium-review.googlesource.com/561048Reviewed-by: default avatarKeishi Hattori <keishi@chromium.org>
Commit-Queue: Kent Tamura <tkent@chromium.org>
Cr-Commit-Position: refs/heads/master@{#485002}
parent f0f0169b
...@@ -866,6 +866,7 @@ crbug.com/487281 [ Mac ] fast/forms/select/menulist-narrow-width.html [ Failure ...@@ -866,6 +866,7 @@ crbug.com/487281 [ Mac ] fast/forms/select/menulist-narrow-width.html [ Failure
# Mark as Failure until the development is done. # Mark as Failure until the development is done.
crbug.com/739091 fast/forms/validation-bubble-appearance-edge.html [ Failure ] crbug.com/739091 fast/forms/validation-bubble-appearance-edge.html [ Failure ]
crbug.com/739091 fast/forms/validation-bubble-appearance-iframe.html [ Failure ] crbug.com/739091 fast/forms/validation-bubble-appearance-iframe.html [ Failure ]
crbug.com/739091 fast/forms/validation-bubble-appearance-rtl-ui.html [ Failure ]
crbug.com/543110 [ Mac ] fast/text/international/text-shaping-arabic.html [ Failure ] crbug.com/543110 [ Mac ] fast/text/international/text-shaping-arabic.html [ Failure ]
......
layer at (0,0) size 800x600 Check if a validation bubble is shown at a correct position.
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x50
LayoutBlockFlow {HTML} at (0,0) size 800x50
LayoutBlockFlow {BODY} at (8,16) size 784x18
LayoutBlockFlow {P} at (0,0) size 784x18
LayoutText {#text} at (0,0) size 377x18
text run at (0,0) width 377: "Check if a validation bubble is shown at a correct position."
layer at (661,573) size 131x19 clip at (663,575) size 127x15
LayoutTextControl (positioned) {INPUT} at (661,573) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
layer at (664,576) size 125x13
LayoutBlockFlow {DIV} at (3,3) size 125x13
LayoutText {#text} at (0,0) size 19x13
text run at (0,0) width 19: "abc"
caret: position 0 of child 0 {#text} of child 0 {DIV} of {#document-fragment} of child 3 {INPUT} of body
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
<p>Check if a validation bubble is shown at a correct position.</p> <p>Check if a validation bubble is shown at a correct position.</p>
<input pattern="\d{4}" title="Please specify four digits." value="abc" style="position:absolute; right:8px; bottom: 8px;"> <input pattern="\d{4}" title="Please specify four digits." value="abc" style="position:absolute; right:8px; bottom: 8px;">
<script> <script>
if (window.testRunner) {
// Layout tree dump doesn't matter.
testRunner.dumpAsTextWithPixelResults();
}
document.querySelector('input').reportValidity(); document.querySelector('input').reportValidity();
</script> </script>
<body> <body>
layer at (0,0) size 800x600 Check if a validation bubble is shown over IFRAME boundary.
LayoutView at (0,0) size 800x600
layer at (0,0) size 800x216
LayoutBlockFlow {HTML} at (0,0) size 800x216
LayoutBlockFlow {BODY} at (8,16) size 784x192
LayoutBlockFlow {P} at (0,0) size 784x18
LayoutText {#text} at (0,0) size 406x18
text run at (0,0) width 406: "Check if a validation bubble is shown over IFRAME boundary."
LayoutBlockFlow (anonymous) at (0,34) size 784x158
LayoutText {#text} at (0,0) size 0x0
layer at (8,50) size 304x154
LayoutIFrame {IFRAME} at (0,0) size 304x154 [border: (2px inset #EEEEEE)]
layer at (0,0) size 300x150
LayoutView at (0,0) size 300x150
layer at (0,0) size 300x68
LayoutBlockFlow {HTML} at (0,0) size 300x68
LayoutBlockFlow {BODY} at (8,16) size 284x36
LayoutBlockFlow {P} at (0,0) size 284x36
LayoutText {#text} at (0,0) size 269x36
text run at (0,0) width 269: "Check if a validation bubble is shown at a"
text run at (0,18) width 104: "correct position."
layer at (161,123) size 131x19 clip at (163,125) size 127x15
LayoutTextControl (positioned) {INPUT} at (161,123) size 131x19 [bgcolor=#FFFFFF] [border: (2px inset #EEEEEE)]
layer at (164,126) size 125x13
LayoutBlockFlow {DIV} at (3,3) size 125x13
LayoutText {#text} at (0,0) size 19x13
text run at (0,0) width 19: "abc"
...@@ -2,4 +2,10 @@ ...@@ -2,4 +2,10 @@
<body> <body>
<p>Check if a validation bubble is shown over IFRAME boundary.</p> <p>Check if a validation bubble is shown over IFRAME boundary.</p>
<iframe src="validation-bubble-appearance-edge.html"></iframe> <iframe src="validation-bubble-appearance-edge.html"></iframe>
<body> <script>
if (window.testRunner) {
// Layout tree dump doesn't matter.
testRunner.dumpAsTextWithPixelResults();
}
</script>
</body>
Check if a validation bubble is shown at a correct position in RTL UI.
<!DOCTYPE html>
<body>
<p>Check if a validation bubble is shown at a correct position in RTL UI.</p>
<input pattern="\d{4}" title="Please specify four digits." value="abc" style="margin-left: 30%;">
<script>
if (window.testRunner) {
// Layout tree dump doesn't matter.
testRunner.dumpAsTextWithPixelResults();
}
internals.setUserPreferredLanguages(['ar']);
document.querySelector('input').reportValidity();
</script>
<body>
...@@ -29,13 +29,18 @@ ...@@ -29,13 +29,18 @@
padding: 8px; padding: 8px;
} }
#spacer-top {
display: block;
height: var(--arrow-size);
}
#outer-arrow-top { #outer-arrow-top {
border-color: transparent transparent var(--bubble-border-color) transparent; border-color: transparent transparent var(--bubble-border-color) transparent;
border-style: solid; border-style: solid;
border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size); border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size);
display: block; display: block;
left: 10px; left: 10px;
margin-top: -2px; margin-top: 0px;
position: absolute; position: absolute;
width: 0px; width: 0px;
} }
...@@ -46,26 +51,31 @@ ...@@ -46,26 +51,31 @@
border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size); border-width: 0px var(--arrow-size) var(--arrow-size) var(--arrow-size);
display: block; display: block;
left: 10px; left: 10px;
margin-bottom: -2px; margin-top: 1px;
position: relative; position: absolute;
width: 0px; width: 0px;
} }
.bottom-arrow #outer-arrow-top, .bottom-arrow #inner-arrow-top { .bottom-arrow #outer-arrow-top, .bottom-arrow #inner-arrow-top, .bottom-arrow #spacer-top {
display: none; display: none;
} }
#outer-arrow-bottom, #inner-arrow-bottom { #outer-arrow-bottom, #inner-arrow-bottom, #spacer-bottom {
display: none; display: none;
} }
.bottom-arrow #spacer-bottom {
display: block;
height: var(--arrow-size);
}
.bottom-arrow #outer-arrow-bottom { .bottom-arrow #outer-arrow-bottom {
border-color: var(--bubble-border-color) transparent transparent transparent; border-color: var(--bubble-border-color) transparent transparent transparent;
border-style: solid; border-style: solid;
border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size); border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size);
display: block; display: block;
left: 10px; left: 10px;
margin-top: 1px; margin-top: 0px;
position: absolute; position: absolute;
width: 0px; width: 0px;
} }
...@@ -76,15 +86,15 @@ ...@@ -76,15 +86,15 @@
border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size); border-width: var(--arrow-size) var(--arrow-size) 0px var(--arrow-size);
display: block; display: block;
left: 10px; left: 10px;
margin-top: -2px; margin-top: -1px;
position: relative; position: absolute;
width: 0px; width: 0px;
} }
#icon { #icon {
grid-row: 1 / 3; grid-row: 1 / 3;
grid-column: 1; grid-column: 1;
margin-right: 8px; -webkit-margin-end: 8px;
} }
#main-message { #main-message {
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "core/page/PagePopupClient.h" #include "core/page/PagePopupClient.h"
#include "platform/LayoutTestSupport.h" #include "platform/LayoutTestSupport.h"
#include "platform/graphics/paint/CullRect.h" #include "platform/graphics/paint/CullRect.h"
#include "platform/text/PlatformLocale.h"
namespace blink { namespace blink {
...@@ -129,7 +130,7 @@ void ValidationMessageOverlayDelegate::EnsurePage(const PageOverlay& overlay, ...@@ -129,7 +130,7 @@ void ValidationMessageOverlayDelegate::EnsurePage(const PageOverlay& overlay,
SubstituteData(data, "text/html", "UTF-8", KURL(), SubstituteData(data, "text/html", "UTF-8", KURL(),
kForceSynchronousLoad))); kForceSynchronousLoad)));
Element& container = BubbleContainer(); Element& container = GetElementById("container");
if (LayoutTestSupport::IsRunningLayoutTest()) if (LayoutTestSupport::IsRunningLayoutTest())
container.SetInlineStyleProperty(CSSPropertyTransition, "none"); container.SetInlineStyleProperty(CSSPropertyTransition, "none");
// Get the size to decide position later. // Get the size to decide position later.
...@@ -137,8 +138,8 @@ void ValidationMessageOverlayDelegate::EnsurePage(const PageOverlay& overlay, ...@@ -137,8 +138,8 @@ void ValidationMessageOverlayDelegate::EnsurePage(const PageOverlay& overlay,
bubble_size_ = container.VisibleBoundsInVisualViewport().Size(); bubble_size_ = container.VisibleBoundsInVisualViewport().Size();
// Add one because the content sometimes exceeds the exact width due to // Add one because the content sometimes exceeds the exact width due to
// rounding errors. // rounding errors.
container.SetInlineStyleProperty(CSSPropertyMinWidth, bubble_size_.Expand(1, 0);
bubble_size_.Width() + 1, container.SetInlineStyleProperty(CSSPropertyMinWidth, bubble_size_.Width(),
CSSPrimitiveValue::UnitType::kPixels); CSSPrimitiveValue::UnitType::kPixels);
} }
...@@ -146,11 +147,15 @@ void ValidationMessageOverlayDelegate::WriteDocument(SharedBuffer* data) { ...@@ -146,11 +147,15 @@ void ValidationMessageOverlayDelegate::WriteDocument(SharedBuffer* data) {
DCHECK(data); DCHECK(data);
PagePopupClient::AddString("<!DOCTYPE html><html><head><style>", data); PagePopupClient::AddString("<!DOCTYPE html><html><head><style>", data);
data->Append(Platform::Current()->GetDataResource("validation_bubble.css")); data->Append(Platform::Current()->GetDataResource("validation_bubble.css"));
PagePopupClient::AddString("</style></head>", data);
PagePopupClient::AddString(
Locale::DefaultLocale().IsRTL() ? "<body dir=rtl>" : "<body dir=ltr>",
data);
PagePopupClient::AddString( PagePopupClient::AddString(
"</style></head><body>"
"<div id=container>" "<div id=container>"
"<div id=outer-arrow-top></div>" "<div id=outer-arrow-top></div>"
"<div id=inner-arrow-top></div>" "<div id=inner-arrow-top></div>"
"<div id=spacer-top></div>"
"<main id=bubble-body>", "<main id=bubble-body>",
data); data);
data->Append(Platform::Current()->GetDataResource("input_alert.svg")); data->Append(Platform::Current()->GetDataResource("input_alert.svg"));
...@@ -168,16 +173,18 @@ void ValidationMessageOverlayDelegate::WriteDocument(SharedBuffer* data) { ...@@ -168,16 +173,18 @@ void ValidationMessageOverlayDelegate::WriteDocument(SharedBuffer* data) {
"</div></main>" "</div></main>"
"<div id=outer-arrow-bottom></div>" "<div id=outer-arrow-bottom></div>"
"<div id=inner-arrow-bottom></div>" "<div id=inner-arrow-bottom></div>"
"<div id=spacer-bottom></div>"
"</div></body></html>\n", "</div></body></html>\n",
data); data);
} }
Element& ValidationMessageOverlayDelegate::BubbleContainer() const { Element& ValidationMessageOverlayDelegate::GetElementById(
Element* container = ToLocalFrame(page_->MainFrame()) const AtomicString& id) const {
->GetDocument() Element* element =
->getElementById("container"); ToLocalFrame(page_->MainFrame())->GetDocument()->getElementById(id);
DCHECK(container) << "Failed to load the document?"; DCHECK(element) << "No element with id=" << id
return *container; << ". Failed to load the document?";
return *element;
} }
void ValidationMessageOverlayDelegate::AdjustBubblePosition( void ValidationMessageOverlayDelegate::AdjustBubblePosition(
...@@ -196,7 +203,7 @@ void ValidationMessageOverlayDelegate::AdjustBubblePosition( ...@@ -196,7 +203,7 @@ void ValidationMessageOverlayDelegate::AdjustBubblePosition(
else if (bubble_x + bubble_size_.Width() > view_size.Width()) else if (bubble_x + bubble_size_.Width() > view_size.Width())
bubble_x = view_size.Width() - bubble_size_.Width(); bubble_x = view_size.Width() - bubble_size_.Width();
Element& container = BubbleContainer(); Element& container = GetElementById("container");
container.SetInlineStyleProperty(CSSPropertyLeft, bubble_x, container.SetInlineStyleProperty(CSSPropertyLeft, bubble_x,
CSSPrimitiveValue::UnitType::kPixels); CSSPrimitiveValue::UnitType::kPixels);
container.SetInlineStyleProperty(CSSPropertyTop, bubble_y, container.SetInlineStyleProperty(CSSPropertyTop, bubble_y,
...@@ -208,7 +215,54 @@ void ValidationMessageOverlayDelegate::AdjustBubblePosition( ...@@ -208,7 +215,54 @@ void ValidationMessageOverlayDelegate::AdjustBubblePosition(
else else
container.removeAttribute(HTMLNames::classAttr); container.removeAttribute(HTMLNames::classAttr);
// TODO(tkent): Adjust arrow position. // Should match to --arrow-size in validation_bubble.css.
const int kArrowSize = 8;
const int kArrowMargin = 10;
const int kMinArrowAnchorX = kArrowSize + kArrowMargin;
double max_arrow_anchor_x =
bubble_size_.Width() - (kArrowSize + kArrowMargin);
double arrow_anchor_x;
const int kOffsetToAnchorRect = 8;
double anchor_rect_center = anchor_rect.X() + anchor_rect.Width() / 2;
if (!Locale::DefaultLocale().IsRTL()) {
double anchor_rect_left = anchor_rect.X() + kOffsetToAnchorRect;
if (anchor_rect_left > anchor_rect_center)
anchor_rect_left = anchor_rect_center;
arrow_anchor_x = kMinArrowAnchorX;
if (bubble_x + arrow_anchor_x < anchor_rect_left) {
arrow_anchor_x = anchor_rect_left - bubble_x;
if (arrow_anchor_x > max_arrow_anchor_x)
arrow_anchor_x = max_arrow_anchor_x;
}
} else {
double anchor_rect_right = anchor_rect.MaxX() - kOffsetToAnchorRect;
if (anchor_rect_right < anchor_rect_center)
anchor_rect_right = anchor_rect_center;
arrow_anchor_x = max_arrow_anchor_x;
if (bubble_x + arrow_anchor_x > anchor_rect_right) {
arrow_anchor_x = anchor_rect_right - bubble_x;
if (arrow_anchor_x < kMinArrowAnchorX)
arrow_anchor_x = kMinArrowAnchorX;
}
}
double arrow_x = arrow_anchor_x - kArrowSize;
if (show_bottom_arrow) {
GetElementById("outer-arrow-bottom")
.SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
CSSPrimitiveValue::UnitType::kPixels);
GetElementById("inner-arrow-bottom")
.SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
CSSPrimitiveValue::UnitType::kPixels);
} else {
GetElementById("outer-arrow-top")
.SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
CSSPrimitiveValue::UnitType::kPixels);
GetElementById("inner-arrow-top")
.SetInlineStyleProperty(CSSPropertyLeft, arrow_x,
CSSPrimitiveValue::UnitType::kPixels);
}
} }
} // namespace blink } // namespace blink
...@@ -49,7 +49,7 @@ class ValidationMessageOverlayDelegate : public PageOverlay::Delegate { ...@@ -49,7 +49,7 @@ class ValidationMessageOverlayDelegate : public PageOverlay::Delegate {
void UpdateFrameViewState(const PageOverlay&, const IntSize& view_size); void UpdateFrameViewState(const PageOverlay&, const IntSize& view_size);
void EnsurePage(const PageOverlay&, const IntSize& view_size); void EnsurePage(const PageOverlay&, const IntSize& view_size);
void WriteDocument(SharedBuffer*); void WriteDocument(SharedBuffer*);
Element& BubbleContainer() const; Element& GetElementById(const AtomicString&) const;
void AdjustBubblePosition(const IntSize& view_size); void AdjustBubblePosition(const IntSize& view_size);
// An internal Page and a ChromeClient for it. // An internal Page and a ChromeClient for it.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment