/*----------------------------------------------------------------------------- Locate the first HTML element on the DOM of any of the browsers that matches the attribute we're looking for The attrVal contains a concatenated attribute and value in attribute=value form -----------------------------------------------------------------------------*/ CComPtr<IHTMLElement> CPagetestBase::FindDomElementByAttribute(CString attrVal) { CComPtr<IHTMLElement> result; CString attribute = _T("id"); CString value = attrVal; value.Trim(); // = or ' are delimiters for a direct full comparison int index = attrVal.Find('='); if( index == -1 ) index = attrVal.Find('\''); // < is the delimiter to compare the left of the string int index2 = attrVal.Find('<'); attrOperator op = equal; if( index2 != -1 && (index2 < index || index == -1) ) { index = index2; op = left; } // ^ is the delimiter to do a substring match int index3 = attrVal.Find('^'); if( index3 != -1 && (index3 < index || index == -1) && (index3 < index2 || index2 == -1) ) { index = index3; op = mid; } if( index != -1 ) { attribute = attrVal.Left(index); value = attrVal.Right(attrVal.GetLength() - index - 1); value.Trim(); value.Trim(_T("\"")); // allow for the value to be in quotes } // see if there is a specific tag we're looking for - i.e. div:xxx=yy or A:xxx=yy index = attribute.Find(':'); CString tag; if( index != -1 ) { tag = attribute.Left(index); attribute = attribute.Right(attribute.GetLength() - index - 1); } attribute.Trim(); result = FindDomElementByAttribute(tag, attribute, value, op); return result; }
/*----------------------------------------------------------------------------- Locate the first HTML element on the DOM of any of the browsers that matches the attribute we're looking for -----------------------------------------------------------------------------*/ CComPtr<IHTMLElement> CPagetestBase::FindDomElementByAttribute(CString &tag, CString &attribute, CString &value, attrOperator &op) { CComPtr<IHTMLElement> result; POSITION pos = browsers.GetHeadPosition(); while(pos && !result) { CBrowserTracker tracker = browsers.GetNext(pos); if( tracker.threadId == GetCurrentThreadId() && tracker.browser ) { CComPtr<IDispatch> spDoc; if( SUCCEEDED(tracker.browser->get_Document(&spDoc)) && spDoc ) { CComQIPtr<IHTMLDocument2> doc = spDoc; if( doc ) result = FindDomElementByAttribute(tag, attribute, value, op, doc); } } } return result; }
/*----------------------------------------------------------------------------- Locate the first HTML element on the DOM in the given document that matches the attribute we're looking for This will recursively search any frames within the document -----------------------------------------------------------------------------*/ CComPtr<IHTMLElement> CPagetestBase::FindDomElementByAttribute(CString &tag, CString &attribute, CString &value, attrOperator &op, CComPtr<IHTMLDocument2> doc) { CComPtr<IHTMLElement> result; CComBSTR attrib(attribute); bool innerText = false; bool innerHtml = false; bool sourceIndex = false; if( !attribute.CompareNoCase(_T("innerText")) ) innerText = true; else if( !attribute.CompareNoCase(_T("innerHtml")) ) innerHtml = true; else if( !attribute.CompareNoCase(_T("sourceIndex")) ) sourceIndex = true; // force class to className (it's a special case where the attribute is different on the DOM) if( !attribute.CompareNoCase(_T("class")) ) attribute = _T("className"); if( doc ) { // get all of the elements if( !result ) { bool ok = false; if( !sourceIndex && !innerText && !innerHtml && op == equal && tag.IsEmpty() && (!attribute.CompareNoCase(_T("id"))) ) { CComQIPtr<IHTMLDocument3> doc3 = doc; if( doc3 ) { ok = true; _bstr_t val = value; doc3->getElementById(val, &result); } } if( !ok ) { // have to manually walk all of the elements CComPtr<IHTMLElementCollection> coll; ok = false; // if we're looking for name, short-cut and do a direct search if( !tag.IsEmpty() || (!attribute.CompareNoCase(_T("name")) && op == equal) ) { CComQIPtr<IHTMLDocument3> doc3 = doc; if( doc3 ) { ok = true; if( !attribute.CompareNoCase(_T("name")) && op == equal ) { _bstr_t name = value; doc3->getElementsByName(name, &coll); } else if( !tag.IsEmpty() ) { _bstr_t tagName = tag; doc3->getElementsByTagName(tagName, &coll); } } } if( !ok ) if( SUCCEEDED(doc->get_all(&coll)) ) ok = true; if( ok && coll ) { long count = 0; if( SUCCEEDED(coll->get_length(&count)) ) { for( long i = 0; i < count && !result; i++ ) { _variant_t index = i; CComPtr<IDispatch> item; if( SUCCEEDED(coll->item(index, index, &item)) && item ) { CComQIPtr<IHTMLElement> element = item; if( element ) { ok = false; // see if we're looking for a particular element type if( tag.IsEmpty() ) ok = true; else { _bstr_t elementTag; if( SUCCEEDED(element->get_tagName(elementTag.GetAddress())) ) { CString elTag = elementTag; if( !tag.CompareNoCase(elTag) ) ok = true; } } if( ok ) { _variant_t varVal; _bstr_t text; if( sourceIndex ) { long index; if( SUCCEEDED(element->get_sourceIndex(&index)) ) { long lValue = _ttol(value); if( index == lValue ) result = element; } } else { if( innerText ) element->get_innerText(text.GetAddress()); else if (innerHtml) element->get_innerHTML(text.GetAddress()); else if( SUCCEEDED(element->getAttribute(attrib, 0, &varVal)) ) { if( varVal.vt != VT_EMPTY && varVal.vt != VT_NULL && varVal.vt != VT_ERROR ) text = (_bstr_t)varVal; } CString val = text; val.Trim(); if( val.GetLength() ) { switch( op ) { case equal: { if( val == value ) result = element; }break; case left: { if( val.Left(value.GetLength()) == value ) result = element; }break; case mid: { if( val.Find(value) > -1 ) result = element; }break; } } } } } } } } } } } // recursively check in any iFrames if( !result ) { // walk all of the frames on the document CComPtr<IHTMLFramesCollection2> frames; if( SUCCEEDED(doc->get_frames(&frames)) && frames ) { // for each frame, walk all of the elements in the frame long count = 0; if( SUCCEEDED(frames->get_length(&count)) ) { for( long i = 0; i < count && !result; i++ ) { _variant_t index = i; _variant_t varFrame; if( SUCCEEDED(frames->item(&index, &varFrame)) ) { CComQIPtr<IHTMLWindow2> window(varFrame); if( window ) { CComQIPtr<IHTMLDocument2> frameDoc; frameDoc = HtmlWindowToHtmlDocument(window); if( frameDoc ) result = FindDomElementByAttribute(tag, attribute, value, op, frameDoc); } } } } } } } return result; }
/*----------------------------------------------------------------------------- Locate the first HTML element on the DOM in the given document that matches the attribute we're looking for This will recursively search any frames within the document -----------------------------------------------------------------------------*/ CComPtr<IHTMLElement> CPagetestBase::FindDomElementByAttribute(CString &tag, CString &attribute, CString &value, attrOperator &op, CComPtr<IHTMLDocument2> doc) { CComPtr<IHTMLElement> result; CComBSTR attrib(attribute); bool innerText = false; bool innerHtml = false; bool sourceIndex = false; if( !attribute.CompareNoCase(_T("innerText")) ) innerText = true; else if( !attribute.CompareNoCase(_T("innerHtml")) ) innerHtml = true; else if( !attribute.CompareNoCase(_T("sourceIndex")) ) sourceIndex = true; // force class to className (it's a special case where the attribute is different on the DOM) if( !attribute.CompareNoCase(_T("class")) ) attribute = _T("className"); if( doc ) { // get all of the elements if( !result ) { bool ok = false; if( !sourceIndex && !innerText && !innerHtml && op == equal && tag.IsEmpty() && (!attribute.CompareNoCase(_T("id"))) ) { CComQIPtr<IHTMLDocument3> doc3 = doc; if( doc3 ) { ok = true; _bstr_t val = value; doc3->getElementById(val, &result); } } if( !ok ) { // have to manually walk all of the elements CComPtr<IHTMLElementCollection> coll; ok = false; // if we're looking for name, short-cut and do a direct search if( !tag.IsEmpty() || (!attribute.CompareNoCase(_T("name")) && op == equal) ) { CComQIPtr<IHTMLDocument3> doc3 = doc; if( doc3 ) { ok = true; if( !attribute.CompareNoCase(_T("name")) && op == equal ) { _bstr_t name = value; doc3->getElementsByName(name, &coll); } else if( !tag.IsEmpty() ) { _bstr_t tagName = tag; doc3->getElementsByTagName(tagName, &coll); } } } if( !ok ) if( SUCCEEDED(doc->get_all(&coll)) ) ok = true; if( ok && coll ) { long count = 0; if( SUCCEEDED(coll->get_length(&count)) ) { for( long i = 0; i < count && !result; i++ ) { _variant_t index = i; CComPtr<IDispatch> item; if( SUCCEEDED(coll->item(index, index, &item)) && item ) { CComQIPtr<IHTMLElement> element = item; if( element ) { ok = false; // see if we're looking for a particular element type if( tag.IsEmpty() ) ok = true; else { _bstr_t elementTag; if( SUCCEEDED(element->get_tagName(elementTag.GetAddress())) ) { CString elTag = elementTag; if( !tag.CompareNoCase(elTag) ) ok = true; } } if( ok ) { _variant_t varVal; _bstr_t text; if( sourceIndex ) { long index; if( SUCCEEDED(element->get_sourceIndex(&index)) ) { long lValue = _ttol(value); if( index == lValue ) result = element; } } else { if( innerText ) element->get_innerText(text.GetAddress()); else if (innerHtml) element->get_innerHTML(text.GetAddress()); else if( SUCCEEDED(element->getAttribute(attrib, 0, &varVal)) ) { if( varVal.vt != VT_EMPTY && varVal.vt != VT_NULL && varVal.vt != VT_ERROR ) text = (_bstr_t)varVal; } CString val = text; val.Trim(); if( val.GetLength() ) { switch( op ) { case equal: { if( val == value ) result = element; }break; case left: { if( val.Left(value.GetLength()) == value ) result = element; }break; case mid: { if( val.Find(value) > -1 ) result = element; }break; } } } } } } } } } } } // walk the IFrames using OLE (to bypass security blocks) if( !result ) { // walk all of the frames on the document // this is a little complicated because we need to bypass cross site scripting security CComQIPtr<IOleContainer> ole(doc); if(ole) { CComPtr<IEnumUnknown> objects; // Get an enumerator for the frames if( SUCCEEDED(ole->EnumObjects(OLECONTF_EMBEDDINGS, &objects)) && objects ) { IUnknown* pUnk; ULONG uFetched; // Enumerate all the frames while( !result && S_OK == objects->Next(1, &pUnk, &uFetched) ) { // QI for IWebBrowser here to see if we have an embedded browser CComQIPtr<IWebBrowser2> browser(pUnk); pUnk->Release(); if (browser) { CComPtr<IDispatch> disp; if( SUCCEEDED(browser->get_Document(&disp)) && disp ) { CComQIPtr<IHTMLDocument2> frameDoc(disp); if (frameDoc) result = FindDomElementByAttribute(tag, attribute, value, op, frameDoc); } } } } } } // walk the IFrames diriectly (the OLE way doesn't appear to always work) if( !result ) { // walk all of the frames on the document CComPtr<IHTMLFramesCollection2> frames; if( SUCCEEDED(doc->get_frames(&frames)) && frames ) { // for each frame, walk all of the elements in the frame long count = 0; if( SUCCEEDED(frames->get_length(&count)) ) { for( long i = 0; i < count && !result; i++ ) { _variant_t index = i; _variant_t varFrame; if( SUCCEEDED(frames->item(&index, &varFrame)) ) { CComQIPtr<IHTMLWindow2> window(varFrame); if( window ) { CComQIPtr<IHTMLDocument2> frameDoc; if( SUCCEEDED(window->get_document(&frameDoc)) && frameDoc ) result = FindDomElementByAttribute(tag, attribute, value, op, frameDoc); } } } } } } } return result; }