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 Script::Execute() { LOG(TRACE) << "Entering Script::Execute"; CComVariant result = L""; CComBSTR error_description = L""; if (this->script_engine_host_ == NULL) { LOG(WARN) << "Script engine host is NULL"; return ENOSUCHDOCUMENT; } CComVariant temp_function; if (!this->CreateAnonymousFunction(&temp_function)) { LOG(WARN) << "Cannot create anonymous function"; return EUNEXPECTEDJSERROR; } if (temp_function.vt != VT_DISPATCH) { LOG(DEBUG) << "No return value that we care about"; return WD_SUCCESS; } // Grab the "call" method out of the returned function DISPID call_member_id; OLECHAR FAR* call_member_name = L"call"; HRESULT hr = temp_function.pdispVal->GetIDsOfNames(IID_NULL, &call_member_name, 1, LOCALE_USER_DEFAULT, &call_member_id); if (FAILED(hr)) { LOGHR(WARN, hr) << "Cannot locate call method on anonymous function"; return EUNEXPECTEDJSERROR; } CComPtr<IHTMLWindow2> win; hr = this->script_engine_host_->get_parentWindow(&win); if (FAILED(hr)) { LOGHR(WARN, hr) << "Cannot get parent window, IHTMLDocument2::get_parentWindow failed"; return EUNEXPECTEDJSERROR; } // IDispatch::Invoke() expects the arguments to be passed into it // in reverse order. To accomplish this, we create a new variant // array of size n + 1 where n is the number of arguments we have. // we copy each element of arguments_array_ into the new array in // reverse order, and add an extra argument, the window object, // to the end of the array to use as the "this" parameter for the // function invocation. size_t arg_count = this->argument_array_.size(); std::vector<CComVariant> argument_array_copy(arg_count + 1); CComVariant window_variant(win); argument_array_copy[arg_count].Copy(&window_variant); for (size_t index = 0; index < arg_count; ++index) { argument_array_copy[arg_count - 1 - index].Copy(&this->argument_array_[index]); } DISPPARAMS call_parameters = { 0 }; memset(&call_parameters, 0, sizeof call_parameters); call_parameters.cArgs = static_cast<unsigned int>(argument_array_copy.size()); call_parameters.rgvarg = &argument_array_copy[0]; int return_code = WD_SUCCESS; EXCEPINFO exception; memset(&exception, 0, sizeof exception); hr = temp_function.pdispVal->Invoke(call_member_id, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &call_parameters, &result, &exception, 0); if (FAILED(hr)) { if (DISP_E_EXCEPTION == hr) { error_description = exception.bstrDescription ? exception.bstrDescription : L"EUNEXPECTEDJSERROR"; CComBSTR error_source(exception.bstrSource ? exception.bstrSource : L"EUNEXPECTEDJSERROR"); LOG(INFO) << "Exception message was: '" << error_description << "'"; LOG(INFO) << "Exception source was: '" << error_source << "'"; } else { LOGHR(DEBUG, hr) << "Failed to execute anonymous function, no exception information retrieved"; } result.Clear(); result.vt = VT_BSTR; result.bstrVal = error_description; return_code = EUNEXPECTEDJSERROR; } this->result_.Copy(&result); return return_code; }
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; }