Skip to content

Commit

Permalink
Updating IE scroll-into-view algorithm for element click
Browse files Browse the repository at this point in the history
This update makes the scroll-into-view algorithm for element click
to be compliant with the W3C WebDriver Specification.
  • Loading branch information
jimevans committed Nov 19, 2018
1 parent 47f4439 commit adae7e4
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 34 deletions.
51 changes: 33 additions & 18 deletions cpp/iedriver/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -686,7 +686,7 @@ int Element::GetLocationOnceScrolledIntoView(const ElementScrollBehavior scroll,

if (result != WD_SUCCESS ||
!this->IsLocationInViewPort(click_location, document_contains_frames) ||
this->IsHiddenByOverflow() ||
this->IsHiddenByOverflow(element_location, click_location) ||
!this->IsLocationVisibleInFrames(click_location, *frame_locations)) {
// Scroll the element into view
LOG(DEBUG) << "Will need to scroll element into view";
Expand Down Expand Up @@ -734,19 +734,25 @@ int Element::GetLocationOnceScrolledIntoView(const ElementScrollBehavior scroll,
return status_code;
}

bool Element::IsHiddenByOverflow() {
bool Element::IsHiddenByOverflow(const LocationInfo element_location,
const LocationInfo click_location) {
LOG(TRACE) << "Entering Element::IsHiddenByOverflow";

bool is_overflow = false;

int x_offset = click_location.x - element_location.x;
int y_offset = click_location.y - element_location.y;

std::wstring script_source(L"(function() { return (");
script_source += atoms::asString(atoms::IS_IN_PARENT_OVERFLOW);
script_source += atoms::asString(atoms::IS_OFFSET_IN_PARENT_OVERFLOW);
script_source += L")})();";

CComPtr<IHTMLDocument2> doc;
this->GetContainingDocument(false, &doc);
Script script_wrapper(doc, script_source, 1);
Script script_wrapper(doc, script_source, 3);
script_wrapper.AddArgument(this->element_);
script_wrapper.AddArgument(x_offset);
script_wrapper.AddArgument(y_offset);
int status_code = script_wrapper.Execute();
if (status_code == WD_SUCCESS) {
std::wstring raw_overflow_state(script_wrapper.result().bstrVal);
Expand Down Expand Up @@ -1222,10 +1228,10 @@ bool Element::GetClickableViewPortLocation(const bool document_contains_frames,
// Hurrah! Now we know what the visible area of the viewport is
// N.B. There is an n-pixel sized area next to the client area border
// where clicks are interpreted as a click on the window border, not
// within the client area. We are assuming n == 2, but that's strictly
// a wild guess, not based on any research.
location->width = window_width - 2;
location->height = window_height - 2;
// within the client area. Some clicks may fail if they are close enough
// to the border.
location->width = window_width;
location->height = window_height;
return true;
}

Expand All @@ -1238,20 +1244,29 @@ LocationInfo Element::CalculateClickPoint(const LocationInfo location, const boo
LocationInfo clickable_viewport = {};
bool result = this->GetClickableViewPortLocation(document_contains_frames, &clickable_viewport);
if (result) {
// If GetClickableViewportLocation fails and this element is really big,
// then we will fail at another point and can trace that failure through
// the logs. If the element is not too big, then all will be normal.
if (corrected_width > (2 * clickable_viewport.width)) {
corrected_width = clickable_viewport.width;
}
if (corrected_height > (2 * clickable_viewport.height)) {
corrected_height = clickable_viewport.height;
RECT element_rect;
element_rect.left = location.x;
element_rect.top = location.y;
element_rect.right = location.x + location.width;
element_rect.bottom = location.y + location.height;

RECT viewport_rect;
viewport_rect.left = clickable_viewport.x;
viewport_rect.top = clickable_viewport.y;
viewport_rect.right = clickable_viewport.x + clickable_viewport.width;
viewport_rect.bottom = clickable_viewport.y + clickable_viewport.height;

RECT intersect_rect;
BOOL is_intersecting = ::IntersectRect(&intersect_rect, &element_rect, &viewport_rect);
if (is_intersecting) {
corrected_width = intersect_rect.right - intersect_rect.left;
corrected_height = intersect_rect.bottom - intersect_rect.top;
}
}

LocationInfo click_location = {};
click_location.x = location.x + (corrected_width / 2);
click_location.y = location.y + (corrected_height / 2);
click_location.x = location.x + floor(corrected_width / 2.0);
click_location.y = location.y + floor(corrected_height / 2.0);
return click_location;
}

Expand Down
3 changes: 2 additions & 1 deletion cpp/iedriver/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ class Element {
const bool document_contains_frames);
bool IsLocationVisibleInFrames(const LocationInfo location,
const std::vector<LocationInfo> frame_locations);
bool IsHiddenByOverflow();
bool IsHiddenByOverflow(const LocationInfo element_location,
const LocationInfo click_location);
bool AppendFrameDetails(std::vector<LocationInfo>* frame_locations);
int GetContainingDocument(const bool use_dom_node, IHTMLDocument2** doc);
int GetDocumentFromWindow(IHTMLWindow2* parent_window,
Expand Down
11 changes: 5 additions & 6 deletions cpp/iedriver/Generated/atoms.h
Original file line number Diff line number Diff line change
Expand Up @@ -6924,7 +6924,7 @@ const wchar_t* const FIND_ELEMENTS[] = {
NULL
};

const wchar_t* const IS_IN_PARENT_OVERFLOW[] = {
const wchar_t* const IS_OFFSET_IN_PARENT_OVERFLOW[] = {
L"function(){return function(){var k=this;function l(a){return void 0!==",
L"a}function n(a){return\"string\"==typeof a}function aa(a,b){a=a.split(",
L"\".\");var c=k;a[0]in c||!c.execScript||c.execScript(\"var \"+a[0]);fo",
Expand Down Expand Up @@ -7526,11 +7526,10 @@ const wchar_t* const IS_IN_PARENT_OVERFLOW[] = {
L"&(b=b instanceof Z?b:new Z(b.x,b.y,1,1),a.a=Math.min(Math.max(a.a+b.a,",
L"a.a),a.b),a.f=Math.min(Math.max(a.f+b.b,a.f),a.c),a.b=Math.min(Math.ma",
L"x(a.a+b.width,a.a),a.b),a.c=Math.min(Math.max(a.f+b.height,a.f),a.c));",
L"return a};aa(\"_\",function(a){var b=mc(a);return kc(a,new x(Math.roun",
L"d(b.width/2),Math.round(b.height/2)))});; return this._.apply(null,arg",
L"uments);}.apply({navigator:typeof window!='undefined'?window.navigator",
L":null,document:typeof window!='undefined'?window.document:null}, argum",
L"ents);}",
L"return a};aa(\"_\",function(a,b,c){return kc(a,new x(b,c))});; return ",
L"this._.apply(null,arguments);}.apply({navigator:typeof window!='undefi",
L"ned'?window.navigator:null,document:typeof window!='undefined'?window.",
L"document:null}, arguments);}",
NULL
};

Expand Down
11 changes: 5 additions & 6 deletions javascript/ie-driver/atoms.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,14 @@ webdriver.ie.findElements = function(mechanism, criteria, opt_root) {
* element, is currently in the overflow region.
*
* @param {!Element} element The element to check.
* @param {number} x The horizontal offset from the top-left corner.
* @param {number} y The vertical offset from the top-left corner.
* @return {bot.dom.OverflowState} Whether the coordinates specified, relative to the element,
* are scrolled in the parent overflow.
*/
webdriver.ie.isInParentOverflow = function(element) {
var rect = bot.dom.getClientRect(element);
var x = Math.round(rect.width / 2);
var y = Math.round(rect.height / 2);
var center = new goog.math.Coordinate(x, y);
return bot.dom.getOverflowState(element, center);
webdriver.ie.isOffsetInParentOverflow = function (element, x, y) {
var offsetPoint = new goog.math.Coordinate(x, y);
return bot.dom.getOverflowState(element, offsetPoint);
};

/**
Expand Down
6 changes: 3 additions & 3 deletions javascript/ie-driver/build.desc
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ js_fragment(name = "find_elements",
function = "webdriver.ie.findElements",
deps = [ ":deps" ])

js_fragment(name = "is_in_parent_overflow",
js_fragment(name = "is_offset_in_parent_overflow",
module = "webdriver.ie",
function = "webdriver.ie.isInParentOverflow",
function = "webdriver.ie.isOffsetInParentOverflow",
deps = [ ":deps" ])

js_fragment(name = "get_element_rect",
Expand All @@ -35,7 +35,7 @@ js_fragment_header(name = "atoms",
"//javascript/atoms/fragments:submit:ie",
"//javascript/ie-driver:find_element:ie",
"//javascript/ie-driver:find_elements:ie",
"//javascript/ie-driver:is_in_parent_overflow:ie",
"//javascript/ie-driver:is_offset_in_parent_overflow:ie",
"//javascript/ie-driver:get_element_rect:ie",
"//javascript/webdriver/atoms:inputs",
"//javascript/webdriver/atoms/fragments:get_attribute:ie",
Expand Down

0 comments on commit adae7e4

Please sign in to comment.