Skip to content

Commit e72d039

Browse files
committed
Making IE driver work with <map> and <area> elements
1 parent fed468f commit e72d039

File tree

2 files changed

+208
-84
lines changed

2 files changed

+208
-84
lines changed

cpp/iedriver/Element.cpp

Lines changed: 207 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,119 @@ bool Element::IsSelected() {
833833
return selected;
834834
}
835835

836+
bool Element::IsImageMap(LocationInfo* location) {
837+
CComPtr<IHTMLElement> map_element;
838+
CComPtr<IHTMLAreaElement> area_element;
839+
CComPtr<IHTMLMapElement> map_element_candidate;
840+
this->element_->QueryInterface<IHTMLMapElement>(&map_element_candidate);
841+
if (map_element_candidate == NULL) {
842+
this->element_->QueryInterface<IHTMLAreaElement>(&area_element);
843+
if (area_element) {
844+
this->element_->get_parentElement(&map_element);
845+
if (map_element) {
846+
map_element->QueryInterface<IHTMLMapElement>(&map_element_candidate);
847+
}
848+
}
849+
}
850+
851+
if (map_element_candidate && map_element) {
852+
CComBSTR name_bstr;
853+
map_element_candidate->get_name(&name_bstr);
854+
CComBSTR img_selector = L"*[usemap='#";
855+
img_selector.Append(name_bstr);
856+
img_selector.Append(L"']");
857+
858+
CComPtr<IDispatch> doc_dispatch;
859+
map_element->get_document(&doc_dispatch);
860+
861+
CComPtr<IDocumentSelector> doc;
862+
doc_dispatch->QueryInterface<IDocumentSelector>(&doc);
863+
if (doc) {
864+
CComPtr<IHTMLElement> img_element;
865+
doc->querySelector(img_selector, &img_element);
866+
if (img_element) {
867+
CComPtr<IHTMLElement2> rect_element;
868+
img_element->QueryInterface<IHTMLElement2>(&rect_element);
869+
if (rect_element) {
870+
CComPtr<IHTMLRect> rect;
871+
rect_element->getBoundingClientRect(&rect);
872+
RECT img_rect;
873+
rect->get_left(&img_rect.left);
874+
rect->get_top(&img_rect.top);
875+
rect->get_right(&img_rect.right);
876+
rect->get_bottom(&img_rect.bottom);
877+
878+
CComBSTR shape;
879+
area_element->get_shape(&shape);
880+
shape.ToLower();
881+
if (shape == L"default") {
882+
location->x = img_rect.left;
883+
location->y = img_rect.top;
884+
location->width = img_rect.right - img_rect.left;
885+
location->height = img_rect.bottom - img_rect.top;
886+
return true;
887+
}
888+
889+
CComBSTR coords_bstr;
890+
area_element->get_coords(&coords_bstr);
891+
std::wstring coords(coords_bstr);
892+
std::vector<std::wstring> individual;
893+
StringUtilities::Split(coords, L",", &individual);
894+
RECT area_rect = { 0, 0, 0, 0 };
895+
if (shape == L"rect" && individual.size() == 4) {
896+
area_rect.left = std::stol(individual.at(0).c_str(), 0, 10);
897+
area_rect.top = std::stol(individual.at(1).c_str(), 0, 10);
898+
area_rect.right = std::stol(individual.at(2).c_str(), 0, 10);
899+
area_rect.bottom = std::stol(individual.at(3).c_str(), 0, 10);
900+
}
901+
else if ((shape == L"circle" || shape == "circ") && individual.size() == 3) {
902+
long center_x = std::stol(individual.at(0), 0, 10);
903+
long center_y = std::stol(individual.at(1), 0, 10);
904+
long radius = std::stol(individual.at(2), 0, 10);
905+
area_rect.left = center_x - radius;
906+
area_rect.top = center_y - radius;
907+
area_rect.right = center_x + radius;
908+
area_rect.bottom = center_y + radius;
909+
}
910+
else if ((shape == L"poly" || shape == L"polygon") && individual.size() > 2) {
911+
long min_x = std::stol(individual.at(0), 0, 10);
912+
long min_y = std::stol(individual.at(1), 0, 10);
913+
long max_x = min_x;
914+
long max_y = min_y;
915+
for (size_t i = 2; i + 1 < individual.size(); i += 2) {
916+
long next_x = std::stol(individual.at(i), 0, 10);
917+
long next_y = std::stol(individual.at(i + 1), 0, 10);
918+
min_x = min(min_x, next_x);
919+
max_x = max(max_x, next_x);
920+
min_y = min(min_y, next_y);
921+
max_y = max(max_y, next_y);
922+
}
923+
area_rect.left = min_x;
924+
area_rect.bottom = min_y;
925+
area_rect.right = max_x;
926+
area_rect.bottom = max_y;
927+
}
928+
else {
929+
// Invalid shape value or coordinate values. Not modifying location.
930+
return false;
931+
}
932+
933+
long img_width = img_rect.right - img_rect.left;
934+
long img_height = img_rect.bottom - img_rect.top;
935+
long area_width = area_rect.right - area_rect.left;
936+
long area_height = area_rect.bottom - area_rect.top;
937+
location->x = img_rect.left + min(max(area_rect.left, 0), img_width);
938+
location->y = img_rect.top + min(max(area_rect.top, 0), img_height);
939+
location->width = min(area_width, img_width - location->x);
940+
location->height = min(area_height, img_height - location->y);
941+
return true;
942+
}
943+
}
944+
}
945+
}
946+
return false;
947+
}
948+
836949
int Element::GetLocation(LocationInfo* location,
837950
std::vector<LocationInfo>* frame_locations) {
838951
LOG(TRACE) << "Entering Element::GetLocation";
@@ -846,102 +959,112 @@ int Element::GetLocation(LocationInfo* location,
846959
return EOBSOLETEELEMENT;
847960
}
848961

849-
// If this element is inline, we need to check whether we should
850-
// use getBoundingClientRect() or the first non-zero-sized rect returned
851-
// by getClientRects(). If the element is not inline, we can use
852-
// getBoundingClientRect() directly.
853-
CComPtr<IHTMLRect> rect;
854-
if (this->IsInline()) {
855-
CComPtr<IHTMLRectCollection> rects;
856-
hr = element2->getClientRects(&rects);
857-
long rect_count;
858-
rects->get_length(&rect_count);
859-
if (rect_count > 1) {
860-
LOG(DEBUG) << "Element is inline with multiple client rects, finding first non-zero sized client rect";
861-
for (long i = 0; i < rect_count; ++i) {
862-
CComVariant index(i);
863-
CComVariant rect_variant;
864-
hr = rects->item(&index, &rect_variant);
865-
if (SUCCEEDED(hr) && rect_variant.pdispVal) {
866-
CComPtr<IHTMLRect> qi_rect;
867-
rect_variant.pdispVal->QueryInterface<IHTMLRect>(&qi_rect);
868-
if (qi_rect) {
869-
rect = qi_rect;
870-
if (RectHasNonZeroDimensions(rect)) {
871-
// IE returns absolute positions in the page, rather than frame- and scroll-bound
872-
// positions, for clientRects (as opposed to boundingClientRects).
873-
has_absolute_position_ready_to_return = true;
874-
break;
962+
long top = 0, bottom = 0, left = 0, right = 0;
963+
LocationInfo map_location = { 0, 0, 0, 0 };
964+
if (this->IsImageMap(&map_location)) {
965+
left = map_location.x;
966+
top = map_location.y;
967+
right = map_location.x + map_location.width;
968+
bottom = map_location.y + map_location.height;
969+
} else {
970+
// If this element is inline, we need to check whether we should
971+
// use getBoundingClientRect() or the first non-zero-sized rect returned
972+
// by getClientRects(). If the element is not inline, we can use
973+
// getBoundingClientRect() directly.
974+
CComPtr<IHTMLRect> rect;
975+
if (this->IsInline()) {
976+
CComPtr<IHTMLRectCollection> rects;
977+
hr = element2->getClientRects(&rects);
978+
long rect_count;
979+
rects->get_length(&rect_count);
980+
if (rect_count > 1) {
981+
LOG(DEBUG) << "Element is inline with multiple client rects, finding first non-zero sized client rect";
982+
for (long i = 0; i < rect_count; ++i) {
983+
CComVariant index(i);
984+
CComVariant rect_variant;
985+
hr = rects->item(&index, &rect_variant);
986+
if (SUCCEEDED(hr) && rect_variant.pdispVal) {
987+
CComPtr<IHTMLRect> qi_rect;
988+
rect_variant.pdispVal->QueryInterface<IHTMLRect>(&qi_rect);
989+
if (qi_rect) {
990+
rect = qi_rect;
991+
if (RectHasNonZeroDimensions(rect)) {
992+
// IE returns absolute positions in the page, rather than frame- and scroll-bound
993+
// positions, for clientRects (as opposed to boundingClientRects).
994+
has_absolute_position_ready_to_return = true;
995+
break;
996+
}
875997
}
876998
}
877999
}
8781000
}
879-
} else {
880-
LOG(DEBUG) << "Element is inline with one client rect, using IHTMLElement2::getBoundingClientRect";
1001+
else {
1002+
LOG(DEBUG) << "Element is inline with one client rect, using IHTMLElement2::getBoundingClientRect";
1003+
hr = element2->getBoundingClientRect(&rect);
1004+
}
1005+
}
1006+
else {
1007+
LOG(DEBUG) << "Element is a block element, using IHTMLElement2::getBoundingClientRect";
8811008
hr = element2->getBoundingClientRect(&rect);
1009+
if (this->HasFirstChildTextNodeOfMultipleChildren()) {
1010+
LOG(DEBUG) << "Element has multiple children, but the first child is a text node, using text node boundaries";
1011+
// Note that since subsequent statements in this method use the HTMLRect
1012+
// object, we will update that object with the values of the text node.
1013+
LocationInfo text_node_location;
1014+
this->GetTextBoundaries(&text_node_location);
1015+
rect->put_left(text_node_location.x);
1016+
rect->put_top(text_node_location.y);
1017+
rect->put_right(text_node_location.x + text_node_location.width);
1018+
rect->put_bottom(text_node_location.y + text_node_location.height);
1019+
}
8821020
}
883-
} else {
884-
LOG(DEBUG) << "Element is a block element, using IHTMLElement2::getBoundingClientRect";
885-
hr = element2->getBoundingClientRect(&rect);
886-
if (this->HasFirstChildTextNodeOfMultipleChildren()) {
887-
LOG(DEBUG) << "Element has multiple children, but the first child is a text node, using text node boundaries";
888-
// Note that since subsequent statements in this method use the HTMLRect
889-
// object, we will update that object with the values of the text node.
890-
LocationInfo text_node_location;
891-
this->GetTextBoundaries(&text_node_location);
892-
rect->put_left(text_node_location.x);
893-
rect->put_top(text_node_location.y);
894-
rect->put_right(text_node_location.x + text_node_location.width);
895-
rect->put_bottom(text_node_location.y + text_node_location.height);
1021+
if (FAILED(hr)) {
1022+
LOGHR(WARN, hr) << "Cannot figure out where the element is on screen, client rect retrieval failed";
1023+
return EUNHANDLEDERROR;
8961024
}
897-
}
898-
if (FAILED(hr)) {
899-
LOGHR(WARN, hr) << "Cannot figure out where the element is on screen, client rect retrieval failed";
900-
return EUNHANDLEDERROR;
901-
}
902-
903-
// If the rect of the element has zero width and height, check its
904-
// children to see if any of them have width and height, in which
905-
// case, this element will be visible.
906-
if (!RectHasNonZeroDimensions(rect)) {
907-
LOG(DEBUG) << "Element has client rect with zero dimension, checking children for non-zero dimension client rects";
908-
CComPtr<IHTMLDOMNode> node;
909-
element2->QueryInterface(&node);
910-
CComPtr<IDispatch> children_dispatch;
911-
node->get_childNodes(&children_dispatch);
912-
CComPtr<IHTMLDOMChildrenCollection> children;
913-
children_dispatch->QueryInterface<IHTMLDOMChildrenCollection>(&children);
914-
if (!!children) {
915-
long children_count = 0;
916-
children->get_length(&children_count);
917-
for (long i = 0; i < children_count; ++i) {
918-
CComPtr<IDispatch> child_dispatch;
919-
children->item(i, &child_dispatch);
920-
CComPtr<IHTMLElement> child;
921-
child_dispatch->QueryInterface(&child);
922-
if (child != NULL) {
923-
int result = WD_SUCCESS;
924-
Element child_element(child, this->containing_window_handle_);
925-
if (frame_locations == nullptr) {
926-
result = child_element.GetLocation(location, nullptr);
927-
} else {
928-
std::vector<LocationInfo> child_frame_locations;
929-
result = child_element.GetLocation(location, &child_frame_locations);
930-
}
931-
if (result == WD_SUCCESS) {
932-
return result;
1025+
1026+
// If the rect of the element has zero width and height, check its
1027+
// children to see if any of them have width and height, in which
1028+
// case, this element will be visible.
1029+
if (!RectHasNonZeroDimensions(rect)) {
1030+
LOG(DEBUG) << "Element has client rect with zero dimension, checking children for non-zero dimension client rects";
1031+
CComPtr<IHTMLDOMNode> node;
1032+
element2->QueryInterface(&node);
1033+
CComPtr<IDispatch> children_dispatch;
1034+
node->get_childNodes(&children_dispatch);
1035+
CComPtr<IHTMLDOMChildrenCollection> children;
1036+
children_dispatch->QueryInterface<IHTMLDOMChildrenCollection>(&children);
1037+
if (!!children) {
1038+
long children_count = 0;
1039+
children->get_length(&children_count);
1040+
for (long i = 0; i < children_count; ++i) {
1041+
CComPtr<IDispatch> child_dispatch;
1042+
children->item(i, &child_dispatch);
1043+
CComPtr<IHTMLElement> child;
1044+
child_dispatch->QueryInterface(&child);
1045+
if (child != NULL) {
1046+
int result = WD_SUCCESS;
1047+
Element child_element(child, this->containing_window_handle_);
1048+
if (frame_locations == nullptr) {
1049+
result = child_element.GetLocation(location, nullptr);
1050+
}
1051+
else {
1052+
std::vector<LocationInfo> child_frame_locations;
1053+
result = child_element.GetLocation(location, &child_frame_locations);
1054+
}
1055+
if (result == WD_SUCCESS) {
1056+
return result;
1057+
}
9331058
}
9341059
}
9351060
}
9361061
}
937-
}
938-
939-
long top = 0, bottom = 0, left = 0, right = 0;
9401062

941-
rect->get_top(&top);
942-
rect->get_left(&left);
943-
rect->get_bottom(&bottom);
944-
rect->get_right(&right);
1063+
rect->get_top(&top);
1064+
rect->get_left(&left);
1065+
rect->get_bottom(&bottom);
1066+
rect->get_right(&right);
1067+
}
9451068

9461069
long w = right - left;
9471070
long h = bottom - top;

cpp/iedriver/Element.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ class Element {
9898
bool HasShadowRoot(void);
9999

100100
bool IsInline(void);
101+
bool IsImageMap(LocationInfo* location);
101102
static bool RectHasNonZeroDimensions(IHTMLRect* rect);
102103

103104
bool HasFirstChildTextNodeOfMultipleChildren(void);

0 commit comments

Comments
 (0)