Skip to content

Commit b46c7bd

Browse files
committed
Expanding inline element detection for obscured elements in IE
This commit expands on the hack that was introduced in 3.14.0.11. It turns out that <label> elements are not the only ones to suffer from issues with elementsFromPoint. Rather, we now check for all types of inline elements. This change also refactors the calculation of the hit-testing point for calling elementsFromPoint in the first place, as it only makes sense to call from within the context of the element's document (omitting frames).
1 parent c58cd0f commit b46c7bd

File tree

1 file changed

+74
-86
lines changed

1 file changed

+74
-86
lines changed

cpp/iedriver/Element.cpp

+74-86
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,6 @@ bool Element::IsObscured(LocationInfo* click_location,
293293
return false;
294294
}
295295

296-
CComPtr<IHTMLDocument2> doc;
297-
this->GetContainingDocument(false, &doc);
298-
299296
// If an element has a style value where pointer-events is set to 'none',
300297
// the element is "obscured" by definition, since any mouse interaction
301298
// will not be handled by the element.
@@ -308,64 +305,49 @@ bool Element::IsObscured(LocationInfo* click_location,
308305
}
309306
}
310307

311-
bool is_obscured = false;
312-
313-
std::vector<LocationInfo> frame_locations;
308+
// The element being obscured only makes sense within the context
309+
// of its own document, even if it's not in the top-level document.
314310
LocationInfo element_location = {};
315-
int status_code = this->GetLocation(&element_location, &frame_locations);
316-
bool document_contains_frames = frame_locations.size() != 0;
317-
*click_location = this->CalculateClickPoint(element_location,
318-
document_contains_frames);
311+
int status_code = this->GetLocation(&element_location, nullptr);
312+
*click_location = this->CalculateClickPoint(element_location, false);
319313
long x = click_location->x;
320314
long y = click_location->y;
321-
if (document_contains_frames) {
322-
// If the document contains frames, we'll need to do elementsFromPoint
323-
// for the framed document, ignoring the frame offsets.
324-
CComPtr<IHTMLElement2> rect_element;
325-
this->element_->QueryInterface<IHTMLElement2>(&rect_element);
326-
CComPtr<IHTMLRect> rect;
327-
rect_element->getBoundingClientRect(&rect);
328-
long top = 0, bottom = 0, left = 0, right = 0;
329-
330-
rect->get_top(&top);
331-
rect->get_left(&left);
332-
rect->get_bottom(&bottom);
333-
rect->get_right(&right);
334315

335-
long width = right - left;
336-
long height = bottom - top;
337-
338-
x = left + (width / 2);
339-
y = top + (height / 2);
340-
}
316+
bool is_inline = this->IsInline();
341317

318+
CComPtr<IHTMLDocument2> doc;
319+
this->GetContainingDocument(false, &doc);
342320
CComPtr<IHTMLElement> element_hit;
343321
hr = doc->elementFromPoint(x, y, &element_hit);
344-
if (SUCCEEDED(hr) && element_.IsEqualObject(element_hit)) {
345-
// Short circuit the use of elementsFromPoint if we don't
346-
// have to use it.
347-
return false;
348-
} else {
349-
// Short circuit in the case where this element is specifically
350-
// a <label> element, and the top-most element as determined by
351-
// elementFromPoint is a direct child of this element. This is
352-
// to work around IE's bug in elementsFromPoint that does not
353-
// return <label> elements in the list of elements hit.
354-
// N.B., this is a hack of the highest order, and there's every
355-
// likelihood that some page somewhere will fail this check.
356-
CComPtr<IHTMLLabelElement> label;
357-
hr = this->element_->QueryInterface<IHTMLLabelElement>(&label);
358-
if (SUCCEEDED(hr) && label) {
359-
CComPtr<IHTMLElement> list_element_parent;
360-
hr = element_hit->get_parentElement(&list_element_parent);
361-
if (SUCCEEDED(hr) && list_element_parent) {
362-
if (this->element_.IsEqualObject(list_element_parent)) {
363-
return false;
322+
if (SUCCEEDED(hr) && element_hit) {
323+
if (element_.IsEqualObject(element_hit)) {
324+
// Short circuit the use of elementsFromPoint if we don't
325+
// have to use it.
326+
return false;
327+
} else {
328+
// Short circuit in the case where this element is specifically
329+
// an "inline" element (<label>, <span>, <a>, at present),
330+
// and the top-most element as determined by elementFromPoint is
331+
// a direct child of this element. This is to work around IE's bug
332+
// in elementsFromPoint that does not return inline elements in the
333+
// list of elements hit.
334+
// N.B., this is a hack of the highest order, and there's every
335+
// likelihood that some page somewhere will fail this check.
336+
if (is_inline) {
337+
CComPtr<IHTMLElement> element_hit_parent;
338+
hr = element_hit->get_parentElement(&element_hit_parent);
339+
CComBSTR element_hit_parent_tag;
340+
element_hit_parent->get_tagName(&element_hit_parent_tag);
341+
if (SUCCEEDED(hr) && element_hit_parent) {
342+
if (this->element_.IsEqualObject(element_hit_parent)) {
343+
return false;
344+
}
364345
}
365346
}
366347
}
367348
}
368349

350+
bool is_obscured = false;
369351
CComPtr<IHTMLDocument8> elements_doc;
370352
hr = doc.QueryInterface<IHTMLDocument8>(&elements_doc);
371353
if (FAILED(hr)) {
@@ -419,37 +401,33 @@ bool Element::IsObscured(LocationInfo* click_location,
419401

420402
CComPtr<IHTMLCSSStyleDeclaration> list_element_computed_style;
421403
if (list_element_wrapper.GetComputedStyle(&list_element_computed_style)) {
422-
// If the element has a pointer-events value set to 'none', it
423-
// may be technically obscuring this element, but manipulating
424-
// it with the pointer device has no effect, so it is effectively
425-
// not obscuring this element.
426404
CComBSTR list_element_pointer_events_value = L"";
427405
hr = list_element_computed_style->get_pointerEvents(&list_element_pointer_events_value);
428-
if (SUCCEEDED(hr) && list_element_pointer_events_value == L"none") {
429-
continue;
430-
} else {
431-
CComVariant opacity_variant;
432-
hr = list_element_computed_style->get_opacity(&opacity_variant);
433-
if (SUCCEEDED(hr)) {
434-
double opacity_value = 1.0;
435-
if (opacity_variant.vt == VT_BSTR) {
436-
opacity_value = _wtof(opacity_variant.bstrVal);
437-
} else {
438-
opacity_value = opacity_variant.dblVal;
439-
}
440-
441-
// If the element has an opacity less than 1.0, it's in a
442-
// different stacking context, and will be drawn below the
443-
// element we want to interact with.
444-
if (opacity_value >= 1.0) {
445-
is_obscured = true;
446-
}
447-
}
406+
if (SUCCEEDED(hr) && list_element_pointer_events_value != L"none") {
407+
// If the element has a pointer-events value set to 'none', it
408+
// may be technically obscuring this element, but manipulating
409+
// it with the pointer device has no effect, so it is effectively
410+
// not obscuring this element.
411+
is_obscured = true;
412+
break;
448413
}
449414
} else {
450415
// We were unable to retrieve the computed style, so we must assume
451416
// the other element is obscuring this one.
452417
is_obscured = true;
418+
break;
419+
}
420+
} else {
421+
// Repeating the immediate-child-of-inline-element hack from above for
422+
// elements found in the list.
423+
if (is_inline) {
424+
CComPtr<IHTMLElement> list_element_parent;
425+
hr = element_in_list->get_parentElement(&list_element_parent);
426+
if (SUCCEEDED(hr) && list_element_parent) {
427+
if (this->element_.IsEqualObject(list_element_parent)) {
428+
break;
429+
}
430+
}
453431
}
454432
}
455433
if (is_obscured) {
@@ -809,10 +787,11 @@ bool Element::IsSelected() {
809787
return selected;
810788
}
811789

812-
int Element::GetLocation(LocationInfo* location, std::vector<LocationInfo>* frame_locations) {
790+
int Element::GetLocation(LocationInfo* location,
791+
std::vector<LocationInfo>* frame_locations) {
813792
LOG(TRACE) << "Entering Element::GetLocation";
814793

815-
bool hasAbsolutePositionReadyToReturn = false;
794+
bool has_absolute_position_ready_to_return = false;
816795

817796
CComPtr<IHTMLElement2> element2;
818797
HRESULT hr = this->element_->QueryInterface(&element2);
@@ -845,7 +824,7 @@ int Element::GetLocation(LocationInfo* location, std::vector<LocationInfo>* fram
845824
if (RectHasNonZeroDimensions(rect)) {
846825
// IE returns absolute positions in the page, rather than frame- and scroll-bound
847826
// positions, for clientRects (as opposed to boundingClientRects).
848-
hasAbsolutePositionReadyToReturn = true;
827+
has_absolute_position_ready_to_return = true;
849828
break;
850829
}
851830
}
@@ -887,17 +866,22 @@ int Element::GetLocation(LocationInfo* location, std::vector<LocationInfo>* fram
887866
CComPtr<IHTMLDOMChildrenCollection> children;
888867
children_dispatch->QueryInterface<IHTMLDOMChildrenCollection>(&children);
889868
if (!!children) {
890-
long childrenCount = 0;
891-
children->get_length(&childrenCount);
892-
for (long i = 0; i < childrenCount; ++i) {
893-
CComPtr<IDispatch> childDispatch;
894-
children->item(i, &childDispatch);
869+
long children_count = 0;
870+
children->get_length(&children_count);
871+
for (long i = 0; i < children_count; ++i) {
872+
CComPtr<IDispatch> child_dispatch;
873+
children->item(i, &child_dispatch);
895874
CComPtr<IHTMLElement> child;
896-
childDispatch->QueryInterface(&child);
875+
child_dispatch->QueryInterface(&child);
897876
if (child != NULL) {
898-
Element childElement(child, this->containing_window_handle_);
899-
std::vector<LocationInfo> child_frame_locations;
900-
int result = childElement.GetLocation(location, &child_frame_locations);
877+
int result = WD_SUCCESS;
878+
Element child_element(child, this->containing_window_handle_);
879+
if (frame_locations == nullptr) {
880+
result = child_element.GetLocation(location, nullptr);
881+
} else {
882+
std::vector<LocationInfo> child_frame_locations;
883+
result = child_element.GetLocation(location, &child_frame_locations);
884+
}
901885
if (result == WD_SUCCESS) {
902886
return result;
903887
}
@@ -917,7 +901,7 @@ int Element::GetLocation(LocationInfo* location, std::vector<LocationInfo>* fram
917901
long h = bottom - top;
918902

919903
bool element_is_in_frame = this->AppendFrameDetails(frame_locations);
920-
if (!hasAbsolutePositionReadyToReturn) {
904+
if (!has_absolute_position_ready_to_return) {
921905
// On versions of IE prior to 8 on Vista, if the element is out of the
922906
// viewport this would seem to return 0,0,0,0. IE 8 returns position in
923907
// the DOM regardless of whether it's in the browser viewport.
@@ -991,6 +975,10 @@ bool Element::RectHasNonZeroDimensions(IHTMLRect* rect) {
991975
bool Element::AppendFrameDetails(std::vector<LocationInfo>* frame_locations) {
992976
LOG(TRACE) << "Entering Element::GetFrameDetails";
993977

978+
if (frame_locations == nullptr) {
979+
return false;
980+
}
981+
994982
CComPtr<IHTMLDocument2> owner_doc;
995983
int status_code = this->GetContainingDocument(true, &owner_doc);
996984
if (status_code != WD_SUCCESS) {

0 commit comments

Comments
 (0)