Skip to content

Commit 2bf3e92

Browse files
committed
Adding detection of intercepted element clicks to IE
This brings the IE driver into alignment with the Chrome and Firefox (geckodriver) drivers, in that it detects when an element attempting to be clicked on is covered in the z-order by another element. Do note that this may cause different behavior from previous versions of the IE driver for WebDriver code that runs only on IE. For code that runs cross-browser, this should now yield the same behavior across platforms.
1 parent 46442ac commit 2bf3e92

File tree

3 files changed

+79
-0
lines changed

3 files changed

+79
-0
lines changed

cpp/iedriver/CommandHandlers/ClickElementCommandHandler.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,19 @@ void ClickElementCommandHandler::ExecuteInternal(const IECommandExecutor& execut
120120
return;
121121
}
122122

123+
LocationInfo click_location = {};
124+
std::string obscuring_element_description;
125+
bool obscured = element_wrapper->IsObscured(&click_location,
126+
&obscuring_element_description);
127+
if (obscured) {
128+
std::string error_msg = StringUtilities::Format("Element not clickable at point (%d,%d). Other element would receive the click: %s",
129+
click_location.x,
130+
click_location.y,
131+
obscuring_element_description.c_str());
132+
response->SetErrorResponse(ERROR_ELEMENT_CLICK_INTERCEPTED, error_msg);
133+
return;
134+
}
135+
123136
IECommandExecutor& mutable_executor = const_cast<IECommandExecutor&>(executor);
124137
status_code = mutable_executor.input_manager()->PerformInputSequence(browser_wrapper, actions);
125138
browser_wrapper->set_wait_required(true);

cpp/iedriver/Element.cpp

+64
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,70 @@ bool Element::IsInteractable() {
188188
return result;
189189
}
190190

191+
bool Element::IsObscured(LocationInfo* click_location,
192+
std::string* obscuring_element_description) {
193+
CComPtr<ISVGElement> svg_element;
194+
HRESULT hr = this->element_->QueryInterface<ISVGElement>(&svg_element);
195+
if (SUCCEEDED(hr) && svg_element != NULL) {
196+
// SVG elements can have complex paths making them non-hierarchical
197+
// when drawn. We'll just assume the user knows what they're doing
198+
// and bail on this test here.
199+
return false;
200+
}
201+
202+
bool is_obscured = false;
203+
int status_code = this->GetStaticClickLocation(click_location);
204+
205+
CComPtr<IHTMLDocument2> doc;
206+
this->GetContainingDocument(false, &doc);
207+
208+
CComPtr<IHTMLDocument8> elements_doc;
209+
hr = doc.QueryInterface<IHTMLDocument8>(&elements_doc);
210+
if (FAILED(hr)) {
211+
LOGHR(WARN, hr) << "QueryInterface for IHTMLDocument8 failed";
212+
}
213+
214+
CComPtr<IHTMLDOMChildrenCollection> elements_hit;
215+
hr = elements_doc->elementsFromPoint(static_cast<float>(click_location->x),
216+
static_cast<float>(click_location->y),
217+
&elements_hit);
218+
if (SUCCEEDED(hr) && elements_hit != NULL) {
219+
long element_count;
220+
elements_hit->get_length(&element_count);
221+
for (long index = 0; index < element_count; ++index) {
222+
CComPtr<IDispatch> dispatch_in_list;
223+
elements_hit->item(index, &dispatch_in_list);
224+
225+
CComPtr<IHTMLElement> element_in_list;
226+
hr = dispatch_in_list->QueryInterface<IHTMLElement>(&element_in_list);
227+
bool are_equal = element_in_list.IsEqualObject(this->element_);
228+
if (index == 0) {
229+
// Return the top-most element in the event we find an obscuring
230+
// element in the tree between this element and the top-most one.
231+
// Note that since it's the top-most element, it will have no
232+
// descendants, so its outerHTML property will contain only itself.
233+
CComBSTR outer_html_bstr;
234+
hr = element_in_list->get_outerHTML(&outer_html_bstr);
235+
std::wstring outer_html = outer_html_bstr;
236+
*obscuring_element_description = StringUtilities::ToString(outer_html);
237+
}
238+
239+
240+
VARIANT_BOOL is_child;
241+
hr = this->element_->contains(element_in_list, &is_child);
242+
VARIANT_BOOL is_ancestor;
243+
hr = element_in_list->contains(this->element_, &is_ancestor);
244+
is_obscured = is_obscured ||
245+
(is_child != VARIANT_TRUE && is_ancestor != VARIANT_TRUE);
246+
if (is_obscured || are_equal) {
247+
break;
248+
}
249+
}
250+
}
251+
252+
return is_obscured;
253+
}
254+
191255
bool Element::IsEditable() {
192256
LOG(TRACE) << "Entering Element::IsEditable";
193257

cpp/iedriver/Element.h

+2
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class Element {
6666
bool IsEditable(void);
6767
bool IsAttachedToDom(void);
6868
bool IsDocumentFocused(IHTMLDocument2* focused_doc);
69+
bool IsObscured(LocationInfo* click_location,
70+
std::string* obscuring_element_description);
6971

7072
std::string element_id(void) const { return this->element_id_; }
7173
IHTMLElement* element(void) { return this->element_; }

0 commit comments

Comments
 (0)