Bug 1669158 - Fix the offset of nested iframe by considering the border and padding size. r=mats,geckoview-reviewers,TYLin,agi

Differential Revision: https://phabricator.services.mozilla.com/D92375
This commit is contained in:
lonocvb 2020-10-13 05:02:26 +00:00
parent 9bd206cdff
commit 803b368879
3 changed files with 88 additions and 10 deletions

View File

@ -1438,12 +1438,6 @@ void AccessibleCaretManager::DispatchCaretStateChangedEvent(
init.mSelectionVisible = true;
}
// The rect computed above is relative to rootFrame, which is the (layout)
// viewport frame. However, the consumers of this event expect the bounds
// of the selection relative to the screen (visual viewport origin), so
// translate between the two.
rect -= mPresShell->GetVisualViewportOffsetRelativeToLayoutViewport();
domRect->SetLayoutRect(rect);
// Send isEditable info w/ event detail. This info can help determine

View File

@ -125,11 +125,21 @@ class GeckoViewSelectionActionChild extends GeckoViewChildModule {
let currentWindow = aEvent.target.defaultView;
while (currentWindow.realFrameElement) {
const currentRect = currentWindow.realFrameElement.getBoundingClientRect();
currentWindow = currentWindow.realFrameElement.ownerGlobal;
const frameElement = currentWindow.realFrameElement;
currentWindow = frameElement.ownerGlobal;
offset.left += currentRect.left;
offset.top += currentRect.top;
// The offset of the iframe window relative to the parent window
// includes the iframe's border, and the iframe's origin in its
// containing document.
const currentRect = frameElement.getBoundingClientRect();
const style = currentWindow.getComputedStyle(frameElement);
const borderLeft = parseFloat(style.borderLeftWidth) || 0;
const borderTop = parseFloat(style.borderTopWidth) || 0;
const paddingLeft = parseFloat(style.paddingLeft) || 0;
const paddingTop = parseFloat(style.paddingTop) || 0;
offset.left += currentRect.left + borderLeft + paddingLeft;
offset.top += currentRect.top + borderTop + paddingTop;
const targetDocShell = currentWindow.docShell;
if (targetDocShell.isMozBrowser) {
@ -137,6 +147,19 @@ class GeckoViewSelectionActionChild extends GeckoViewChildModule {
}
}
// Now we have coordinates relative to the root content document's
// layout viewport. Subtract the offset of the visual viewport
// relative to the layout viewport, to get coordinates relative to
// the visual viewport.
var offsetX = {};
var offsetY = {};
currentWindow.windowUtils.getVisualViewportOffsetRelativeToLayoutViewport(
offsetX,
offsetY
);
offset.left -= offsetX.value;
offset.top -= offsetY.value;
return offset;
}

View File

@ -13,6 +13,7 @@ import org.mozilla.geckoview.test.util.Callbacks
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.graphics.RectF;
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.filters.MediumTest
@ -182,6 +183,20 @@ class SelectionActionDelegateTest : BaseSessionTest() {
counter, equalTo(1))
}
@Test fun compareClientRect() {
val jsCssReset = """(function() {
document.querySelector('${id}').style.display = "block";
document.querySelector('${id}').style.border = "0";
document.querySelector('${id}').style.padding = "0";
})()"""
val jsBorder10pxPadding10px = """(function() {
document.querySelector('${id}').style.display = "block";
document.querySelector('${id}').style.border = "10px solid";
document.querySelector('${id}').style.padding = "10px";
})()"""
val expectedDiff = RectF(20f, 20f, 20f, 20f) // left, top, right, bottom
testClientRect(selectedContent, jsCssReset, jsBorder10pxPadding10px, expectedDiff)
}
/** Interface that defines behavior for a particular type of content */
private interface SelectedContent {
@ -228,6 +243,52 @@ class SelectionActionDelegateTest : BaseSessionTest() {
sideEffects.forEach { it(content) }
}
private fun testClientRect(content: SelectedContent,
initialJsA: String,
initialJsB: String,
expectedDiff: RectF) {
// Show selection actions for collapsed selections, so we can test them.
// Also, always show accessible carets / selection actions for changes due to JS calls.
sessionRule.setPrefsUntilTestEnd(mapOf(
"geckoview.selection_action.show_on_focus" to true,
"layout.accessiblecaret.script_change_update_mode" to 2))
mainSession.loadTestPath(INPUTS_PATH)
mainSession.waitForPageStop()
val requestClientRect: (String) -> RectF = {
mainSession.reload()
mainSession.waitForPageStop()
mainSession.evaluateJS(it)
content.focus()
var clientRect = RectF()
content.select()
mainSession.waitUntilCalled(object : Callbacks.SelectionActionDelegate {
@AssertCalled(count = 1)
override fun onShowActionRequest(session: GeckoSession, selection: Selection) {
clientRect = selection.clientRect!!
}
})
clientRect
}
val clientRectA = requestClientRect(initialJsA)
val clientRectB = requestClientRect(initialJsB)
val fuzzyEqual = { a: Float, b: Float, e: Float -> Math.abs(a + e - b) <= 1 }
val result = fuzzyEqual(clientRectA.top, clientRectB.top, expectedDiff.top)
&& fuzzyEqual(clientRectA.left, clientRectB.left, expectedDiff.left)
&& fuzzyEqual(clientRectA.width(), clientRectB.width(), expectedDiff.width())
&& fuzzyEqual(clientRectA.height(), clientRectB.height(), expectedDiff.height())
assertThat("Selection rect is not at expected location. a$clientRectA b$clientRectB",
result, equalTo(true))
}
/** Helpers. */