bool Element::GetFrameDetails(LocationInfo* location, std::vector<LocationInfo>* frame_locations) { LOG(TRACE) << "Entering Element::GetFrameDetails"; CComPtr<IHTMLDocument2> owner_doc; int status_code = this->GetContainingDocument(true, &owner_doc); if (status_code != WD_SUCCESS) { LOG(WARN) << "Unable to get containing document"; return false; } CComPtr<IHTMLWindow2> owner_doc_window; HRESULT hr = owner_doc->get_parentWindow(&owner_doc_window); if (!owner_doc_window) { LOG(WARN) << "Unable to get parent window, call to IHTMLDocument2::get_parentWindow failed"; return false; } // Get the parent window to the current window, where "current window" is // the window containing the parent document of this element. If that parent // window exists, and it is not the same as the current window, we assume // this element exists inside a frame or iframe. If it is in a frame, get // the parent document containing the frame, so we can get the information // about the frame or iframe element hosting the document of this element. CComPtr<IHTMLWindow2> parent_window; hr = owner_doc_window->get_parent(&parent_window); if (parent_window && !owner_doc_window.IsEqualObject(parent_window)) { LOG(DEBUG) << "Element is in a frame."; CComPtr<IHTMLDocument2> parent_doc; status_code = this->GetDocumentFromWindow(parent_window, &parent_doc); CComPtr<IHTMLFramesCollection2> frames; hr = parent_doc->get_frames(&frames); long frame_count(0); hr = frames->get_length(&frame_count); CComVariant index; index.vt = VT_I4; for (long i = 0; i < frame_count; ++i) { // See if the document in each frame is this element's // owner document. index.lVal = i; CComVariant result; hr = frames->item(&index, &result); CComPtr<IHTMLWindow2> frame_window; result.pdispVal->QueryInterface<IHTMLWindow2>(&frame_window); if (!frame_window) { // Frame is not an HTML frame. continue; } CComPtr<IHTMLDocument2> frame_doc; status_code = this->GetDocumentFromWindow(frame_window, &frame_doc); if (frame_doc.IsEqualObject(owner_doc)) { // The document in this frame *is* this element's owner // document. Get the frameElement property of the document's // containing window (which is itself an HTML element, either // a frame or an iframe). Then get the x and y coordinates of // that frame element. // N.B. We must use JavaScript here, as directly using // IHTMLWindow4.get_frameElement() returns E_NOINTERFACE under // some circumstances. LOG(DEBUG) << "Located host frame. Attempting to get hosting element"; std::wstring script_source = L"(function(){ return function() { return arguments[0].frameElement };})();"; Script script_wrapper(frame_doc, script_source, 1); CComVariant window_variant(frame_window); script_wrapper.AddArgument(window_variant); status_code = script_wrapper.Execute(); CComPtr<IHTMLFrameBase> frame_base; if (status_code == WD_SUCCESS) { hr = script_wrapper.result().pdispVal->QueryInterface<IHTMLFrameBase>(&frame_base); if (FAILED(hr)) { LOG(WARN) << "Found the frame element, but could not QueryInterface to IHTMLFrameBase."; } } else { // Can't get the frameElement property, likely because the frames are from different // domains. So start at the parent document, and use getElementsByTagName to retrieve // all of the iframe elements (if there are no iframe elements, get the frame elements) // **** BIG HUGE ASSUMPTION!!! **** // The index of the frame from the document.frames collection will correspond to the // index into the collection of iframe/frame elements returned by getElementsByTagName. LOG(WARN) << "Attempting to get frameElement via JavaScript failed. " << "This usually means the frame is in a different domain than the parent frame. " << "Browser security against cross-site scripting attacks will not allow this. " << "Attempting alternative method."; long collection_count = 0; CComPtr<IDispatch> element_dispatch; CComPtr<IHTMLDocument3> doc; parent_doc->QueryInterface<IHTMLDocument3>(&doc); if (doc) { LOG(DEBUG) << "Looking for <iframe> elements in parent document."; CComBSTR iframe_tag_name = L"iframe"; CComPtr<IHTMLElementCollection> iframe_collection; hr = doc->getElementsByTagName(iframe_tag_name, &iframe_collection); hr = iframe_collection->get_length(&collection_count); if (collection_count != 0) { if (collection_count > index.lVal) { LOG(DEBUG) << "Found <iframe> elements in parent document, retrieving element" << index.lVal << "."; hr = iframe_collection->item(index, index, &element_dispatch); hr = element_dispatch->QueryInterface<IHTMLFrameBase>(&frame_base); } } else { LOG(DEBUG) << "No <iframe> elements, looking for <frame> elements in parent document."; CComBSTR frame_tag_name = L"frame"; CComPtr<IHTMLElementCollection> frame_collection; hr = doc->getElementsByTagName(frame_tag_name, &frame_collection); hr = frame_collection->get_length(&collection_count); if (collection_count > index.lVal) { LOG(DEBUG) << "Found <frame> elements in parent document, retrieving element" << index.lVal << "."; hr = frame_collection->item(index, index, &element_dispatch); hr = element_dispatch->QueryInterface<IHTMLFrameBase>(&frame_base); } } } else { LOG(WARN) << "QueryInterface of parent document to IHTMLDocument3 failed."; } } if (frame_base) { LOG(DEBUG) << "Successfully found frame hosting element"; LocationInfo frame_doc_info; bool doc_dimensions_success = DocumentHost::GetDocumentDimensions( frame_doc, &frame_doc_info); // Wrap the element so we can find its location. Note that // GetLocation() may recursively call into this method. CComPtr<IHTMLElement> frame_element; frame_base->QueryInterface<IHTMLElement>(&frame_element); Element element_wrapper(frame_element, this->containing_window_handle_); CComPtr<IHTMLStyle> style; frame_element->get_style(&style); LocationInfo frame_location = {}; status_code = element_wrapper.GetLocation(&frame_location, frame_locations); // Take the border of the frame element into account. // N.B. We don't have to do this for non-frame elements, // because the border is part of the hit-test region. For // finding offsets to get absolute position of elements // within frames, the origin of the frame document is offset // by the border width. CComPtr<IHTMLElement2> border_width_element; frame_element->QueryInterface<IHTMLElement2>(&border_width_element); long left_border_width = 0; border_width_element->get_clientLeft(&left_border_width); long top_border_width = 0; border_width_element->get_clientTop(&top_border_width); if (status_code == WD_SUCCESS) { // Take into account the presence of scrollbars in the frame. long frame_element_width = frame_location.width; long frame_element_height = frame_location.height; if (doc_dimensions_success) { if (frame_doc_info.height > frame_element_height) { int horizontal_scrollbar_height = ::GetSystemMetrics(SM_CYHSCROLL); frame_element_height -= horizontal_scrollbar_height; } if (frame_doc_info.width > frame_element_width) { int vertical_scrollbar_width = ::GetSystemMetrics(SM_CXVSCROLL); frame_element_width -= vertical_scrollbar_width; } } location->x = frame_location.x + left_border_width; location->y = frame_location.y + top_border_width; location->width = frame_element_width; location->height = frame_element_height; } return true; } } } } // If we reach here, the element isn't in a frame/iframe. return false; }
int Element::GetFrameOffset(long* x, long* y) { LOG(TRACE) << "Entering Element::GetFrameOffset"; CComPtr<IHTMLDocument2> owner_doc; int status_code = this->GetContainingDocument(true, &owner_doc); if (status_code != SUCCESS) { LOG(WARN) << "Unable to get containing document"; return status_code; } CComPtr<IHTMLWindow2> owner_doc_window; HRESULT hr = owner_doc->get_parentWindow(&owner_doc_window); if (!owner_doc_window) { LOG(WARN) << "Unable to get parent window, call to IHTMLDocument2::get_parentWindow failed"; return ENOSUCHDOCUMENT; } CComPtr<IHTMLWindow2> parent_window; hr = owner_doc_window->get_parent(&parent_window); if (parent_window && !owner_doc_window.IsEqualObject(parent_window)) { CComPtr<IHTMLDocument2> parent_doc; status_code = this->GetParentDocument(parent_window, &parent_doc); CComPtr<IHTMLFramesCollection2> frames; hr = parent_doc->get_frames(&frames); long frame_count(0); hr = frames->get_length(&frame_count); CComVariant index; index.vt = VT_I4; for (long i = 0; i < frame_count; ++i) { // See if the document in each frame is this element's // owner document. index.lVal = i; CComVariant result; hr = frames->item(&index, &result); CComQIPtr<IHTMLWindow2> frame_window(result.pdispVal); if (!frame_window) { // Frame is not an HTML frame. continue; } CComPtr<IHTMLDocument2> frame_doc; hr = frame_window->get_document(&frame_doc); if (frame_doc.IsEqualObject(owner_doc)) { // The document in this frame *is* this element's owner // document. Get the frameElement property of the document's // containing window (which is itself an HTML element, either // a frame or an iframe). Then get the x and y coordinates of // that frame element. std::wstring script_source = L"(function(){ return function() { return arguments[0].frameElement };})();"; Script script_wrapper(frame_doc, script_source, 1); CComVariant window_variant(frame_window); script_wrapper.AddArgument(window_variant); script_wrapper.Execute(); CComQIPtr<IHTMLElement> frame_element(script_wrapper.result().pdispVal); // Wrap the element so we can find its location. Element element_wrapper(frame_element, this->containing_window_handle_); long frame_x, frame_y, frame_width, frame_height; status_code = element_wrapper.GetLocation(&frame_x, &frame_y, &frame_width, &frame_height); if (status_code == SUCCESS) { *x = frame_x; *y = frame_y; } break; } } } return SUCCESS; }