// iforms.cpp : Implementation of CIntelliForms #include "priv.h" #include #include #include "hlframe.h" #include "iformsp.h" #include "shldisp.h" #include "opsprof.h" #include "resource.h" #include // {E161255A-37C3-11d2-BCAA-00C04FD929DB} static const GUID c_PStoreType = { 0xe161255a, 0x37c3, 0x11d2, { 0xbc, 0xaa, 0x0, 0xc0, 0x4f, 0xd9, 0x29, 0xdb } }; const TCHAR c_szIntelliForms[] = TEXT("Internet Explorer"); #define TF_IFORMS TF_CUSTOM2 // ALLOW_SHELLUIOC_HOST code will allow us to host intelliforms // from the Shell UI OC (shuioc.cpp). This is used for the // HTML Find dialog #define ALLOW_SHELLUIOC_HOST CIntelliForms *GetIntelliFormsFromDoc(IHTMLDocument2 *pDoc2); inline void MyToLower(LPWSTR pwszStr) { if (g_fRunningOnNT) { CharLowerBuffW(pwszStr, lstrlenW(pwszStr)); } else { // Ideally we would use the code page contained in the string instead of // the system code page. CHAR chBuf[MAX_PATH]; SHUnicodeToAnsi(pwszStr, chBuf, ARRAYSIZE(chBuf)); CharLowerBuffA(chBuf, lstrlenA(chBuf)); SHAnsiToUnicode(chBuf, pwszStr, lstrlenW(pwszStr)+1); } } //=================== Exported functions ===================== // Exported for inetCPL HRESULT ClearAutoSuggestForForms(DWORD dwClear) { CIntelliForms *pObj = new CIntelliForms(); if (pObj) { HRESULT hr; hr = pObj->ClearStore(dwClear); pObj->Release(); return hr; } return E_OUTOFMEMORY; } HRESULT SetIdAutoSuggestForForms(const GUID *pguidId, void *pIntelliForms) { CIntelliForms *pThis = (CIntelliForms *)pIntelliForms; if (pThis) { if (GUID_NULL == *pguidId) { pThis->m_guidUserId = c_PStoreType; } else { pThis->m_guidUserId = *pguidId; } return S_OK; } return E_FAIL; } // called from iedisp.cpp void AttachIntelliForms(void *pvOmWindow, HWND hwnd, IHTMLDocument2 *pDoc2, void **ppIntelliForms) { static DWORD s_dwAdminRestricted = 0xFE; CIEFrameAuto::COmWindow *pOmWindow = (CIEFrameAuto::COmWindow *)pvOmWindow; ASSERT(ppIntelliForms && *ppIntelliForms==NULL); if (s_dwAdminRestricted == 0xFE) { s_dwAdminRestricted = CIntelliForms::IsAdminRestricted(c_szRegValFormSuggestRestrict) && CIntelliForms::IsAdminRestricted(c_szRegValSavePasswords); } if (s_dwAdminRestricted) { return; } // If we're not hosted by internet explorer, we don't want to enable Intelliforms // unless dochost explicitly overrides this if (!IsInternetExplorerApp() && !(pOmWindow && (DOCHOSTUIFLAG_ENABLE_FORMS_AUTOCOMPLETE & pOmWindow->IEFrameAuto()->GetDocHostFlags()))) { return; } if (!hwnd && pOmWindow) { pOmWindow->IEFrameAuto()->get_HWND((LONG_PTR *)&hwnd); } if (!hwnd || !pDoc2 || !ppIntelliForms || (*ppIntelliForms != NULL)) { return; } #ifndef ALLOW_SHELLUIOC_HOST if (!pOmWindow) { return; } #else if (!pOmWindow) { // Script is asking to attach to this document // Deny their request if another CIntelliForms is already attached if (NULL != GetIntelliFormsFromDoc(pDoc2)) { return; } } #endif CIntelliForms *pForms = new CIntelliForms(); if (pForms) { if (SUCCEEDED(pForms->Init(pOmWindow, pDoc2, hwnd))) { *ppIntelliForms = pForms; } else { pForms->Release(); } } } void ReleaseIntelliForms(void *pIntelliForms) { CIntelliForms *pForms = (CIntelliForms *) pIntelliForms; if (pForms) { pForms->UnInit(); pForms->Release(); } } HRESULT IntelliFormsActiveElementChanged(void *pIntelliForms, IHTMLElement * pHTMLElement) { CIntelliForms *pForms = (CIntelliForms *) pIntelliForms; if (pForms) return pForms->ActiveElementChanged(pHTMLElement); return E_FAIL; } INT_PTR CALLBACK AskUserDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); HRESULT IncrementAskCount(); HRESULT IntelliFormsDoAskUser(HWND hwndBrowser, void *pv) { // Make sure that we haven't asked them yet if (S_OK == IncrementAskCount()) { // Modal dialog to ask the user our little question SHFusionDialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_ASK_USER), hwndBrowser, AskUserDlgProc, NULL); } return S_OK; } // Linked list of active CIntelliform objects to translate from // IHTMLDocument2->CIntelliforms when script calls window.external.saveforms // Protected by g_csDll CIntelliForms *g_pIntelliFormsFirst=NULL; // Translate this pDoc2 to an existing instance of CIntelliForms // Will return NULL if no CIntelliForms attached to this doc // NO REFCOUNT IS ADDED TO THE RETURN CIntelliForms *GetIntelliFormsFromDoc(IHTMLDocument2 *pDoc2) { if (!pDoc2) { return NULL; } ENTERCRITICAL; CIntelliForms *pNext = g_pIntelliFormsFirst; IUnknown *punkDoc; CIntelliForms *pIForms=NULL; pDoc2->QueryInterface(IID_IUnknown, (void **)&punkDoc); if (punkDoc) { while (pNext) { if (pNext->GetDocument() == punkDoc) { pIForms = pNext; break; } pNext=pNext->GetNext(); } punkDoc->Release(); } LEAVECRITICAL; return pIForms; } // called from shuioc.cpp HRESULT IntelliFormsSaveForm(IHTMLDocument2 *pDoc2, VARIANT *pvarForm) { HRESULT hrRet = S_FALSE; IHTMLFormElement *pForm=NULL; CIntelliForms *pIForms=NULL; if (pvarForm->vt == VT_DISPATCH) { pvarForm->pdispVal->QueryInterface(IID_IHTMLFormElement, (void **)&pForm); } if (pForm) { pIForms = GetIntelliFormsFromDoc(pDoc2); if (pIForms) { // Should validate that pIForms was created on this thread hrRet = pIForms->ScriptSubmit(pForm); } pForm->Release(); } return hrRet; } const TCHAR c_szYes[] = TEXT("yes"); const TCHAR c_szNo[] = TEXT("no"); INT_PTR AutoSuggestDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam); #ifdef CHECKBOX_HELP const DWORD c_aIFormsHelpIds[] = { IDC_AUTOSUGGEST_NEVER, IDH_INTELLIFORM_PW_PROMPT, 0, 0 }; #endif const WCHAR c_wszVCardPrefix[] = L"vCard."; BOOL CIntelliForms::CAutoSuggest::s_fRegisteredWndClass = FALSE; // Must be in same order as EVENT enum type // All events we need to sink anywhere CEventSinkCallback::EventSinkEntry CEventSinkCallback::EventsToSink[] = { { EVENT_KEYDOWN, L"onkeydown", L"keydown" }, { EVENT_KEYPRESS, L"onkeypress", L"keypress" }, { EVENT_MOUSEDOWN, L"onmousedown", L"mousedown"}, { EVENT_DBLCLICK, L"ondblclick", L"dblclick" }, { EVENT_FOCUS, L"onfocus", L"focus" }, { EVENT_BLUR, L"onblur", L"blur" }, { EVENT_SUBMIT, L"onsubmit", L"submit" }, { EVENT_SCROLL, L"onscroll", L"scroll" }, { EVENT_COMPOSITION,NULL, L"composition"}, { EVENT_NOTIFY, NULL, L"notify" }, }; // Fake edit window class const WCHAR c_szEditWndClass[] = TEXT("IntelliFormClass"); // Minimum dropdown width const int MINIMUM_WIDTH=100; // Submit number to ask user to enable us const int ASK_USER_ON_SUBMIT_N = 2; void GetStuffFromEle(IUnknown *punkEle, IHTMLWindow2 **ppWin2, IHTMLDocument2 **ppDoc2) { if (ppWin2) *ppWin2=NULL; if (ppDoc2) *ppDoc2=NULL; IHTMLElement *pEle=NULL; punkEle->QueryInterface(IID_IHTMLElement, (void **)&pEle); if (pEle) { IDispatch *pDisp=NULL; pEle->get_document(&pDisp); if (pDisp) { IHTMLDocument2 *pDoc2 = NULL; pDisp->QueryInterface(IID_IHTMLDocument2, (void **)&pDoc2); if (pDoc2) { if (ppWin2) { pDoc2->get_parentWindow(ppWin2); } if (ppDoc2) { *ppDoc2 = pDoc2; } else { pDoc2->Release(); } } pDisp->Release(); } pEle->Release(); } } void Win3FromDoc2(IHTMLDocument2 *pDoc2, IHTMLWindow3 **ppWin3) { *ppWin3=NULL; IHTMLWindow2 *pWin2=NULL; if (SUCCEEDED(pDoc2->get_parentWindow(&pWin2)) && pWin2) { pWin2->QueryInterface(IID_IHTMLWindow3, (void **)ppWin3); pWin2->Release(); } } // Increment the count of whether we've asked the user to enable us or not. We won't // ask them on the first form submit since installing ie5. HRESULT IncrementAskCount() { DWORD dwData, dwSize, dwType; dwSize = sizeof(dwData); // c_szRegValAskUser contains the number of form submits // 0 means we've already asked user whether to enable us // 1 means we've already had one form submit, and should ask the user this time // value not present means we haven't had any form submits if ((ERROR_SUCCESS == SHGetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser, &dwType, &dwData, &dwSize)) && dwType == REG_DWORD) { if (dwData == 0) { // Shouldn't get this far TraceMsg(TF_IFORMS|TF_WARNING, "IntelliFormsDoAskUser: Already asked user"); return E_FAIL; // Already asked user } } else { dwData = 0; } if (dwData+1 < ASK_USER_ON_SUBMIT_N) { dwData ++; SHSetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser, REG_DWORD, &dwData, sizeof(dwData)); TraceMsg(TF_IFORMS, "IntelliFormsDoAskUser incrementing submit count. Not asking user."); return E_FAIL; // Don't ask the user } return S_OK; // Let's ask the user } ///////////////////////////////////////////////////////////////////////////// // CIntelliForms CIntelliForms::CIntelliForms() { TraceMsg(TF_IFORMS, "CIntelliForms::CIntelliForms"); m_cRef = 1; m_iRestoredIndex = -1; m_fRestricted = IsAdminRestricted(c_szRegValFormSuggestRestrict); m_fRestrictedPW = IsAdminRestricted(c_szRegValSavePasswords); m_guidUserId = c_PStoreType; // Add us to global linked list ENTERCRITICAL; m_pNext = g_pIntelliFormsFirst; g_pIntelliFormsFirst = this; LEAVECRITICAL; } CIntelliForms::~CIntelliForms() { // Remove us from global linked list ENTERCRITICAL; CIntelliForms *pLast=NULL, *pNext = g_pIntelliFormsFirst; while (pNext && pNext != this) { pLast = pNext; pNext=pNext->m_pNext; } ASSERT(pNext == this); if (pNext) { if (pLast) { pLast->m_pNext = m_pNext; } else { g_pIntelliFormsFirst = m_pNext; } } LEAVECRITICAL; TraceMsg(TF_IFORMS, "CIntelliForms::~CIntelliForms"); } // Called when document is ready to attach to // We don't support re-initting HRESULT CIntelliForms::Init(CIEFrameAuto::COmWindow *pOmWindow, IHTMLDocument2 *pDoc2, HWND hwndBrowser) { HRESULT hr; ASSERT(pDoc2 && hwndBrowser); #ifndef ALLOW_SHELLUIOC_HOST if (pOmWindow == NULL) { return E_INVALIDARG; } #endif // Connect to get active element changed notifications m_pOmWindow = pOmWindow; if (pOmWindow) { pOmWindow->AddRef(); } m_pDoc2 = pDoc2; pDoc2->AddRef(); pDoc2->QueryInterface(IID_IUnknown, (void **)&m_punkDoc2); m_hwndBrowser = hwndBrowser; m_iRestoredIndex = -1; hr = S_OK; #ifdef ALLOW_SHELLUIOC_HOST if (!pOmWindow && (hr == S_OK)) { // Check for the current active element since the page is requesting // us to attach to an existing document IHTMLElement *pHTMLElement = NULL; m_pDoc2->get_activeElement(&pHTMLElement); ActiveElementChanged(pHTMLElement); if (pHTMLElement) pHTMLElement->Release(); } #endif GetUrl(); // Init Url member variables so we don't get the url on the // wrong thread in the FillEnumerator call TraceMsg(TF_IFORMS, "CIntelliForms::Init hr=%08x", hr); return hr; } HRESULT CIntelliForms::UnInit() { if (m_fInModalDialog) { // Lifetime management. If UnInit is called during modal dialog, we keep ourself // alive. Use Enter/LeaveModalDialog to ensure correct use ASSERT(m_fUninitCalled == FALSE); // Should only be called once... m_fUninitCalled = TRUE; return S_FALSE; } // Destroy this now, before we free other member variables, to ensure CAutoSuggest doesn't // try to access us on a second thread. if (m_pAutoSuggest) { m_pAutoSuggest->SetParent(NULL); m_pAutoSuggest->DetachFromInput(); delete m_pAutoSuggest; m_pAutoSuggest = NULL; } if (m_hdpaForms && m_pSink) { IHTMLElement2 *pEle2; EVENTS events[] = { EVENT_SUBMIT }; for (int i=DPA_GetPtrCount(m_hdpaForms)-1; i>=0; i--) { ((IHTMLFormElement *)(DPA_FastGetPtr(m_hdpaForms, i)))->QueryInterface(IID_IHTMLElement2, (void **)&pEle2); m_pSink->UnSinkEvents(pEle2, ARRAYSIZE(events), events); pEle2->Release(); } } SysFreeString(m_bstrFullUrl); m_bstrFullUrl = NULL; SysFreeString(m_bstrUrl); m_bstrUrl = NULL; if (m_pwszUrlHash) { LocalFree((void *)m_pwszUrlHash); m_pwszUrlHash = NULL; } // Unhook regular event sink if (m_pSink) { #ifndef ALLOW_SHELLUIOC_HOST ASSERT(m_pOmWindow); #endif if (m_pOmWindow) { IHTMLWindow3 *pWin3=NULL; Win3FromDoc2(m_pDoc2, &pWin3); if (pWin3) { EVENTS events[] = { EVENT_SCROLL }; m_pSink->UnSinkEvents(pWin3, ARRAYSIZE(events), events); pWin3->Release(); } } m_pSink->SetParent(NULL); m_pSink->Release(); m_pSink=NULL; } // Unhook designer event sink if (m_pEditSink) { m_pEditSink->Attach(NULL); m_pEditSink->SetParent(NULL); m_pEditSink->Release(); m_pEditSink=NULL; } // SAFERELEASE (and ATOMICRELEASE) macro in shdocvw is actually function which requires IUnknown ATOMICRELEASET(m_pOmWindow, CIEFrameAuto::COmWindow); SAFERELEASE(m_pDoc2); SAFERELEASE(m_punkDoc2); FreeElementList(); FreeFormList(); if (m_pslPasswords) { delete m_pslPasswords; m_pslPasswords = NULL; } ReleasePStore(); TraceMsg(TF_IFORMS, "CIntelliForms::UnInit"); return S_OK; } STDMETHODIMP CIntelliForms::QueryInterface(REFIID riid, void **ppv) { *ppv = NULL; if ((IID_IPropertyNotifySink == riid) || (IID_IUnknown == riid)) { *ppv = (IPropertyNotifySink *)this; } if (NULL != *ppv) { ((IUnknown *)*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CIntelliForms::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) CIntelliForms::Release(void) { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } HRESULT CIntelliForms::ActiveElementChanged(IHTMLElement * pHTMLElement) { ASSERT(m_pDoc2); // Detach the AutoSuggest object and destroy it if (m_pAutoSuggest) { m_pAutoSuggest->DetachFromInput(); delete m_pAutoSuggest; m_pAutoSuggest=NULL; } if (m_pDoc2) { IHTMLElement *pEle=pHTMLElement; if (pEle) { BOOL fPassword=FALSE; IHTMLInputTextElement *pTextEle = NULL; if (SUCCEEDED(ShouldAttachToElement(pEle, TRUE, NULL, &pTextEle, NULL, &fPassword))) { BOOL fEnabledInCPL = IsEnabledInCPL(); BOOL fEnabledPW = IsEnabledRestorePW(); // We need to watch user activity if... if (fEnabledInCPL || // Intelliforms is enabled fEnabledPW || // Or Restore Passwords is enabled !AskedUserToEnable()) // Or we may ask them to enable us { m_pAutoSuggest = new CAutoSuggest(this, fEnabledInCPL, fEnabledPW); if (m_pAutoSuggest) { if (!m_pSink) { m_pSink = new CEventSink(this); if (m_pSink) { #ifndef ALLOW_SHELLUIOC_HOST // Don't sink scroll event if hosted by ShellUIOC // or jscript.dll asserts on unload ASSERT(m_pOmWindow); #endif if (m_pOmWindow) { IHTMLWindow3 *pWin3=NULL; Win3FromDoc2(m_pDoc2, &pWin3); if (pWin3) { EVENTS events[] = { EVENT_SCROLL }; m_pSink->SinkEvents(pWin3, ARRAYSIZE(events), events); pWin3->Release(); } } } } // Hook up designer sink for IME event if (!m_pEditSink) { m_pEditSink = new CEditEventSink(this); if (m_pEditSink) { m_pEditSink->Attach(pEle); } } if (!m_pSink || FAILED(m_pAutoSuggest->AttachToInput(pTextEle))) { delete m_pAutoSuggest; m_pAutoSuggest = NULL; } } } pTextEle->Release(); } else { ASSERT(!pTextEle); if (fPassword) { m_fHitPWField = TRUE; } } // // Don't release pEle } } return S_OK; } // Helper functions BOOL CIntelliForms::AskedUserToEnable() { DWORD dwType, dwSize; DWORD dwVal; DWORD dwRet; dwSize = sizeof(dwVal); dwRet = SHGetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser, &dwType, &dwVal, &dwSize); if ((dwRet == ERROR_SUCCESS) && (dwType == REG_DWORD)) { return (dwVal == 0) ? TRUE : FALSE; } return FALSE; } BOOL CIntelliForms::IsEnabledInRegistry(LPCTSTR pszKey, LPCTSTR pszValue, BOOL fDefault) { DWORD dwType, dwSize; TCHAR szEnabled[16]; DWORD dwRet; dwSize = sizeof(szEnabled); dwRet = SHGetValue(HKEY_CURRENT_USER, pszKey, pszValue, &dwType, szEnabled, &dwSize); if (dwRet == ERROR_INSUFFICIENT_BUFFER) { // Invalid value in registry. ASSERT(dwRet == ERROR_SUCCESS); return FALSE; } if (dwRet == ERROR_SUCCESS) { if ((dwType == REG_SZ) && (!StrCmp(szEnabled, TEXT("yes")))) { // Enabled return TRUE; } else { // Disabled return FALSE; } } // Value not found return fDefault; } BOOL CIntelliForms::IsAdminRestricted(LPCTSTR pszRegVal) { DWORD lSize; DWORD lValue; lValue = 0; // clear it lSize = sizeof(lValue); if (ERROR_SUCCESS != SHGetValue(HKEY_CURRENT_USER, c_szRegKeyRestrict, pszRegVal, NULL, (LPBYTE)&lValue, &lSize )) { return FALSE; } ASSERT(lSize == sizeof(lValue)); return (0 != lValue) ? TRUE : FALSE; } BOOL CIntelliForms::IsEnabledForPage() { if (!m_fCheckedIfEnabled) { m_fCheckedIfEnabled = TRUE; // We will have our Url in m_bstrFullUrl, only if it is https: protocol if (m_bstrFullUrl) { ASSERT(!StrCmpNIW(m_bstrFullUrl, L"https:", 5)); m_fEnabledForPage = TRUE; // See if this page is in the internet cache. If not, we won't intelliform // for this page either. if (!GetUrlCacheEntryInfoW(m_bstrFullUrl, NULL, NULL) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER)) { // Failed - it's not in the cache m_fEnabledForPage = FALSE; } } else { // Url is not https: so always enable Intelliforms m_fEnabledForPage = TRUE; } } return m_fEnabledForPage; } HRESULT CIntelliForms::GetBodyEle(IHTMLElement2 **ppEle2) { if (!m_pDoc2 || !ppEle2) { return E_INVALIDARG; } *ppEle2=NULL; IHTMLElement *pBodyEle=NULL; m_pDoc2->get_body(&pBodyEle); if (pBodyEle) { pBodyEle->QueryInterface(IID_IHTMLElement2, (void **)ppEle2); pBodyEle->Release(); } return (*ppEle2) ? S_OK : E_FAIL; } // static BOOL CIntelliForms::IsElementEnabled(IHTMLElement *pEle) { BOOL fEnabled=TRUE; BSTR bstrAttribute; VARIANT varVal; varVal.vt = VT_EMPTY; // First check "AutoComplete=OFF" bstrAttribute=SysAllocString(L"AutoComplete"); if (bstrAttribute && SUCCEEDED(pEle->getAttribute(bstrAttribute, 0, &varVal))) { if (varVal.vt == VT_BSTR) { if (!StrCmpIW(varVal.bstrVal, L"off")) { // We are disabled. fEnabled=FALSE; } } VariantClear(&varVal); } SysFreeString(bstrAttribute); // Then check "READONLY" attribute if (fEnabled) { IHTMLInputElement *pInputEle=NULL; pEle->QueryInterface(IID_IHTMLInputElement, (void **)&pInputEle); if (pInputEle) { VARIANT_BOOL vbReadOnly=VARIANT_FALSE; pInputEle->get_readOnly(&vbReadOnly); if (vbReadOnly) { // We are read only. fEnabled=FALSE; } pInputEle->Release(); } } return fEnabled; } // static HRESULT CIntelliForms::ShouldAttachToElement(IUnknown *punkEle, BOOL fCheckForm, IHTMLElement2 **ppEle2, IHTMLInputTextElement **ppITE, IHTMLFormElement **ppFormEle, BOOL *pfPassword) { IHTMLInputTextElement *pITE = NULL; if (ppEle2) { *ppEle2 = NULL; } if (ppITE) { *ppITE = NULL; } if (ppFormEle) { *ppFormEle = NULL; } punkEle->QueryInterface(IID_IHTMLInputTextElement, (void **)&pITE); if (NULL == pITE) { // Not an input text element. Do not attach. return E_FAIL; } HRESULT hr = E_FAIL; IHTMLElement2 *pEle2 = NULL; IHTMLElement *pEle = NULL; IHTMLFormElement *pFormEle = NULL; punkEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2); punkEle->QueryInterface(IID_IHTMLElement, (void **)&pEle); if (pEle2 && pEle) { // type=text is all that's allowed BSTR bstrType=NULL; if (SUCCEEDED(pITE->get_type(&bstrType)) && bstrType) { if (!StrCmpICW(bstrType, L"text")) { // FormSuggest=off attribute turns us off for this element if (IsElementEnabled(pEle)) { IHTMLElement *pFormHTMLEle=NULL; if (fCheckForm || ppFormEle) { pITE->get_form(&pFormEle); if (pFormEle) { pFormEle->QueryInterface(IID_IHTMLElement, (void **)&pFormHTMLEle); } else { // This may be valid if element is not in form TraceMsg(TF_IFORMS, "Iforms: pITE->get_form() returned NULL"); } } // FormSuggest=off for form turns us off for this form if (pFormEle && (!fCheckForm || (pFormHTMLEle && IsElementEnabled(pFormHTMLEle)))) { hr = S_OK; if (ppEle2) { *ppEle2 = pEle2; pEle2->AddRef(); } if (ppFormEle) { *ppFormEle = pFormEle; pFormEle->AddRef(); } if (ppITE) { *ppITE = pITE; pITE->AddRef(); } } SAFERELEASE(pFormHTMLEle); SAFERELEASE(pFormEle); } } else { if (pfPassword && !StrCmpICW(bstrType, L"password") && IsElementEnabled(pEle)) { TraceMsg(TF_IFORMS, "IForms: Password field detected."); *pfPassword = TRUE; } } SysFreeString(bstrType); } else { TraceMsg(TF_IFORMS, "IntelliForms disabled for single element via attribute"); } } SAFERELEASE(pITE); SAFERELEASE(pEle2); SAFERELEASE(pEle); return hr; } // Get the URL that we're located at, with query string/anchor stripped. LPCWSTR CIntelliForms::GetUrl() { if (m_bstrUrl) { return m_bstrUrl; } if (m_pOmWindow) { m_pOmWindow->IEFrameAuto()->get_LocationURL(&m_bstrUrl); } #ifdef ALLOW_SHELLUIOC_HOST else { IHTMLLocation *pHTMLLocation=NULL; m_pDoc2->get_location(&pHTMLLocation); if (NULL != pHTMLLocation) { pHTMLLocation->get_href(&m_bstrUrl); pHTMLLocation->Release(); } } #endif if (m_bstrUrl) { PARSEDURLW puW = {0}; puW.cbSize = sizeof(puW); // Save the full url for a security check, if we are https protocol if (SUCCEEDED(ParseURLW(m_bstrUrl, &puW))) { if (puW.nScheme == URL_SCHEME_HTTPS) { m_bstrFullUrl = SysAllocString(m_bstrUrl); if (!m_bstrFullUrl) { SysFreeString(m_bstrUrl); m_bstrUrl=NULL; } } } } if (m_bstrUrl) { // Strip off any query string or anchor LPWSTR lpUrl = m_bstrUrl; while (*lpUrl) { if ((*lpUrl == L'?') || (*lpUrl == L'#')) { *lpUrl = L'\0'; break; } lpUrl ++; } return m_bstrUrl; } TraceMsg(TF_WARNING|TF_IFORMS, "CIntelliForms::GetUrl() failing!"); return L""; // We can assume non-NULL pointer } // hook our "Submit" event sink to this form HRESULT CIntelliForms::AttachToForm(IHTMLFormElement *pFormEle) { ASSERT(m_pSink); if (m_pSink) { IHTMLElement2 *pEle2 = NULL; pFormEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2); if (pEle2) { // Sink event for the form EVENTS events[] = { EVENT_SUBMIT }; m_pSink->SinkEvents(pEle2, ARRAYSIZE(events), events); } SAFERELEASE(pEle2); return S_OK; } return E_OUTOFMEMORY; } // Returns TRUE if nothing but spaces in string inline BOOL IsEmptyString(LPCWSTR lpwstr) { while (*lpwstr && (*lpwstr == L' ')) lpwstr++; return (*lpwstr == 0); } // called for each element in the form we are submitting HRESULT CIntelliForms::SubmitElement(IHTMLInputTextElement *pITE, FILETIME ftSubmit, BOOL fEnabledInCPL) { if (m_fRestricted) return E_FAIL; HRESULT hrRet = S_OK; BSTR bstrName; CIntelliForms::GetName(pITE, &bstrName); if (bstrName && bstrName[0]) { BSTR bstrValue=NULL; pITE->get_value(&bstrValue); if (bstrValue && bstrValue[0] && !IsEmptyString(bstrValue)) { if (fEnabledInCPL) { TraceMsg(TF_IFORMS, "IForms: Saving field \"%ws\" as \"%ws\"", bstrName, bstrValue); CStringList *psl; if (FAILED(ReadFromStore(bstrName, &psl))) { CStringList_New(&psl); } if (psl) { HRESULT hr; if (SUCCEEDED(hr = psl->AddString(bstrValue, ftSubmit))) { if ((S_OK == hr) || (psl->NumStrings() > CStringList::MAX_STRINGS / 4)) { // We added a non-duplicate string, or we updated the // last submit time of an existing string WriteToStore(bstrName, psl); } } delete psl; } } else { hrRet = S_FALSE; // Tell caller that we didn't save because we were disabled } } SysFreeString(bstrValue); } SysFreeString(bstrName); return hrRet; } HRESULT CIntelliForms::HandleFormSubmit(IHTMLFormElement *pForm) { IUnknown *punkForm=NULL; if (!pForm) { // We currently require a form element even from script return E_INVALIDARG; } if (!m_hdpaElements || !m_hdpaForms) { return S_OK; } // Make sure we're enabled BOOL fEnabledInCPL = IsEnabledInCPL(); if (fEnabledInCPL || IsEnabledRestorePW() || !AskedUserToEnable()) { pForm->QueryInterface(IID_IUnknown, (void **)&punkForm); if (punkForm) { IHTMLFormElement *pThisFormEle; IUnknown *punkThisForm; FILETIME ftSubmit; int iCount=0; BOOL fShouldAskUser=FALSE; IHTMLInputTextElement *pFirstEle=NULL; GetSystemTimeAsFileTime(&ftSubmit); // Go through list of 'changed' elements and save their values // make sure we loop backwards since we nuke elements as we find them for (int i=DPA_GetPtrCount(m_hdpaElements)-1; i>=0; i--) { IHTMLInputTextElement *pITE = ((IHTMLInputTextElement *)(DPA_FastGetPtr(m_hdpaElements, i))); if (SUCCEEDED(pITE->get_form(&pThisFormEle)) && pThisFormEle) { if (SUCCEEDED(pThisFormEle->QueryInterface(IID_IUnknown, (void **)&punkThisForm))) { if (punkThisForm == punkForm) { // Verify that we're still allowed to save this element if (SUCCEEDED(ShouldAttachToElement(pITE, TRUE, NULL, NULL, NULL, NULL))) { iCount ++; if (!pFirstEle) { pFirstEle = pITE; pFirstEle->AddRef(); } // Don't save non-password stuff for non-cached pages if (IsEnabledForPage()) { // Won't actually save the value if fEnabledInCPL is FALSE if (S_FALSE == SubmitElement(pITE, ftSubmit, fEnabledInCPL)) { // We would have saved this if we were enabled fShouldAskUser = TRUE; } } // Remove this element from the DPA to prevent any possibility of // saving before more user input takes place pITE->Release(); DPA_DeletePtr(m_hdpaElements, i); } } else { TraceMsg(TF_IFORMS, "IForms: User input in different form than was submitted...?"); } punkThisForm->Release(); } pThisFormEle->Release(); } else { // It shouldn't be in our DPA if it isn't in a form... TraceMsg(TF_WARNING|TF_IFORMS, "Iforms: pITE->get_form() returned NULL!"); } } if (0 == DPA_GetPtrCount(m_hdpaElements)) { DPA_Destroy(m_hdpaElements); m_hdpaElements=NULL; } if (m_fHitPWField || (m_iRestoredIndex != -1)) { // ?? why not check iCount==1 here? if (pFirstEle) { // May have restored PW and may have changed or entered it SavePassword(pForm, ftSubmit, pFirstEle); // WARNING - after returning from "SavePassword" our object may be invalid // if we got released/detached during modal dialog } } else if (fShouldAskUser) { // Possibly ask user if they want to enable intelliforms, only if // this isn't a login if (m_pOmWindow) { m_pOmWindow->IntelliFormsAskUser(NULL); } fShouldAskUser = FALSE; } if (fShouldAskUser) { // If we should ask the user but we're not going to (login form), // increment our count anyway so that we ask them as soon as we can IncrementAskCount(); } punkForm->Release(); SAFERELEASE(pFirstEle); } } return S_OK; } HRESULT CIntelliForms::HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj) { TraceMsg(TF_IFORMS, "CIntelliForms::HandleEvent Event=%ws", EventsToSink[Event].pwszEventName); if (Event == EVENT_SUBMIT) { // Save strings for modified text inputs when appropriate IHTMLFormElement *pFormEle = NULL; if (pEle) { pEle->QueryInterface(IID_IHTMLFormElement, (void **)&pFormEle); if (pFormEle) { HandleFormSubmit(pFormEle); // Warning - "this" may be detached/destroyed at this point pFormEle->Release(); } } } else { ASSERT(Event == EVENT_SCROLL); if (m_pAutoSuggest) m_pAutoSuggest->UpdateDropdownPosition(); } return S_OK; } HRESULT CIntelliForms::PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj* pIEventObj) { if ((inEvtDispId == 0) && (m_pAutoSuggest != NULL) && (m_pAutoSuggest->AttachedElement() != NULL)) { BSTR bstrType = NULL; CEventSinkCallback::EVENTS Event = EVENT_BOGUS; pIEventObj->get_type(&bstrType); if (bstrType) { #if 0 // Spew wParam and lParam IHTMLEventObj3 *pObj3 = NULL; pIEventObj->QueryInterface(IID_PPV_ARG(IHTMLEventObj3, &pObj3)); if (pObj3) { long lLong=0; long wWord=0; pObj3->get_imeCompositionChange(&lLong); pObj3->get_imeNotifyCommand(&wWord); TraceMsg(TF_ALWAYS, "PreHandleEvent: %ws - wWord=0x%04x lLong=0x%08x", bstrType, wWord, lLong); pObj3->Release(); } #endif if (!StrCmp(bstrType, L"composition")) { Event = EVENT_COMPOSITION; } else if (!StrCmp(bstrType, L"notify")) { Event = EVENT_NOTIFY; } if (Event != EVENT_BOGUS) { // Trident doesn't set srcElement on eventobj, so just use the one // we're attached to IHTMLElement *pEle; m_pAutoSuggest->AttachedElement()->QueryInterface(IID_IHTMLElement, (void **)&pEle); if (pEle) { m_pAutoSuggest->HandleEvent(pEle, Event, pIEventObj); pEle->Release(); } } SysFreeString(bstrType); } } return S_FALSE; // S_FALSE so that Trident will still process this } // Our passwords are stored in username/value pairs // Search every other string for the username HRESULT CIntelliForms::FindPasswordEntry(LPCWSTR pwszValue, int *piIndex) { ASSERT(m_pslPasswords); ASSERT(!(m_pslPasswords->NumStrings() & 1)); // Should be even number int i; for (i=0; iNumStrings(); i += 2) { if (!StrCmpIW(pwszValue, m_pslPasswords->GetString(i))) { // Found it *piIndex = i+1; return S_OK; } } return E_FAIL; } // Convert url to string based on shlwapi UrlHash return LPCWSTR CIntelliForms::GetUrlHash() { BYTE bBuf[15]; if (m_pwszUrlHash) { return m_pwszUrlHash; } LPCWSTR pwszUrl = GetUrl(); if (!pwszUrl || !*pwszUrl) { return NULL; } if (SUCCEEDED(UrlHashW(pwszUrl, bBuf, ARRAYSIZE(bBuf)))) { // Translate this array of bytes into 7-bit chars m_pwszUrlHash = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(WCHAR)*(ARRAYSIZE(bBuf)+1)); if (m_pwszUrlHash) { for (int i=0; i::EnumCollection(pFormEle, s_PasswordCB, (DWORD_PTR)this); // For forms with two password fields (possibly used for login *and* new accounts) // we clear the second field on PW restore and require it to be blank for saving. // Ideally, we would detect this as a password change situation as well. if (m_pPasswordEle2) { if (fRestoring) { BSTR bstrEmpty=SysAllocString(L""); if (bstrEmpty) { m_pPasswordEle2->put_value(bstrEmpty); SysFreeString(bstrEmpty); } } else { BSTR bstrVal=NULL; m_pPasswordEle2->get_value(&bstrVal); if (bstrVal && bstrVal[0]) { // Failure! Second password field isn't empty. SAFERELEASE(m_pNameEle); SAFERELEASE(m_pPasswordEle); } SysFreeString(bstrVal); } SAFERELEASE(m_pPasswordEle2); // Always release this } if (m_pPasswordEle && m_pNameEle) { return S_OK; } SAFERELEASE(m_pNameEle); SAFERELEASE(m_pPasswordEle); ASSERT(!m_pPasswordEle2); return E_FAIL; } // Password callback for CEnumCollection to find username and password fields // in a login form HRESULT CDetectLoginForm::s_PasswordCB(IDispatch *pDispEle, DWORD_PTR dwCBData) { CDetectLoginForm *pThis = (CDetectLoginForm *)dwCBData; HRESULT hr=S_OK; IHTMLInputTextElement *pTextEle=NULL; pDispEle->QueryInterface(IID_IHTMLInputTextElement, (void **)&pTextEle); if (pTextEle) { BSTR bstrType; pTextEle->get_type(&bstrType); if (bstrType) { if (!StrCmpICW(bstrType, L"text")) { // Assume this is the 'name' field if (pThis->m_pNameEle) { // Whoops, we've already got a name field. Can't have two... hr = E_ABORT; } else { pThis->m_pNameEle = pTextEle; pTextEle->AddRef(); } } else if (!StrCmpICW(bstrType, L"password")) { // Assume this is the 'password' field if (pThis->m_pPasswordEle) { // Whoops, we've already got a password field. Can't have two... // ...oh wait, yes we can... if (pThis->m_pPasswordEle2) { // ...but we definitely can't have three!!! hr = E_ABORT; } else { pThis->m_pPasswordEle2 = pTextEle; pTextEle->AddRef(); } } else { pThis->m_pPasswordEle = pTextEle; pTextEle->AddRef(); } } SysFreeString(bstrType); } pTextEle->Release(); } if (hr == E_ABORT) { SAFERELEASE(pThis->m_pNameEle); SAFERELEASE(pThis->m_pPasswordEle); SAFERELEASE(pThis->m_pPasswordEle2); } return hr; } // Fill in passwords for this username, if one is available HRESULT CIntelliForms::AutoFillPassword(IHTMLInputTextElement *pTextEle, LPCWSTR pwszUsername) { BSTR bstrUrl = NULL; if (!pTextEle || !pwszUsername) return E_INVALIDARG; if (!IsEnabledRestorePW() || !LoadPasswords()) { // We have no passwords for this url return S_FALSE; } int iIndex; if (SUCCEEDED(FindPasswordEntry(pwszUsername, &iIndex))) { // Returns index of password in m_pslPasswords ASSERT(iIndex>=0 && iIndexNumStrings() && (iIndex&1)); FILETIME ft; // StringTime==0 indicates user said "no" to saving password if (SUCCEEDED(m_pslPasswords->GetStringTime(iIndex, &ft)) && (FILETIME_TO_INT(ft) != 0)) { TraceMsg(TF_IFORMS, "IntelliForms found saved password"); // We have a password saved for this specific username. Fill it in. CDetectLoginForm LoginForm; IHTMLFormElement *pFormEle=NULL; HRESULT hr = E_FAIL; pTextEle->get_form(&pFormEle); if (pFormEle) { // See if this is a valid form: One plain text input, One password input. Find the fields. hr = LoginForm.ParseForm(pFormEle, TRUE); pFormEle->Release(); } else { // Shouldn't get this far if we don't have a form for this element TraceMsg(TF_WARNING|TF_IFORMS, "Iforms: pITE->get_form() returned NULL!"); } if (SUCCEEDED(hr)) { BSTR bstrPW=NULL; m_pslPasswords->GetBSTR(iIndex, &bstrPW); if (bstrPW) { LoginForm.GetPasswordEle()->put_value(bstrPW); SysFreeString(bstrPW); m_iRestoredIndex = iIndex; // We restored this password. sink the SUBMIT for this form (if we haven't yet) UserInput(pTextEle); } } } else { // User previously said 'no' to remembering passwords m_iRestoredIndex = -1; } } return S_OK; } HRESULT CIntelliForms::DeletePassword(LPCWSTR pwszUsername) { // If we have a password, ask them if they want to delete it. if (LoadPasswords()) { int iIndex; if (SUCCEEDED(FindPasswordEntry(pwszUsername, &iIndex))) { // If they previously said "no", delete without asking - they don't actually // have a password saved // Otherwise, ask and delete only if they say "yes" FILETIME ft; if (FAILED(m_pslPasswords->GetStringTime(iIndex, &ft)) || (0 == FILETIME_TO_INT(ft)) || (IDYES == DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_DELETEPASSWORD), m_hwndBrowser, AutoSuggestDlgProc, IDD_AUTOSUGGEST_DELETEPASSWORD))) { // Delete username then password from string list if (SUCCEEDED(m_pslPasswords->DeleteString(iIndex-1)) && SUCCEEDED(m_pslPasswords->DeleteString(iIndex-1))) { TraceMsg(TF_IFORMS, "Deleting password for user \"%ws\"", pwszUsername); ASSERT(!(m_pslPasswords->NumStrings() & 1)); if (m_iRestoredIndex == iIndex) { m_iRestoredIndex = -1; } else if (m_iRestoredIndex > iIndex) { m_iRestoredIndex -= 2; } if (m_pslPasswords->NumStrings() == 0) { // No more strings for this url. Nuke it. DeleteFromStore(GetUrl()); SetPasswordsAreSaved(FALSE); delete m_pslPasswords; m_pslPasswords = NULL; ASSERT(m_iRestoredIndex == -1); } else { SavePasswords(); } } } } } return S_OK; } HRESULT CIntelliForms::SavePassword(IHTMLFormElement *pFormEle, FILETIME ftSubmit, IHTMLInputTextElement *pFirstEle) { if (m_fRestrictedPW || !IsEnabledRestorePW()) { return S_FALSE; } BOOL fAskUser = TRUE; // First let's check for previously saved entries for this username if (LoadPasswords()) { int iIndex; BSTR bstrUserName=NULL; pFirstEle->get_value(&bstrUserName); if (bstrUserName) { if (SUCCEEDED(FindPasswordEntry(bstrUserName, &iIndex))) { FILETIME ft; if (SUCCEEDED(m_pslPasswords->GetStringTime(iIndex, &ft))) { if (FILETIME_TO_INT(ft) == 0) { // StringTime==0 means user previously said "no". TraceMsg(TF_IFORMS, "IForms not asking about saving password"); fAskUser = FALSE; } else if (m_iRestoredIndex != iIndex) { // User previously said "yes" - but we didn't restore it for some reason // Can happen with "back" button then submit TraceMsg(TF_WARNING|TF_IFORMS, "IForms - user saved password and we didn't restore it"); // Write regkey in case that was the problem - we'll work next time SetPasswordsAreSaved(TRUE); m_iRestoredIndex = iIndex; } } } else { m_iRestoredIndex = -1; } SysFreeString(bstrUserName); } } // Then lets ask the user if they'd like to save the password for this username if (fAskUser) { CDetectLoginForm LoginForm; // See if this is a valid form: One plain text input, One password input. Find the fields. if (SUCCEEDED(LoginForm.ParseForm(pFormEle, FALSE))) { TraceMsg(TF_IFORMS, "IForms Successfully detected 'save password' form"); BSTR bstrUsername=NULL; BSTR bstrPassword=NULL; LoginForm.GetNameEle()->get_value(&bstrUsername); LoginForm.GetPasswordEle()->get_value(&bstrPassword); if (bstrUsername && bstrPassword) { if (m_iRestoredIndex != -1) { // We have a previously saved password. See if our current entry is the same. if (!StrCmpW(bstrPassword, m_pslPasswords->GetString(m_iRestoredIndex))) { // They're the same... nothing to do... TraceMsg(TF_IFORMS, "IForms - user entered PW same as saved PW - nothing to do"); // Check to see that the username case is the same, just to be sure if (StrCmpW(bstrUsername, m_pslPasswords->GetString(m_iRestoredIndex-1))) { TraceMsg(TF_IFORMS, "IForms - except change the username's case"); if (SUCCEEDED(m_pslPasswords->ReplaceString(m_iRestoredIndex-1, bstrUsername))) { SavePasswords(); } else { // Something went horribly wrong! delete m_pslPasswords; m_pslPasswords=NULL; } } } else { // Ask the user if we want to change the saved password INT_PTR iMB; EnterModalDialog(); iMB = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_CHANGEPASSWORD), m_hwndBrowser, AutoSuggestDlgProc, IDD_AUTOSUGGEST_CHANGEPASSWORD); if (IDYES == iMB) { // Delete the old one and add the new one. Update filetimes. if (SUCCEEDED(m_pslPasswords->ReplaceString(m_iRestoredIndex, bstrPassword))) { m_pslPasswords->SetStringTime(m_iRestoredIndex, ftSubmit); SavePasswords(); TraceMsg(TF_IFORMS, "IForms successfully saved changed password"); } else { TraceMsg(TF_IFORMS|TF_WARNING, "IForms couldn't change password!"); delete m_pslPasswords; m_pslPasswords = NULL; } } LeaveModalDialog(); } } else { // We don't have a previously saved password for this user. See if they want to save it. // If the password is empty, don't bother asking or saving if (IsEnabledAskPW() && bstrPassword[0]) { EnterModalDialog(); INT_PTR iMB = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_AUTOSUGGEST_SAVEPASSWORD), m_hwndBrowser, AutoSuggestDlgProc, IDD_AUTOSUGGEST_SAVEPASSWORD); // If we can't load passwords, then create a new list if (!LoadPasswords()) { CStringList_New(&m_pslPasswords); if (m_pslPasswords) m_pslPasswords->SetListData(LIST_DATA_PASSWORD); } if (m_pslPasswords) { if ((IDCANCEL == iMB) || ((IDNO == iMB) && (!IsEnabledAskPW()))) { // If they hit the close box or said "no" and checked "don't ask", // don't even save the username; we may ask them again next time } else { if (IDYES != iMB) { // User said "no" but we save the username (no password) and // set filetime to 0 which means they said "no" bstrPassword[0] = L'\0'; ftSubmit.dwLowDateTime = ftSubmit.dwHighDateTime = 0; } else { TraceMsg(TF_IFORMS, "IForms saving password for user %ws", bstrUsername); } m_pslPasswords->SetAutoScavenge(FALSE); // Save the username and password, or just the username if they said "no" if (SUCCEEDED(m_pslPasswords->AppendString(bstrUsername, ftSubmit)) && SUCCEEDED(m_pslPasswords->AppendString(bstrPassword, ftSubmit))) { SavePasswords(); } else { TraceMsg(TF_WARNING, "IForms couldn't save username/password"); delete m_pslPasswords; m_pslPasswords=NULL; } } } LeaveModalDialog(); } } } SysFreeString(bstrUsername); SysFreeString(bstrPassword); } // if (SUCCEEDED(ParseForm())) } return S_OK; } // Returns reference to password string list if present. Return value must be used // immediately and not destroyed. Used only by CEnumString. HRESULT CIntelliForms::GetPasswordStringList(CStringList **ppslPasswords) { if (LoadPasswords()) { *ppslPasswords = m_pslPasswords; return S_OK; } *ppslPasswords = NULL; return E_FAIL; } HRESULT CIntelliForms::CreatePStore() { if (!m_pPStore) { if (!m_hinstPStore) { m_hinstPStore = LoadLibrary(TEXT("PSTOREC.DLL")); } if (m_hinstPStore) { HRESULT (* pfn)(IPStore **, PST_PROVIDERID *, void *, DWORD) = NULL; *(FARPROC *)&pfn = GetProcAddress(m_hinstPStore, "PStoreCreateInstance"); if (pfn) { pfn(&m_pPStore, NULL, NULL, 0); } } } return m_pPStore ? S_OK : E_FAIL; } void CIntelliForms::ReleasePStore() { SAFERELEASE(m_pPStore); if (m_hinstPStore) { FreeLibrary(m_hinstPStore); m_hinstPStore = NULL; } m_fPStoreTypeInit=FALSE; } HRESULT CIntelliForms::CreatePStoreAndType() { HRESULT hr; hr = CreatePStore(); if (SUCCEEDED(hr) && !m_fPStoreTypeInit) { PST_TYPEINFO typeInfo; typeInfo.cbSize = sizeof(typeInfo); typeInfo.szDisplayName = (LPTSTR)c_szIntelliForms; hr = m_pPStore->CreateType(PST_KEY_CURRENT_USER, &c_PStoreType, &typeInfo, 0); if (hr == PST_E_TYPE_EXISTS) { hr = S_OK; } if (SUCCEEDED(hr)) { hr = m_pPStore->CreateSubtype(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, &typeInfo, NULL, 0); if (hr == PST_E_TYPE_EXISTS) { hr = S_OK; } } if (SUCCEEDED(hr)) { m_fPStoreTypeInit = TRUE; } } return hr; } const WCHAR c_szBlob1Value[] = L"StringIndex"; const WCHAR c_szBlob2Value[] = L"StringData"; HRESULT CIntelliForms::WriteToStore(LPCWSTR pwszName, CStringList *psl) { HRESULT hr = E_FAIL; TraceMsg(TF_IFORMS, "+WriteToStore"); if (SUCCEEDED(CreatePStoreAndType())) { LPBYTE pBlob1, pBlob2; DWORD cbBlob1, cbBlob2; if (SUCCEEDED(psl->WriteToBlobs(&pBlob1, &cbBlob1, &pBlob2, &cbBlob2))) { PST_PROMPTINFO promptInfo; promptInfo.cbSize = sizeof(promptInfo); promptInfo.dwPromptFlags = 0; promptInfo.hwndApp = NULL; promptInfo.szPrompt = NULL; LPWSTR pwszValue; int iValLen = lstrlenW(c_szBlob1Value) + lstrlenW(pwszName) + 10; pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, iValLen * sizeof(WCHAR)); if (pwszValue) { // Write Index wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value); hr = m_pPStore->WriteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, cbBlob1, pBlob1, &promptInfo, PST_CF_NONE, 0); if (FAILED(hr)) { TraceMsg(TF_WARNING | TF_IFORMS, "Failure writing Blob1 (Index). hr=%x", hr); } else { // Wrote Index successfully. Write data. wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value); hr = m_pPStore->WriteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, cbBlob2, pBlob2, &promptInfo, PST_CF_NONE, 0); if (FAILED(hr)) { // IE6#16676: This call failed on 64-bit Windows. Added a warning trace here to facilitate // future debugging. TraceMsg(TF_WARNING | TF_IFORMS, "Failure writing Blob2 (Data). hr=%x", hr); } } // If *either* WriteItem failed, we really need to delete both the Index and the Data. // if (FAILED(hr)) { // Delete bogus Blobs // Delete Index Blob wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value); if (FAILED(m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0))) { TraceMsg(TF_ERROR | TF_IFORMS, "Failure deleting Blob1 (Index). hr=%x", hr); } // Delete Data Blob wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value); if (FAILED(m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0))) { TraceMsg(TF_ERROR | TF_IFORMS, "Failure deleting Blob2 (Data). hr=%x", hr); } } LocalFree(pwszValue); pwszValue = NULL; } if (pBlob1) { LocalFree(pBlob1); pBlob1 = NULL; } if (pBlob2) { LocalFree(pBlob2); pBlob2 = NULL; } } } TraceMsg(TF_IFORMS, "-WriteToStore"); return hr; } HRESULT CIntelliForms::ReadFromStore(LPCWSTR pwszName, CStringList **ppsl, BOOL fPasswordList/*=FALSE*/) { HRESULT hr = E_FAIL; TraceMsg(TF_IFORMS, "+ReadFromStore"); *ppsl=NULL; if (SUCCEEDED(CreatePStore())) { PST_PROMPTINFO promptInfo; promptInfo.cbSize = sizeof(promptInfo); promptInfo.dwPromptFlags = 0; promptInfo.hwndApp = NULL; promptInfo.szPrompt = NULL; LPWSTR pwszValue; int iValLen = lstrlenW(c_szBlob1Value) + lstrlenW(pwszName) + 10; pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, iValLen * sizeof(WCHAR)); if (pwszValue) { DWORD dwBlob1Size, dwBlob2Size; LPBYTE pBlob1=NULL, pBlob2=NULL; wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value); hr = m_pPStore->ReadItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &dwBlob1Size, &pBlob1, &promptInfo, 0); if (SUCCEEDED(hr)) { wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value); hr = m_pPStore->ReadItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &dwBlob2Size, &pBlob2, &promptInfo, 0); if (SUCCEEDED(hr)) { // bogus... have to reallocate here... bogus... bogus... LPBYTE pBlob1b, pBlob2b; pBlob1b=(LPBYTE)LocalAlloc(LMEM_FIXED, dwBlob1Size); pBlob2b=(LPBYTE)LocalAlloc(LMEM_FIXED, dwBlob2Size); if (pBlob1b && pBlob2b) { memcpy(pBlob1b, pBlob1, dwBlob1Size); memcpy(pBlob2b, pBlob2, dwBlob2Size); CStringList_New(ppsl); if (*ppsl) { hr = (*ppsl)->ReadFromBlobs(&pBlob1b, dwBlob1Size, &pBlob2b, dwBlob2Size); if (SUCCEEDED(hr)) { INT64 i; if (FAILED((*ppsl)->GetListData(&i)) || ((fPasswordList && !(i & LIST_DATA_PASSWORD)) || (!fPasswordList && (i & LIST_DATA_PASSWORD)))) { TraceMsg(TF_WARNING|TF_IFORMS, "IForms: Password/nonpassword lists mixed up"); hr = E_FAIL; // don't allow malicious site to access PW data } } if (FAILED(hr)) { delete *ppsl; *ppsl=NULL; } } } else { if (pBlob1b) { LocalFree(pBlob1b); pBlob1b = NULL; } if (pBlob2b) { LocalFree(pBlob2b); pBlob2b = NULL; } } } else { TraceMsg(TF_IFORMS, "Failed reading Blob2. hr=%x", hr); } } else { TraceMsg(TF_IFORMS, "Failed reading Blob1. hr=%x", hr); } LocalFree(pwszValue); pwszValue = NULL; if (pBlob1) { CoTaskMemFree(pBlob1); pBlob1 = NULL; } if (pBlob2) { CoTaskMemFree(pBlob2); pBlob2 = NULL; } } } TraceMsg(TF_IFORMS, "-ReadFromStore"); return hr; } HRESULT CIntelliForms::DeleteFromStore(LPCWSTR pwszName) { HRESULT hr=E_FAIL; if (SUCCEEDED(CreatePStore())) { HRESULT hr1, hr2; LPWSTR pwszValue; int iValLen = lstrlenW(c_szBlob1Value) + lstrlenW(pwszName) + 10; pwszValue = (LPWSTR) LocalAlloc(LMEM_FIXED, iValLen * sizeof(WCHAR)); if (pwszValue) { PST_PROMPTINFO promptInfo; promptInfo.cbSize = sizeof(promptInfo); promptInfo.dwPromptFlags = 0; promptInfo.hwndApp = NULL; promptInfo.szPrompt = NULL; wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob1Value); hr1 = m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0); wnsprintfW(pwszValue, iValLen, L"%s:%s", pwszName, c_szBlob2Value); hr2 = m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszValue, &promptInfo, 0); if (SUCCEEDED(hr1) && SUCCEEDED(hr2)) { hr = S_OK; } LocalFree(pwszValue); pwszValue = NULL; } } return hr; } const int c_iEnumSize=256; HRESULT CIntelliForms::ClearStore(DWORD dwClear) { BOOL fReleasePStore = (m_pPStore == NULL); ASSERT(dwClear <= 2); if (dwClear > 2) { return E_INVALIDARG; } if (SUCCEEDED(CreatePStoreAndType())) { IEnumPStoreItems *pEnumItems; ULONG cFetched=0; do { if (SUCCEEDED(m_pPStore->EnumItems(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, 0, &pEnumItems))) { LPWSTR pwszName[c_iEnumSize]; PST_PROMPTINFO promptInfo; promptInfo.cbSize = sizeof(promptInfo); promptInfo.dwPromptFlags = 0; promptInfo.hwndApp = NULL; promptInfo.szPrompt = NULL; // Enumerator doesn't keep its state - deleting items while we enumerate makes us // miss some. It does support celt>1... but returns failure codes when it succeeds. cFetched = 0; pEnumItems->Next(c_iEnumSize, pwszName, &cFetched); if (cFetched) { for (ULONG i=0; iReadItem( PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, (pwszIndexName) ? pwszIndexName : pwszName[i], &dwBlob1Size, &pBlob1, &promptInfo, 0)) && pBlob1) { if (SUCCEEDED(CStringList::GetFlagsFromIndex(pBlob1, &iFlags))) { if (((iFlags & LIST_DATA_PASSWORD) && (dwClear == IECMDID_ARG_CLEAR_FORMS_PASSWORDS_ONLY)) || (!(iFlags & LIST_DATA_PASSWORD) && (dwClear == IECMDID_ARG_CLEAR_FORMS_ALL_BUT_PASSWORDS))) { // Delete this item fDelete = TRUE; } } CoTaskMemFree(pBlob1); } else { // The index is already deleted fDelete = TRUE; } if (pwszIndexName) { LocalFree(pwszIndexName); pwszIndexName = NULL; } } } // if (dwClear != CLEAR_INTELLIFORMS_ALL) if (fDelete) { m_pPStore->DeleteItem(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, pwszName[i], &promptInfo, 0); } } // for (iHack) CoTaskMemFree(pwszName[i]); } // if (pwszName[i]) } } pEnumItems->Release(); } } while (cFetched == c_iEnumSize); // In case we didn't cover everything in one pass if (dwClear == IECMDID_ARG_CLEAR_FORMS_ALL) { m_pPStore->DeleteSubtype(PST_KEY_CURRENT_USER, &c_PStoreType, &m_guidUserId, 0); m_pPStore->DeleteType(PST_KEY_CURRENT_USER, &c_PStoreType, 0); } if ((dwClear == IECMDID_ARG_CLEAR_FORMS_ALL) || (dwClear == IECMDID_ARG_CLEAR_FORMS_PASSWORDS_ONLY)) { // Delete the urlhash key storing which urls we have passwords saved for SHDeleteKey(HKEY_CURRENT_USER, c_szRegKeyIntelliForms); } TraceMsg(TF_IFORMS, "IForms: ClearStore cleared at least %d entries", cFetched); } if (fReleasePStore) { ReleasePStore(); } return S_OK; } // static: Get the name from an input element - uses VCARD_NAME attribute if present. HRESULT CIntelliForms::GetName(IHTMLInputTextElement *pTextEle, BSTR *pbstrName) { IHTMLElement *pEle=NULL; *pbstrName = NULL; pTextEle->QueryInterface(IID_IHTMLElement, (void **)&pEle); if (pEle) { BSTR bstrAttr = SysAllocString(L"VCARD_NAME"); if (bstrAttr) { VARIANT var; var.vt = VT_EMPTY; pEle->getAttribute(bstrAttr, 0, &var); if (var.vt == VT_BSTR && var.bstrVal) { *pbstrName = var.bstrVal; } else { VariantClear(&var); } SysFreeString(bstrAttr); } pEle->Release(); } if (!*pbstrName) { pTextEle->get_name(pbstrName); } // Convert the name to lowercase if (*pbstrName) { // Call "MyToLower" instead if (g_fRunningOnNT) { CharLowerBuffW(*pbstrName, lstrlenW(*pbstrName)); } else { // Ideally we would use the code page contained in the string instead of // the system code page. CHAR chBuf[MAX_PATH]; SHUnicodeToAnsi(*pbstrName, chBuf, ARRAYSIZE(chBuf)); CharLowerBuffA(chBuf, lstrlenA(chBuf)); SHAnsiToUnicode(chBuf, *pbstrName, SysStringLen(*pbstrName)+1); } } return (*pbstrName) ? S_OK : E_FAIL; } // Called when script calls window.external.AutoCompleteSaveForm HRESULT CIntelliForms::ScriptSubmit(IHTMLFormElement *pForm) { HRESULT hr = E_FAIL; if (pForm) { hr = HandleFormSubmit(pForm); } return SUCCEEDED(hr) ? S_OK : S_FALSE; } // Called when user changes a text field. Mark it "dirty" and sink submit event for form HRESULT CIntelliForms::UserInput(IHTMLInputTextElement *pTextEle) { AddToElementList(pTextEle); IHTMLFormElement *pForm=NULL; pTextEle->get_form(&pForm); if (pForm) { if (S_OK == AddToFormList(pForm)) { AttachToForm(pForm); } pForm->Release(); } else { TraceMsg(TF_WARNING|TF_IFORMS, "Iforms: pITE->get_form() returned NULL!"); } return S_OK; } HRESULT CIntelliForms::AddToElementList(IHTMLInputTextElement *pITE) { if (m_hdpaElements) { if (SUCCEEDED(FindInElementList(pITE))) { return S_FALSE; } } else { m_hdpaElements = DPA_Create(4); } if (m_hdpaElements) { TraceMsg(TF_IFORMS, "CIntelliForms::AddToElementList adding"); if (DPA_AppendPtr(m_hdpaElements, pITE) >= 0) { pITE->AddRef(); return S_OK; } } return E_OUTOFMEMORY; } HRESULT CIntelliForms::FindInElementList(IHTMLInputTextElement *pITE) { IUnknown *punk; HRESULT hr = E_FAIL; pITE->QueryInterface(IID_IUnknown, (void **)&punk); if (m_hdpaElements) { for (int i=DPA_GetPtrCount(m_hdpaElements)-1; i>=0; i--) { IUnknown *punk2; ((IUnknown *)DPA_FastGetPtr(m_hdpaElements, i))->QueryInterface(IID_IUnknown, (void **)&punk2); if (punk == punk2) { punk2->Release(); break; } punk2->Release(); } if (i >= 0) { hr = S_OK; } } punk->Release(); return hr; } void CIntelliForms::FreeElementList() { if (m_hdpaElements) { for (int i=DPA_GetPtrCount(m_hdpaElements)-1; i>=0; i--) { ((IUnknown *)(DPA_FastGetPtr(m_hdpaElements, i)))->Release(); } DPA_Destroy(m_hdpaElements); m_hdpaElements=NULL; } } HRESULT CIntelliForms::AddToFormList(IHTMLFormElement *pFormEle) { if (m_hdpaForms) { if (SUCCEEDED(FindInFormList(pFormEle))) { return S_FALSE; } } else { m_hdpaForms = DPA_Create(2); } if (m_hdpaForms) { if (DPA_AppendPtr(m_hdpaForms, pFormEle) >= 0) { TraceMsg(TF_IFORMS, "CIntelliForms::AddToFormList adding"); pFormEle->AddRef(); return S_OK; } } return E_OUTOFMEMORY; } HRESULT CIntelliForms::FindInFormList(IHTMLFormElement *pFormEle) { IUnknown *punk; HRESULT hr = E_FAIL; pFormEle->QueryInterface(IID_IUnknown, (void **)&punk); if (m_hdpaForms) { for (int i=DPA_GetPtrCount(m_hdpaForms)-1; i>=0; i--) { IUnknown *punk2; ((IUnknown *)DPA_FastGetPtr(m_hdpaForms, i))->QueryInterface(IID_IUnknown, (void **)&punk2); if (punk == punk2) { punk2->Release(); break; } punk2->Release(); } if (i >= 0) { hr = S_OK; } } punk->Release(); return hr; } void CIntelliForms::FreeFormList() { if (m_hdpaForms) { for (int i=DPA_GetPtrCount(m_hdpaForms)-1; i>=0; i--) { ((IUnknown *)(DPA_FastGetPtr(m_hdpaForms, i)))->Release(); } DPA_Destroy(m_hdpaForms); m_hdpaForms = NULL; } } //========================================================================= // // Event sinking class // // We simply implement IDispatch and make a call into our parent when // we receive a sinked event. // //========================================================================= CIntelliForms::CEventSink::CEventSink(CEventSinkCallback *pParent) { TraceMsg(TF_IFORMS, "CIntelliForms::CEventSink::CEventSink"); DllAddRef(); m_cRef = 1; m_pParent = pParent; } CIntelliForms::CEventSink::~CEventSink() { TraceMsg(TF_IFORMS, "CIntelliForms::CEventSink::~CEventSink"); ASSERT( m_cRef == 0 ); DllRelease(); } STDMETHODIMP CIntelliForms::CEventSink::QueryInterface(REFIID riid, void **ppv) { *ppv = NULL; if ((IID_IDispatch == riid) || (IID_IUnknown == riid)) { *ppv = (IDispatch *)this; } if (NULL != *ppv) { ((IUnknown *)*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CIntelliForms::CEventSink::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) CIntelliForms::CEventSink::Release(void) { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } HRESULT CIntelliForms::CEventSink::SinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents) { VARIANT_BOOL bSuccess = VARIANT_TRUE; for (int i=0; iattachEvent(bstrEvent, (IDispatch *)this, &bSuccess); SysFreeString(bstrEvent); } else { bSuccess = VARIANT_FALSE; } if (!bSuccess) break; } return (bSuccess) ? S_OK : E_FAIL; } HRESULT CIntelliForms::CEventSink::SinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents) { VARIANT_BOOL bSuccess = VARIANT_TRUE; for (int i=0; iattachEvent(bstrEvent, (IDispatch *)this, &bSuccess); SysFreeString(bstrEvent); } else { bSuccess = VARIANT_FALSE; } if (!bSuccess) break; } return (bSuccess) ? S_OK : E_FAIL; } HRESULT CIntelliForms::CEventSink::UnSinkEvents(IHTMLElement2 *pEle2, int iNum, EVENTS *pEvents) { for (int i=0; idetachEvent(bstrEvent, (IDispatch *)this); SysFreeString(bstrEvent); } } return S_OK; } HRESULT CIntelliForms::CEventSink::UnSinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents) { for (int i=0; idetachEvent(bstrEvent, (IDispatch *)this); SysFreeString(bstrEvent); } } return S_OK; } // IDispatch STDMETHODIMP CIntelliForms::CEventSink::GetTypeInfoCount(UINT* /*pctinfo*/) { return E_NOTIMPL; } STDMETHODIMP CIntelliForms::CEventSink::GetTypeInfo(/* [in] */ UINT /*iTInfo*/, /* [in] */ LCID /*lcid*/, /* [out] */ ITypeInfo** /*ppTInfo*/) { return E_NOTIMPL; } STDMETHODIMP CIntelliForms::CEventSink::GetIDsOfNames( REFIID riid, OLECHAR** rgszNames, UINT cNames, LCID lcid, DISPID* rgDispId) { return E_NOTIMPL; } STDMETHODIMP CIntelliForms::CEventSink::Invoke( DISPID dispIdMember, REFIID, LCID, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult, EXCEPINFO*, UINT* puArgErr) { if (m_pParent && pDispParams && pDispParams->cArgs>=1) { if (pDispParams->rgvarg[0].vt == VT_DISPATCH) { IHTMLEventObj *pObj=NULL; if (SUCCEEDED(pDispParams->rgvarg[0].pdispVal->QueryInterface(IID_IHTMLEventObj, (void **)&pObj) && pObj)) { EVENTS Event=EVENT_BOGUS; BSTR bstrEvent=NULL; pObj->get_type(&bstrEvent); if (bstrEvent) { for (int i=0; iget_srcElement(&pEle); // EVENT_SCROLL comes from our window so we won't have an // element for it if (pEle || (Event == EVENT_SCROLL)) { // Call the event handler here m_pParent->HandleEvent(pEle, Event, pObj); if (pEle) { pEle->Release(); } } } pObj->Release(); } } } return S_OK; } //========================================================================= // // Event sinking class // // We implement IHTMLEditDesigner and make a call into our parent when // we receive any event. // //========================================================================= CIntelliForms::CEditEventSink::CEditEventSink(CEditEventSinkCallback *pParent) { TraceMsg(TF_IFORMS, "CIntelliForms::CEditEventSink::CEditEventSink"); DllAddRef(); m_cRef = 1; m_pParent = pParent; } CIntelliForms::CEditEventSink::~CEditEventSink() { TraceMsg(TF_IFORMS, "CIntelliForms::CEditEventSink::~CEditEventSink"); ASSERT(m_cRef == 0); ASSERT(!m_pEditServices); DllRelease(); } STDMETHODIMP CIntelliForms::CEditEventSink::QueryInterface(REFIID riid, void **ppv) { if ((IID_IHTMLEditDesigner == riid) || (IID_IUnknown == riid)) { *ppv = SAFECAST(this, IHTMLEditDesigner *); } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } STDMETHODIMP_(ULONG) CIntelliForms::CEditEventSink::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) CIntelliForms::CEditEventSink::Release(void) { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } HRESULT CIntelliForms::CEditEventSink::Attach(IUnknown *punkElement) { HRESULT hr = S_OK; // Detach from any existing element if (m_pEditServices) { m_pEditServices->RemoveDesigner(this); m_pEditServices->Release(); m_pEditServices = NULL; } // Attach to any new element if (punkElement) { hr = E_FAIL; IHTMLDocument2 *pDoc2 = NULL; GetStuffFromEle(punkElement, NULL, &pDoc2); if (pDoc2) { IServiceProvider *pSP = NULL; pDoc2->QueryInterface(IID_IServiceProvider, (void **)&pSP); if (pSP) { pSP->QueryService(SID_SHTMLEditServices, IID_IHTMLEditServices, (void **)&m_pEditServices); pSP->Release(); } if (m_pEditServices) { hr = m_pEditServices->AddDesigner(this); } pDoc2->Release(); } } return hr; } HRESULT CIntelliForms::CEditEventSink::PreHandleEvent(DISPID inEvtDispId, IHTMLEventObj *pIEventObj) { if (m_pParent) { return m_pParent->PreHandleEvent(inEvtDispId, pIEventObj); } return S_FALSE; } HRESULT CIntelliForms::CEditEventSink::PostHandleEvent(DISPID inEvtDispId, IHTMLEventObj *pIEventObj) { return S_FALSE; } HRESULT CIntelliForms::CEditEventSink::TranslateAccelerator(DISPID inEvtDispId, IHTMLEventObj *pIEventObj) { return S_FALSE; } //========================================================================= // // AutoSuggest class // // Handles connecting and disconnecting the AutoComplete object, as well // as translating between Trident OM and Edit window messages //========================================================================= CIntelliForms::CAutoSuggest::CAutoSuggest(CIntelliForms *pParent, BOOL fEnabled, BOOL fEnabledPW) { TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::CAutoSuggest"); m_pParent = pParent; m_fEnabled = fEnabled; m_fEnabledPW = fEnabledPW; ASSERT(m_pEventSink == NULL); ASSERT(m_pAutoComplete == NULL); ASSERT(m_hwndEdit == NULL); ASSERT(m_pTextEle == NULL); // // bug 81414 : To avoid clashing with app messages used by the edit window, we // use registered messages. // m_uMsgItemActivate = RegisterWindowMessageA("AC_ItemActivate"); if (m_uMsgItemActivate == 0) { m_uMsgItemActivate = WM_APP + 301; } // Register our window class if necessary if (!s_fRegisteredWndClass) { s_fRegisteredWndClass = TRUE; WNDCLASSEXW wndclass = { sizeof(WNDCLASSEX), 0, CIntelliForms::CAutoSuggest::WndProc, 0, sizeof(DWORD_PTR), g_hinst, NULL, NULL, NULL, NULL, c_szEditWndClass }; if (!RegisterClassEx(&wndclass)) { TraceMsg(TF_IFORMS, "Intelliforms failed to register wnd class!"); } } } CIntelliForms::CAutoSuggest::~CAutoSuggest() { TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::~CAutoSuggest"); CleanUp(); } HRESULT CIntelliForms::CAutoSuggest::CleanUp() { SetParent(NULL); DetachFromInput(); return S_OK; } // List of all events we sink for an individual INPUT tag // post IE5.5 we can use CEditEventSink instead of CEventSink for all of these events. CEventSinkCallback::EVENTS CIntelliForms::CAutoSuggest::s_EventsToSink[] = { EVENT_KEYPRESS, EVENT_KEYDOWN, EVENT_MOUSEDOWN, EVENT_DBLCLICK, EVENT_FOCUS, EVENT_BLUR, }; HRESULT CIntelliForms::CAutoSuggest::AttachToInput(IHTMLInputTextElement *pTextEle) { HRESULT hr; TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::AttachToInput"); if (!pTextEle) return E_INVALIDARG; hr = DetachFromInput(); if (SUCCEEDED(hr)) { m_pTextEle = pTextEle; pTextEle->AddRef(); if (!m_pEventSink) { m_pEventSink = new CEventSink(this); if (!m_pEventSink) { hr = E_OUTOFMEMORY; } } if (SUCCEEDED(hr)) { // Hook up our event sink IHTMLElement2 *pEle2=NULL; hr = pTextEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2); if (pEle2) { hr = m_pEventSink->SinkEvents(pEle2, ARRAYSIZE(s_EventsToSink), s_EventsToSink); pEle2->Release(); } } } if (FAILED(hr)) { TraceMsg(TF_IFORMS, "IForms: AttachToInput failed"); DetachFromInput(); } return hr; } HRESULT CIntelliForms::CAutoSuggest::DetachFromInput() { if (!m_pTextEle) { return S_FALSE; } TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::DetachFromInput"); // Auto Fill Password here, since we get ACTIVEELEMENT change before blur event BSTR bstrUsername=NULL; m_pTextEle->get_value(&bstrUsername); if (bstrUsername) { CheckAutoFillPassword(bstrUsername); SysFreeString(bstrUsername); } if (m_bstrLastUsername) { SysFreeString(m_bstrLastUsername); m_bstrLastUsername=NULL; } if (m_hwndEdit) { // This is for subclass wndproc SendMessage(m_hwndEdit, WM_KILLFOCUS, 0, 0); } if (m_pEnumString) { m_pEnumString->UnInit(); m_pEnumString->Release(); m_pEnumString = NULL; } if (m_pEventSink) { IHTMLElement2 *pEle2=NULL; m_pTextEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2); if (pEle2) { m_pEventSink->UnSinkEvents(pEle2, ARRAYSIZE(s_EventsToSink), s_EventsToSink); pEle2->Release(); } m_pEventSink->SetParent(NULL); m_pEventSink->Release(); m_pEventSink=NULL; } SAFERELEASE(m_pAutoComplete); SAFERELEASE(m_pAutoCompleteDD); if (m_hwndEdit) { DestroyWindow(m_hwndEdit); m_hwndEdit = NULL; } SAFERELEASE(m_pTextEle); m_fInitAutoComplete = FALSE; return S_OK; } // Creates autocomplete and string enumerator. HRESULT CIntelliForms::CAutoSuggest::CreateAutoComplete() { if (m_fInitAutoComplete) { return (m_pAutoCompleteDD != NULL) ? S_OK : E_FAIL; } HRESULT hr = S_OK; ASSERT(!m_hwndEdit && !m_pEnumString && !m_pAutoComplete && !m_pAutoCompleteDD); // Create the edit window #ifndef UNIX m_hwndEdit = CreateWindowEx(0, c_szEditWndClass, TEXT("IntelliFormProxy"), WS_POPUP, #else m_hwndEdit = CreateWindowEx(WS_EX_MW_UNMANAGED_WINDOW, c_szEditWndClass, TEXT("IntelliFormProxy"), WS_POPUP, #endif 300, 200, 200, 50, m_pParent->m_hwndBrowser, NULL, g_hinst, this); if (!m_hwndEdit) { hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { // Create our enumerator m_pEnumString = new CEnumString(); if (m_pEnumString) { m_pEnumString->Init(m_pTextEle, m_pParent); // Create the AutoComplete Object if (!m_pAutoComplete) { hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete2, (void **)&m_pAutoComplete); if (m_pAutoComplete) { m_pAutoComplete->QueryInterface(IID_IAutoCompleteDropDown, (void **)&m_pAutoCompleteDD); if (!m_pAutoCompleteDD) { SAFERELEASE(m_pAutoComplete); } } } if (m_pAutoComplete) { hr = m_pAutoComplete->Init(m_hwndEdit, (IUnknown *) m_pEnumString, NULL, NULL); DWORD dwOptions = ACO_AUTOSUGGEST | ACO_UPDOWNKEYDROPSLIST; // Add the RTLREADING option to the dropdown, if the element is RTL BSTR bstrDir = NULL; IHTMLElement2 *pEle2=NULL; m_pTextEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2); if (pEle2) { pEle2->get_dir(&bstrDir); pEle2->Release(); } if (bstrDir) { if (!StrCmpIW(bstrDir, L"RTL")) { dwOptions |= ACO_RTLREADING; } SysFreeString(bstrDir); } m_pAutoComplete->SetOptions(dwOptions); } } } m_fInitAutoComplete = TRUE; ASSERT_MSG(SUCCEEDED(hr), "IForms: CreateAutoComplete failed"); return hr; } void CIntelliForms::CAutoSuggest::CheckAutoFillPassword(LPCWSTR pwszUsername) { // We don't autofill their password unless we know they've hit a key if (m_pParent && m_fEnabledPW && m_fAllowAutoFillPW) { if (m_bstrLastUsername && !StrCmpCW(pwszUsername, m_bstrLastUsername)) { return; } SysFreeString(m_bstrLastUsername); m_bstrLastUsername = SysAllocString(pwszUsername); m_pParent->AutoFillPassword(m_pTextEle, pwszUsername); } } HRESULT GetScreenCoordinates(IUnknown *punkEle, HWND hwnd, long *plLeft, long *plTop, long *plWidth, long *plHeight) { long lScreenLeft=0, lScreenTop=0; HRESULT hr = E_FAIL; *plLeft = *plTop = *plWidth = *plHeight = 0; IHTMLElement2 *pEle2; if (SUCCEEDED(punkEle->QueryInterface(IID_IHTMLElement2, (void **)&pEle2)) && pEle2) { IHTMLRect *pRect=NULL; if (SUCCEEDED(pEle2->getBoundingClientRect(&pRect)) && pRect) { IHTMLWindow2 *pWin2; long lLeft, lRight, lTop, lBottom; pRect->get_left(&lLeft); pRect->get_right(&lRight); pRect->get_top(&lTop); pRect->get_bottom(&lBottom); lBottom -= 2; // put dropdown on top of edit box if (lBottom < lTop) { lBottom = lTop; } if (lTop >= 0 && lLeft >= 0) { GetStuffFromEle(punkEle, &pWin2, NULL); if (pWin2) { IHTMLWindow3 *pWin3; if (SUCCEEDED(pWin2->QueryInterface(IID_IHTMLWindow3, (void **)&pWin3)) && pWin3) { IHTMLScreen *pScreen = NULL; RECT rcBrowserWnd; pWin3->get_screenLeft(&lScreenLeft); pWin3->get_screenTop(&lScreenTop); // GetClientRect & the screen_* APIs return document coordinates. // We're position using device coordinates. // Use document (currently 96DPI) and device resolutions & transform pWin2->get_screen(&pScreen); if (pScreen) { IHTMLScreen2 * pScreen2 = NULL; if (SUCCEEDED(pScreen->QueryInterface(IID_IHTMLScreen2, (void **)&pScreen2))) { if (pScreen2) { long xDeviceDPI, yDeviceDPI, xLogicalDPI, yLogicalDPI; pScreen2->get_deviceXDPI(&xDeviceDPI); pScreen2->get_deviceYDPI(&yDeviceDPI); pScreen2->get_logicalXDPI(&xLogicalDPI); pScreen2->get_logicalYDPI(&yLogicalDPI); lBottom = (lBottom * yDeviceDPI) / yLogicalDPI; lTop = (lTop * yDeviceDPI) / yLogicalDPI; lScreenTop = (lScreenTop * yDeviceDPI) / yLogicalDPI; lLeft = (lLeft * xDeviceDPI) / xLogicalDPI; lRight = (lRight * xDeviceDPI) / xLogicalDPI; lScreenLeft = (lScreenLeft * xDeviceDPI) / xLogicalDPI; pScreen2->Release(); } } pScreen->Release(); } if (GetWindowRect(hwnd, &rcBrowserWnd)) { // Clip the right edge to the window if (lRight+lScreenLeft > rcBrowserWnd.right) { lRight = rcBrowserWnd.right - lScreenLeft; } *plLeft = lScreenLeft + lLeft; *plWidth = lRight-lLeft; *plTop = lScreenTop + lTop; *plHeight = lBottom-lTop; hr = S_OK; if (*plWidth < MINIMUM_WIDTH) { // Primitive minimum width for now *plWidth = MINIMUM_WIDTH; } } pWin3->Release(); } pWin2->Release(); } } pRect->Release(); } pEle2->Release(); } return hr; } HRESULT CIntelliForms::CAutoSuggest::UpdateDropdownPosition() { if (m_pTextEle && m_pParent && m_hwndEdit) { long lLeft, lTop, lWidth, lHeight; if (SUCCEEDED(GetScreenCoordinates(m_pTextEle, m_pParent->m_hwndBrowser, &lLeft, &lTop, &lWidth, &lHeight))) { MoveWindow(m_hwndEdit, lLeft, lTop, lWidth, lHeight, FALSE); } else { // Send "escape" key to autocomplete so that it hides the dropdown. // This will happen if dropdown moves outside of parent window, for example. SendMessage(m_hwndEdit, IF_CHAR, (WPARAM) VK_ESCAPE, 0); } } return S_OK; } HRESULT CIntelliForms::CAutoSuggest::HandleEvent(IHTMLElement *pEle, EVENTS Event, IHTMLEventObj *pEventObj) { TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::HandleEvent Event=%ws", EventsToSink[Event].pwszEventName); ASSERT(SHIsSameObject(pEle, m_pTextEle)); long lKey = 0; BOOL fIsComposition = FALSE; if (!m_pParent) { TraceMsg(TF_WARNING|TF_IFORMS, "IForms autosuggest receiving events while invalid"); return E_FAIL; } if (Event == EVENT_KEYPRESS || Event == EVENT_KEYDOWN) { pEventObj->get_keyCode(&lKey); } if (Event == EVENT_COMPOSITION) { fIsComposition = TRUE; Event = EVENT_KEYPRESS; // Pretend to be a "keypress" for various processing below } if (Event == EVENT_NOTIFY) { // Send WM_IME_NOTIFY to AutoComplete so it can hide the dropdown // if necessary IHTMLEventObj3 *pObj3 = NULL; pEventObj->QueryInterface(IID_PPV_ARG(IHTMLEventObj3, &pObj3)); if (pObj3) { LONG_PTR wParam = 0; pObj3->get_imeNotifyCommand(&wParam); SendMessage(m_hwndEdit, WM_IME_NOTIFY, (WPARAM)wParam, 0); pObj3->Release(); } return S_OK; } if (!m_fEnabled && !m_fEnabledPW) { // If the dropdown isn't enabled, our only purpose is to tell Intelliforms when // user activity occurs for the first-time enable dialog box. if (Event == EVENT_KEYPRESS && lKey != VK_TAB) { // Add this element to the master list so we save it when we submit // and sink the submit event for this element's form MarkDirty(); } return S_OK; } if (Event == EVENT_KEYDOWN || Event == EVENT_KEYPRESS || Event == EVENT_MOUSEDOWN || Event == EVENT_DBLCLICK) { m_fAllowAutoFillPW = TRUE; // Create our autocomplete object if it hasn't happened yet. // If it's "tab" we don't create it; we're leaving the field if (lKey != VK_TAB) { if (FAILED(CreateAutoComplete())) return E_FAIL; } else { // Add this element to the master list so we save it when we submit // and sink the submit event for this element's form MarkDirty(); } ASSERT((m_pEnumString && m_hwndEdit) || (lKey==VK_TAB)); } // If AutoComplete hasn't been initialized there's nothing for us to do if (!m_pAutoCompleteDD || !m_hwndEdit) { return E_FAIL; } // Update the position of our hidden edit box long lLeft, lTop, lWidth, lHeight; // call UpdateDropdownPosition instead if (SUCCEEDED(GetScreenCoordinates(pEle, m_pParent->m_hwndBrowser, &lLeft, &lTop, &lWidth, &lHeight))) { MoveWindow(m_hwndEdit, lLeft, lTop, lWidth, lHeight, FALSE); } switch (Event) { case EVENT_FOCUS : SendMessage(m_hwndEdit, WM_SETFOCUS, 0, 0); break; case EVENT_BLUR: { if (m_hwndEdit) { SendMessage(m_hwndEdit, WM_KILLFOCUS, 0, 0); } // ensure that script hasn't changed value of edit field? BSTR bstrUsername=NULL; m_pTextEle->get_value(&bstrUsername); if (bstrUsername) { CheckAutoFillPassword(bstrUsername); SysFreeString(bstrUsername); } } break; case EVENT_MOUSEDOWN: case EVENT_DBLCLICK: { // If the dropdown is invisible, give AutoComplete a downarrow long lButton=0; pEventObj->get_button(&lButton); if ((Event == EVENT_DBLCLICK) || (lButton & 1)) // Left button down? { DWORD dwFlags; if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(&dwFlags, NULL)) && !(dwFlags & ACDD_VISIBLE)) { TraceMsg(TF_IFORMS, "IForms sending downarrow because of mouse click"); PostMessage(m_hwndEdit, IF_KEYDOWN, (WPARAM)VK_DOWN, 0); m_fEscapeHit = FALSE; } } } break; case EVENT_KEYPRESS: { // Add this element to the master list so we save it when we submit // and sink the submit event for this element's form MarkDirty(); // Ignore ctrl-enter (quickcomplete) (may be unnecessary) if (lKey == VK_RETURN) { VARIANT_BOOL bCtrl; if (SUCCEEDED(pEventObj->get_ctrlKey(&bCtrl)) && bCtrl) { lKey = 0; } } if (lKey != 0) { if (lKey == m_lCancelKeyPress) { // tell MSHTML to ignore this keystroke (may be tab, enter, escape) TraceMsg(TF_IFORMS, "Intelliforms cancelling default action for EVENT_KEYPRESS=%d", lKey); VARIANT v; v.vt = VT_BOOL; v.boolVal = VARIANT_FALSE; pEventObj->put_returnValue(v); if(!(lKey == VK_DOWN || lKey == VK_UP)) pEventObj->put_cancelBubble(VARIANT_TRUE); } m_lCancelKeyPress = 0; // Tell AutoComplete about this keystroke if (!m_fEscapeHit) { PostMessage(m_hwndEdit, IF_CHAR, (WPARAM)lKey, 0); } } if (fIsComposition) { // Tell AutoComplete about the new string. This must be a Post so that // Trident handles the event before we send the WM_CHAR to browseui. PostMessage(m_hwndEdit, IF_IME_COMPOSITION, 0, 0); } } break; case EVENT_KEYDOWN: { long lKey; BOOL fCancelEvent=FALSE, // Cancel default MSHTML action? fForwardKeystroke=TRUE; // Forward keystroke to AutoComplete? pEventObj->get_keyCode(&lKey); if (m_fEscapeHit) { // They dismissed the dropdown; don't bring it back unless they ask for it if (lKey == VK_DOWN) { m_fEscapeHit = FALSE; } else { fForwardKeystroke = FALSE; } } if (lKey != 0) { if ((lKey == VK_RETURN) || (lKey == VK_TAB)) { fForwardKeystroke=FALSE; LPWSTR pwszString=NULL; if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(NULL, &pwszString)) && pwszString) { // User is inside dropdown fForwardKeystroke=TRUE; // Set this value into our edit field SetText(pwszString); // We will fill in their password if they asked for it in m_uMsgItemActivate if (lKey == VK_RETURN) { // Avoid submitting this form fCancelEvent = TRUE; } CoTaskMemFree(pwszString); } else if (lKey == VK_RETURN) { // User's gonna submit. Give 'em their password first. // ensure that script hasn't changed value of edit field? BSTR bstrUsername=NULL; m_pTextEle->get_value(&bstrUsername); if (bstrUsername) { CheckAutoFillPassword(bstrUsername); SysFreeString(bstrUsername); } } } else if (lKey == VK_DELETE) { LPWSTR pwszString=NULL; if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(NULL, &pwszString)) && pwszString) { // User is inside dropdown fForwardKeystroke=FALSE; // Delete this value from our string lists CStringList *psl=NULL; BSTR bstrName; CIntelliForms::GetName(m_pTextEle, &bstrName); if (bstrName) { int iIndex; if (SUCCEEDED(m_pParent->ReadFromStore(bstrName, &psl)) && SUCCEEDED(psl->FindString(pwszString, -1, &iIndex, FALSE))) { TraceMsg(TF_IFORMS, "IForms: Deleting string \"%ws\"", pwszString); psl->DeleteString(iIndex); // We deleted string. if (psl->NumStrings() > 0) { m_pParent->WriteToStore(bstrName, psl); } else { m_pParent->DeleteFromStore(bstrName); } } } SysFreeString(bstrName); if (psl) delete psl; // avoid deleting a character from the edit window; user was inside dropdown fCancelEvent = TRUE; // Check this url to see if we should maybe delete a password entry m_pParent->DeletePassword(pwszString); // Get AutoComplete to fill in the dropdown again m_pEnumString->ResetEnum(); m_pAutoCompleteDD->ResetEnumerator(); CoTaskMemFree(pwszString); } } if (lKey == VK_ESCAPE) { DWORD dwFlags; if (SUCCEEDED(m_pAutoCompleteDD->GetDropDownStatus(&dwFlags, NULL)) && (dwFlags & ACDD_VISIBLE)) { fCancelEvent = TRUE; m_fEscapeHit = TRUE; } } if (lKey == VK_DOWN || lKey == VK_UP) { // Cancel the MSHTML events. This will cause MSHTML to return // S_OK instead of S_FALSE from its TranslateAccelerator, and we // won't get multiple keystrokes in different panes fCancelEvent = TRUE; } if (fForwardKeystroke) { PostMessage(m_hwndEdit, IF_KEYDOWN, lKey, 0); if (lKey == VK_BACK) { // Never get OnKeyPress for this guy PostMessage(m_hwndEdit, IF_CHAR, lKey, 0); } } if (fCancelEvent) { TraceMsg(TF_IFORMS, "Intelliforms cancelling default action for EVENT_KEYDOWN=%d", lKey); m_lCancelKeyPress = lKey; // Cancel the EVENT_KEYPRESS when it comes VARIANT v; v.vt = VT_BOOL; v.boolVal = VARIANT_FALSE; pEventObj->put_returnValue(v); if(!(lKey == VK_DOWN || lKey == VK_UP)) pEventObj->put_cancelBubble(VARIANT_TRUE); } else { m_lCancelKeyPress = 0; } } } break; } return S_OK; } HRESULT CIntelliForms::CAutoSuggest::GetText(int cchTextMax, LPWSTR pszTextOut, LRESULT *lcchCopied) { *pszTextOut = TEXT('\0'); *lcchCopied = 0; if (m_pTextEle) { BSTR bstr=NULL; m_pTextEle->get_value(&bstr); if (bstr) { StrCpyN(pszTextOut, bstr, cchTextMax); *lcchCopied = lstrlenW(pszTextOut); // needed for NT SysFreeString(bstr); } } return (*pszTextOut) ? S_OK : E_FAIL; } HRESULT CIntelliForms::CAutoSuggest::GetTextLength(int *pcch) { *pcch = 0; if (m_pTextEle) { BSTR bstr=NULL; m_pTextEle->get_value(&bstr); if (bstr) { *pcch = SysStringLen(bstr); SysFreeString(bstr); } } return S_OK; } HRESULT CIntelliForms::CAutoSuggest::SetText(LPCWSTR pszTextIn) { if (m_pTextEle && pszTextIn) { BSTR bstr=SysAllocString(pszTextIn); if (bstr) { // Even though we know we already have this string in our dropdown, mark // it as dirty so that we sink submit event; can be necessary in saved // password situation. MarkDirty(); // Make sure we don't put a string longer than the max length in this field long lMaxLen=-1; m_pTextEle->get_maxLength(&lMaxLen); if ((lMaxLen >= 0) && (lstrlenW(bstr) > lMaxLen)) { bstr[lMaxLen] = L'\0'; } m_pTextEle->put_value(bstr); SysFreeString(bstr); } } TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::SetText \"%ws\"", pszTextIn); return S_OK; } #define MY_GWL_THISPTR 0 LRESULT CALLBACK CIntelliForms::CAutoSuggest::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { CIntelliForms::CAutoSuggest *pThis = (CIntelliForms::CAutoSuggest *)GetWindowLongPtr(hwnd, MY_GWL_THISPTR); switch (uMsg) { case WM_CREATE: { LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam; if (!pcs || !(pcs->lpCreateParams)) { return -1; } SetWindowLongPtr(hwnd, MY_GWL_THISPTR, (LONG_PTR) pcs->lpCreateParams); return 0; } case WM_GETTEXT: if (pThis) { LRESULT lcchCopied=0; if (g_fRunningOnNT) { pThis->GetText((int)wParam, (LPWSTR) lParam, &lcchCopied); } else { // We are actually an ANSI window. Convert. LPWSTR pwszOutBuf = (LPWSTR) LocalAlloc(LPTR, (wParam+1)*sizeof(WCHAR)); if (pwszOutBuf) { pThis->GetText((int)wParam, pwszOutBuf, &lcchCopied); SHUnicodeToAnsi(pwszOutBuf, (LPSTR) lParam, (int)(wParam+1)); LocalFree((HLOCAL)pwszOutBuf); pwszOutBuf = NULL; } } return lcchCopied; } return 0; case WM_GETTEXTLENGTH: if (pThis) { int iLen; pThis->GetTextLength(&iLen); return iLen; } return 0; case EM_GETSEL: // Must return zeroes here or autocomp will use uninitialized // values and crash if (wParam) (*(DWORD *)wParam) = 0; if (lParam) (*(DWORD *)lParam) = 0; break; case IF_IME_COMPOSITION: // Forward a WM_CHAR. Autocomplete will notice that the rest of the string // has changed if necessary (it does a GetText) SendMessage(hwnd, WM_CHAR, 32, 0); break; case IF_CHAR: SendMessage(hwnd, WM_CHAR, wParam, lParam); break; case IF_KEYDOWN: SendMessage(hwnd, WM_KEYDOWN, wParam, lParam); break; case WM_KEYDOWN: case WM_CHAR: return 0; // eat it (see notes at top of file) default: // Check registered message if (pThis && uMsg == pThis->m_uMsgItemActivate) { TraceMsg(TF_IFORMS, "IForms: Received AM_ITEMACTIVATE(WM_APP+2)"); pThis->SetText((LPCWSTR)lParam); pThis->CheckAutoFillPassword((LPCWSTR)lParam); return 0; } return DefWindowProc(hwnd, uMsg, wParam, lParam); } return 1; } CIntelliForms::CAutoSuggest::CEnumString::CEnumString() { // TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::CEnumString::CEnumString"); DllAddRef(); InitializeCriticalSection(&m_crit); m_cRef = 1; } CIntelliForms::CAutoSuggest::CEnumString::~CEnumString() { // TraceMsg(TF_IFORMS, "CIntelliForms::CAutoSuggest::CEnumString::~CEnumString"); if (m_pslMain) { delete m_pslMain; } SysFreeString(m_bstrName); if (m_pszOpsValue) { CoTaskMemFree(m_pszOpsValue); } DeleteCriticalSection(&m_crit); DllRelease(); } HRESULT CIntelliForms::CAutoSuggest::CEnumString::Init(IHTMLInputTextElement *pInputEle, CIntelliForms *pIntelliForms) { if (m_fInit || // Can only init once !pInputEle || !pIntelliForms) // Need both pointers { return E_FAIL; } m_fInit=TRUE; m_pIntelliForms = pIntelliForms; // Take care of things that must be done on the main thread. Autocomplete will // call us on a secondary thread to do the enumeration. CIntelliForms::GetName(pInputEle, &m_bstrName); if (m_bstrName && m_bstrName[0]) { // See if this specifies the "vcard." format if (IsEnabledInCPL() && !StrCmpNICW(m_bstrName, c_wszVCardPrefix, ARRAYSIZE(c_wszVCardPrefix)-1)) { // It does. Retrieve string from the profile assistant store. IHTMLWindow2 *pWin2 = NULL; IServiceProvider *pQS = NULL; // QS up to get the shdocvw IHTMLWindow2 instead of NF trident's pInputEle->QueryInterface(IID_IServiceProvider, (void **)&pQS); if (pQS) { pQS->QueryService(IID_IHTMLWindow2, IID_IHTMLWindow2, (void **)&pWin2); pQS->Release(); } if (pWin2) { IOmNavigator *pNav=NULL; pWin2->get_navigator(&pNav); if (pNav) { IHTMLOpsProfile *pProfile=NULL; pNav->get_userProfile(&pProfile); if (pProfile) { IOpsProfileSimple *pSimple=NULL; pProfile->QueryInterface(IID_IOpsProfileSimple, (void **)&pSimple); if (pSimple) { pSimple->ReadProperties(1, &m_bstrName, &m_pszOpsValue); pSimple->Release(); } pProfile->Release(); } pNav->Release(); } pWin2->Release(); } } } return S_OK; } void CIntelliForms::CAutoSuggest::CEnumString::UnInit() { EnterCriticalSection(&m_crit); m_pIntelliForms = NULL; LeaveCriticalSection(&m_crit); } HRESULT CIntelliForms::CAutoSuggest::CEnumString::ResetEnum() { EnterCriticalSection(&m_crit); if (m_pslMain) { delete m_pslMain; m_pslMain = NULL; } m_fFilledStrings = FALSE; LeaveCriticalSection(&m_crit); return S_OK; } STDMETHODIMP CIntelliForms::CAutoSuggest::CEnumString::QueryInterface(REFIID riid, void **ppv) { *ppv = NULL; if ((IID_IEnumString == riid) || (IID_IUnknown == riid)) { *ppv = (IEnumString *)this; } if (NULL != *ppv) { ((IUnknown *)*ppv)->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CIntelliForms::CAutoSuggest::CEnumString::AddRef(void) { return InterlockedIncrement(&m_cRef); } STDMETHODIMP_(ULONG) CIntelliForms::CAutoSuggest::CEnumString::Release(void) { if (InterlockedDecrement(&m_cRef) != 0) { return 1; } delete this; return 0; } STDMETHODIMP CIntelliForms::CAutoSuggest::CEnumString::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched) { EnterCriticalSection(&m_crit); if (!m_fFilledStrings) { FillEnumerator(); } if (m_pslMain) { int iNewPtr = m_iPtr + celt; if (iNewPtr > m_pslMain->NumStrings()) { iNewPtr = m_pslMain->NumStrings(); } *pceltFetched = iNewPtr - m_iPtr; LPOLESTR lpstr; for (; m_iPtr < iNewPtr; m_iPtr ++) { m_pslMain->GetTaskAllocString(m_iPtr, &lpstr); if (!lpstr) break; *(rgelt ++) = lpstr; } if (m_iPtr < iNewPtr) { *pceltFetched += (m_iPtr - iNewPtr); } } LeaveCriticalSection(&m_crit); if (!m_pslMain) { return E_FAIL; } return (*pceltFetched) ? S_OK : S_FALSE; } STDMETHODIMP CIntelliForms::CAutoSuggest::CEnumString::Reset() { EnterCriticalSection(&m_crit); m_iPtr = 0; LeaveCriticalSection(&m_crit); return S_OK; } HRESULT CIntelliForms::CAutoSuggest::CEnumString::FillEnumerator() { // Already in critical section ASSERT(!m_pslMain); if (m_fFilledStrings) { return S_FALSE; } if (!m_bstrName || !m_bstrName[0] || !m_pIntelliForms) { return E_FAIL; } m_fFilledStrings = TRUE; m_iPtr = 0; // Fill the enumerator based on our name TraceMsg(TF_IFORMS, "IForms: Intelliforms filling enumerator"); // Open any previously saved strings if (!m_pIntelliForms->IsRestricted() && IsEnabledInCPL() && m_pIntelliForms->IsEnabledForPage()) { m_pIntelliForms->ReadFromStore(m_bstrName, &m_pslMain); // Add in profile assistant value, if any if (m_pszOpsValue && m_pszOpsValue[0]) { if (!m_pslMain) { CStringList_New(&m_pslMain); } else { // don't risk a scavenge (perf) m_pslMain->SetMaxStrings(CStringList::MAX_STRINGS+4); } if (m_pslMain) { m_pslMain->AddString(m_pszOpsValue); } } } // Next fill with any usernames that have saved passwords CStringList *pslPasswords; if (!m_pIntelliForms->IsRestrictedPW() && CIntelliForms::IsEnabledRestorePW() && SUCCEEDED(m_pIntelliForms->GetPasswordStringList(&pslPasswords))) { ASSERT(!(pslPasswords->NumStrings() & 1)); FILETIME ft; if (pslPasswords->NumStrings() > 0) { if (!m_pslMain) { CStringList_New(&m_pslMain); } else { // avoid expensive scavenging while adding usernames to string list m_pslMain->SetMaxStrings(m_pslMain->GetMaxStrings() + pslPasswords->NumStrings()/2); } if (m_pslMain) { for (int i=0; iNumStrings(); i+=2) { if (SUCCEEDED(pslPasswords->GetStringTime(i, &ft)) && FILETIME_TO_INT(ft) != 0) { // We have a saved password for this username. Add username to enumerator. m_pslMain->AddString(pslPasswords->GetString(i)); } } } } // do not delete pslPasswords } return (m_pslMain) ? ((m_pslMain->NumStrings()) ? S_OK : S_FALSE) : E_FAIL; } // Static helper. Pretty basic. HRESULT CStringList_New(CStringList **ppNew, BOOL fAutoDelete/*=TRUE*/) { *ppNew = new CStringList(); if (*ppNew) { (*ppNew)->SetAutoScavenge(fAutoDelete); } return (*ppNew) ? S_OK : E_OUTOFMEMORY; } CStringList::CStringList() { TraceMsg(TF_IFORMS, "IForms: CStringList::CStringList"); m_fAutoScavenge = TRUE; m_dwMaxStrings = MAX_STRINGS; } CStringList::~CStringList() { TraceMsg(TF_IFORMS, "IForms: CStringList::~CStringList"); CleanUp(); } void CStringList::CleanUp() { if (m_psiIndex) { LocalFree(m_psiIndex); m_psiIndex = NULL; } if (m_pBuffer) { LocalFree(m_pBuffer); m_pBuffer = NULL; } m_dwIndexSize = 0; m_dwBufEnd = m_dwBufSize = 0; } HRESULT CStringList::WriteToBlobs(LPBYTE *ppBlob1, DWORD *pcbBlob1, LPBYTE *ppBlob2, DWORD *pcbBlob2) { HRESULT hr = E_FAIL; TraceMsg(TF_IFORMS, "+WriteToBlobs"); if (SUCCEEDED(Validate())) { DWORD dwIndexSize; dwIndexSize = INDEX_SIZE(m_psiIndex->dwNumStrings); ASSERT(dwIndexSize <= m_dwIndexSize); *ppBlob1 = (LPBYTE) LocalAlloc(LMEM_FIXED, dwIndexSize); if (*ppBlob1) { *ppBlob2 = (LPBYTE) LocalAlloc(LMEM_FIXED, m_dwBufEnd); if (*ppBlob2) { memcpy(*ppBlob1, m_psiIndex, dwIndexSize); *pcbBlob1=dwIndexSize; memcpy(*ppBlob2, m_pBuffer, m_dwBufEnd); *pcbBlob2=m_dwBufEnd; hr = S_OK; } } } else { // Validate failed. TraceMsg(TF_ERROR | TF_IFORMS, "Validate FAILED in WriteToBlobs"); *ppBlob1=NULL; *ppBlob2=NULL; } if (FAILED(hr)) { if (*ppBlob1) { LocalFree(*ppBlob1); *ppBlob1=NULL; } if (*ppBlob2) { LocalFree(*ppBlob2); *ppBlob2=NULL; } *pcbBlob1=0; *pcbBlob2=0; } TraceMsg(TF_IFORMS, "-WriteToBlobs"); return hr; } // Take the blobs and use as our buffer HRESULT CStringList::ReadFromBlobs(LPBYTE *ppBlob1, DWORD cbBlob1, LPBYTE *ppBlob2, DWORD cbBlob2) { HRESULT hr = E_FAIL; TraceMsg(TF_IFORMS, "+ReadFromBlobs"); if (m_psiIndex) { TraceMsg(TF_IFORMS, "IForms: CStringList::ReadFromRegistry called with initialized instance."); CleanUp(); } // Allocate our buffers. m_psiIndex = (StringIndex *) (*ppBlob1); m_pBuffer = (LPBYTE) (*ppBlob2); *ppBlob1 = NULL; *ppBlob2 = NULL; if (!m_psiIndex || !m_pBuffer || !cbBlob1 || !cbBlob2) { // Nothing to do CleanUp(); return S_FALSE; } // Validate our string index. if ((m_psiIndex->dwSignature == INDEX_SIGNATURE) && (m_psiIndex->cbSize == STRINGINDEX_CBSIZE) && (m_psiIndex->dwNumStrings <= MAX_STRINGS)) { m_dwBufEnd = m_dwBufSize = cbBlob2; m_dwIndexSize = cbBlob1; if (SUCCEEDED(Validate())) { // Everything worked. Amazing. hr = S_OK; } } if (FAILED(hr)) { // Release buffers if necessary. CleanUp(); } TraceMsg(TF_IFORMS, "-ReadFromBlobs"); return hr; } // static HRESULT CStringList::GetFlagsFromIndex(LPBYTE pBlob1, INT64 *piFlags) { StringIndex *psiIndex = (StringIndex *)pBlob1; if ((psiIndex->dwSignature == INDEX_SIGNATURE) && (psiIndex->cbSize == STRINGINDEX_CBSIZE)) { *piFlags = psiIndex->iData; return S_OK; } return E_FAIL; } HRESULT CStringList::Validate() { TraceMsg(TF_IFORMS, "+CStringList::Validate"); if (!m_psiIndex || !m_pBuffer) { return E_FAIL; } for (DWORD dw=0; dw < m_psiIndex->dwNumStrings; dw++) { DWORD dwPtr = m_psiIndex->StringEntry[dw].dwStringPtr; DWORD dwSize = (GetStringLen(dw)+1) * sizeof(WCHAR); if (dwPtr + dwSize > m_dwBufSize) { return E_FAIL; } } TraceMsg(TF_IFORMS, "-CStringList::Validate"); return S_OK; } HRESULT CStringList::Init(DWORD dwBufSize /* =0 */) { DWORD dwMaxStrings=0; DWORD dwIndexSize=0; if (m_psiIndex) { TraceMsg(TF_IFORMS, "IForms: CStringList::Init called when already initialized"); CleanUp(); } if (dwBufSize == 0) { dwBufSize = INIT_BUF_SIZE; } dwMaxStrings = dwBufSize >> 5; // this is relatively arbitrary but doesn't matter much if (dwMaxStrings == 0) dwMaxStrings = 1; dwIndexSize = INDEX_SIZE(dwMaxStrings); m_pBuffer = (LPBYTE)LocalAlloc(LMEM_FIXED, dwBufSize); m_psiIndex = (StringIndex *)LocalAlloc(LMEM_FIXED, dwIndexSize); if ((NULL == m_psiIndex) || (NULL == m_pBuffer)) { TraceMsg(TF_IFORMS, "IForms: CStringList::Init memory allocation failed"); CleanUp(); return E_OUTOFMEMORY; } *((WCHAR *)m_pBuffer) = L'\0'; m_dwBufSize = dwBufSize; m_dwBufEnd = 0; m_psiIndex->dwSignature = INDEX_SIGNATURE; m_psiIndex->cbSize = STRINGINDEX_CBSIZE; m_psiIndex->dwNumStrings = 0; m_psiIndex->iData = 0; m_dwIndexSize = dwIndexSize; TraceMsg(TF_IFORMS, "IForms: CStringList::Init succeeded"); return S_OK; } HRESULT CStringList::GetBSTR(int iIndex, BSTR *pbstrRet) { LPCWSTR lpwstr = GetString(iIndex); if (!lpwstr) { *pbstrRet = NULL; return E_INVALIDARG; } *pbstrRet = SysAllocString(lpwstr); return (*pbstrRet) ? S_OK : E_OUTOFMEMORY; } HRESULT CStringList::GetTaskAllocString(int iIndex, LPOLESTR *pRet) { LPCWSTR lpwstr = GetString(iIndex); if (!lpwstr) { *pRet = NULL; return E_INVALIDARG; } DWORD dwSize = (GetStringLen(iIndex)+1) * sizeof(WCHAR); *pRet = (LPOLESTR)CoTaskMemAlloc(dwSize); if (!*pRet) { return E_OUTOFMEMORY; } memcpy(*pRet, lpwstr, dwSize); return S_OK; } HRESULT CStringList::FindString(LPCWSTR lpwstr, int iLen, int *piNum, BOOL fCaseSensitive) { if (!m_psiIndex) return E_FAIL; DWORD dw; if (!lpwstr) { return E_INVALIDARG; } if (iLen <= 0) { iLen = lstrlenW(lpwstr); } if (piNum) { *piNum = -1; } for (dw=0; dwdwNumStrings; dw++) { if (m_psiIndex->StringEntry[dw].dwStringLen == (DWORD)iLen) { if ((fCaseSensitive && (!StrCmpW(GetString(dw), lpwstr))) || (!fCaseSensitive && (!StrCmpIW(GetString(dw), lpwstr)))) { // Match! if (piNum) { *piNum = (int) dw; } return S_OK; } } } return E_FAIL; // Couldn't find it } // CStringList is not optimized for deleting HRESULT CStringList::DeleteString(int iIndex) { TraceMsg(TF_IFORMS, "+DeleteString"); if (!m_psiIndex) { return E_FAIL; } if ((iIndex<0) || ((DWORD)iIndex >= m_psiIndex->dwNumStrings)) { return E_INVALIDARG; } if ((DWORD)iIndex == (m_psiIndex->dwNumStrings-1)) { // Simple case - deleting last string m_dwBufEnd -= (sizeof(WCHAR) * (GetStringLen(iIndex) + 1)); m_psiIndex->dwNumStrings --; return S_OK; } DWORD cbSizeDeleted; LPCWSTR pwszString1, pwszString2; pwszString1 = GetString(iIndex); pwszString2 = GetString(iIndex+1); // Size in bytes of string to be deleted including null terminator cbSizeDeleted = (DWORD)((DWORD_PTR)pwszString2 - (DWORD_PTR)pwszString1); ASSERT(cbSizeDeleted == (sizeof(WCHAR) * (lstrlenW(GetString(iIndex))+1))); // Delete entry in index memcpy(&(m_psiIndex->StringEntry[iIndex]), &(m_psiIndex->StringEntry[iIndex+1]), STRINGENTRY_SIZE*(m_psiIndex->dwNumStrings - iIndex - 1)); m_psiIndex->dwNumStrings --; // Delete string in buffer memcpy((LPWSTR)pwszString1, pwszString2, m_dwBufEnd-(int)PtrDiff(pwszString2, m_pBuffer)); m_dwBufEnd -= cbSizeDeleted; // Fix up pointers in index for (int i=iIndex; (DWORD)i < m_psiIndex->dwNumStrings; i++) { m_psiIndex->StringEntry[i].dwStringPtr -= cbSizeDeleted; } TraceMsg(TF_IFORMS, "-DeleteString"); return S_OK; } HRESULT CStringList::InsertString(int iIndex, LPCWSTR lpwstr) { TraceMsg(TF_IFORMS, "+InsertString"); if (!m_psiIndex) { return E_FAIL; } if ((iIndex<0) || ((DWORD)iIndex > m_psiIndex->dwNumStrings)) { return E_INVALIDARG; } if ((DWORD)iIndex == m_psiIndex->dwNumStrings) { // Simple case - inserting to end return _AddString(lpwstr, FALSE, NULL); } DWORD dwLen = (DWORD)lstrlenW(lpwstr); DWORD dwSizeInserted = sizeof(WCHAR) * (dwLen + 1); if (FAILED(EnsureBuffer(m_dwBufEnd + dwSizeInserted)) || FAILED(EnsureIndex(m_psiIndex->dwNumStrings + 1))) { return E_OUTOFMEMORY; } // Insert into buffer LPWSTR pwszBufLoc = GetStringPtr(iIndex); memcpy((LPBYTE)pwszBufLoc + dwSizeInserted, pwszBufLoc, m_dwBufEnd - (int) PtrDiff(pwszBufLoc, m_pBuffer)); memcpy(pwszBufLoc, lpwstr, dwSizeInserted); m_dwBufEnd += dwSizeInserted; // Insert into index memcpy(&(m_psiIndex->StringEntry[iIndex+1]), &(m_psiIndex->StringEntry[iIndex]), STRINGENTRY_SIZE*(m_psiIndex->dwNumStrings - iIndex)); struct StringIndex::tagStringEntry *pse=&(m_psiIndex->StringEntry[iIndex]); pse->dwStringPtr = (DWORD)PtrDiff(pwszBufLoc, m_pBuffer); pse->ftLastSubmitted.dwLowDateTime = pse->ftLastSubmitted.dwHighDateTime = 0; pse->dwStringLen = dwLen; m_psiIndex->dwNumStrings ++; // Fix up pointers after inserted string for (int i=iIndex+1; (DWORD)idwNumStrings; i++) { m_psiIndex->StringEntry[i].dwStringPtr += dwSizeInserted; } TraceMsg(TF_IFORMS, "-InsertString"); return S_OK; } HRESULT CStringList::ReplaceString(int iIndex, LPCWSTR lpwstr) { TraceMsg(TF_IFORMS, "+ReplaceString"); if (!m_psiIndex) { return E_FAIL; } if ((iIndex<0) || ((DWORD)iIndex >= m_psiIndex->dwNumStrings)) { return E_INVALIDARG; } if ((DWORD)lstrlenW(lpwstr) == m_psiIndex->StringEntry[iIndex].dwStringLen) { // Simple case - strings equal length memcpy( GetStringPtr(iIndex), lpwstr, (m_psiIndex->StringEntry[iIndex].dwStringLen)*sizeof(WCHAR)); return S_OK; } // Delete old string, then insert new one DeleteString(iIndex); HRESULT hr = InsertString(iIndex, lpwstr); TraceMsg(TF_IFORMS, "-ReplaceString"); return hr; } HRESULT CStringList::AddString(LPCWSTR lpwstr, FILETIME ft, int *piNum /*=NULL*/) { int iNum; HRESULT hr; TraceMsg(TF_IFORMS, "+AddString"); hr = _AddString(lpwstr, TRUE, &iNum); if (piNum) { *piNum = iNum; } if (SUCCEEDED(hr)) { UpdateStringTime(iNum, ft); } TraceMsg(TF_IFORMS, "-AddString"); return hr; } HRESULT CStringList::AddString(LPCWSTR lpwstr, int *piNum /*=NULL*/) { return _AddString(lpwstr, TRUE, piNum); } HRESULT CStringList::AppendString(LPCWSTR lpwstr, int *piNum /*=NULL*/) { return _AddString(lpwstr, FALSE, piNum); } HRESULT CStringList::AppendString(LPCWSTR lpwstr, FILETIME ft, int *piNum /*=NULL*/) { int iNum; HRESULT hr; hr = _AddString(lpwstr, FALSE, &iNum); if (piNum) { *piNum = iNum; } if (SUCCEEDED(hr)) { SetStringTime(iNum, ft); } return hr; } HRESULT CStringList::_AddString(LPCWSTR lpwstr, BOOL fCheckDuplicates, int *piNum) { DWORD dwSize, dwLen; int iNum = -1; WCHAR wchBufTruncated[MAX_URL_STRING]; LPCWSTR lpwstrTruncated=lpwstr; TraceMsg(TF_IFORMS, "+_AddString"); if (piNum) { *piNum = -1; } if (!lpwstr) { return E_INVALIDARG; } if (!m_psiIndex) { if (FAILED(Init())) { return E_FAIL; } } dwLen = (DWORD) lstrlenW(lpwstr); // Explicitly truncate strings to MAX_URL characters. If we don't do this, browseui // autocomplete code truncates it anyway and then we have problems removing // duplicates and deleting these long strings. All IntelliForms code can handle // arbitrary length strings. if (dwLen >= ARRAYSIZE(wchBufTruncated)) { StrCpyNW(wchBufTruncated, lpwstr, ARRAYSIZE(wchBufTruncated)); lpwstrTruncated = wchBufTruncated; dwLen = lstrlenW(wchBufTruncated); } dwSize = (dwLen+1)*sizeof(WCHAR); if (fCheckDuplicates && SUCCEEDED(FindString(lpwstrTruncated, (int)dwLen, &iNum, FALSE))) { if (piNum) { *piNum = iNum; } if (!StrCmpW(lpwstrTruncated, GetString(iNum))) { return S_FALSE; // String is an exact duplicate } // String is a duplicate but has different case. Replace. ASSERT(m_psiIndex->StringEntry[iNum].dwStringLen == dwLen); memcpy(GetStringPtr(iNum), lpwstrTruncated, dwSize); return S_OK; // String was different in case } if (m_psiIndex->dwNumStrings >= m_dwMaxStrings) { if (m_fAutoScavenge) { // Remove the oldest string from our list. DWORD dwIndex; int iOldest=-1; FILETIME ftOldest = { 0xFFFFFFFF, 0x7FFFFFFF }; for (dwIndex=0; dwIndexdwNumStrings; dwIndex++) { if ((FILETIME_TO_INT(m_psiIndex->StringEntry[dwIndex].ftLastSubmitted) != 0) && (1 == CompareFileTime(&ftOldest, &m_psiIndex->StringEntry[dwIndex].ftLastSubmitted))) { ftOldest = m_psiIndex->StringEntry[dwIndex].ftLastSubmitted; iOldest = (int)dwIndex; } } if (iOldest != -1) { DeleteString(iOldest); } else { // User must not be setting string times. return E_OUTOFMEMORY; } } else { // Auto-scavenge is disabled. return E_OUTOFMEMORY; } } if (FAILED(EnsureBuffer(m_dwBufEnd + dwSize)) || FAILED(EnsureIndex(m_psiIndex->dwNumStrings + 1))) { return E_OUTOFMEMORY; } // Our buffers are large enough. Do it. if (piNum) { *piNum = (int) m_psiIndex->dwNumStrings; } LPWSTR pwszNewString = (LPWSTR)(m_pBuffer + m_dwBufEnd); memcpy(pwszNewString, lpwstrTruncated, dwSize); m_dwBufEnd += dwSize; struct StringIndex::tagStringEntry *pse=&(m_psiIndex->StringEntry[m_psiIndex->dwNumStrings]); pse->dwStringPtr = (DWORD)PtrDiff(pwszNewString, m_pBuffer); pse->ftLastSubmitted.dwLowDateTime = pse->ftLastSubmitted.dwHighDateTime = 0; pse->dwStringLen = dwLen; m_psiIndex->dwNumStrings ++; TraceMsg(TF_IFORMS, "-_AddString"); return S_OK; // We added a new string } HRESULT CStringList::EnsureBuffer(DWORD dwSizeNeeded) { TraceMsg(TF_IFORMS, "+EnsureBuffer"); if (dwSizeNeeded <= m_dwBufSize) { return S_OK; // Already big enough } if (!m_pBuffer) { return E_FAIL; } DWORD dwNewBufSize = m_dwBufSize * 2; // Grow buffer. if (dwSizeNeeded > dwNewBufSize) { TraceMsg(TF_IFORMS, "IForms: StringList special growing size (big string)"); dwNewBufSize = dwSizeNeeded; } TraceMsg(TF_IFORMS, "IForms: CStringList growing"); LPBYTE pBuf = (LPBYTE)LocalReAlloc(m_pBuffer, dwNewBufSize, LMEM_MOVEABLE); if (!pBuf) { TraceMsg(TF_IFORMS, "IForms: CStringList: ReAlloc failure"); // Realloc failure: our old memory is still present return E_FAIL; } m_dwBufSize = dwNewBufSize; m_pBuffer = pBuf; TraceMsg(TF_IFORMS, "-EnsureBuffer"); // Successfully realloced to bigger buffer return S_OK; } // grow psiIndex if needed HRESULT CStringList::EnsureIndex(DWORD dwNumStringsNeeded) { TraceMsg(TF_IFORMS, "+EnsureIndex"); if (!m_psiIndex) { return E_FAIL; } if (INDEX_SIZE(dwNumStringsNeeded) > m_dwIndexSize) { DWORD dwNewMaxStrings = (m_psiIndex->dwNumStrings) * 2; DWORD dwNewIndexSize = INDEX_SIZE(dwNewMaxStrings); TraceMsg(TF_IFORMS, "IForms: CStringList growing max strings"); StringIndex *psiBuf = (StringIndex *)LocalReAlloc(m_psiIndex, dwNewIndexSize, LMEM_MOVEABLE); if (!psiBuf) { // Realloc failure: Old memory still present TraceMsg(TF_IFORMS, "IForms: CStringList ReAlloc failure"); return E_OUTOFMEMORY; } // Success. Don't need to fix any pointers in index (buffer is unchanged) m_psiIndex = psiBuf; m_dwIndexSize = dwNewIndexSize; } TraceMsg(TF_IFORMS, "-EnsureIndex"); return S_OK; } // This dlg proc is used for password save, change, delete dialogs INT_PTR AutoSuggestDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: CenterWindow(hDlg, GetParent(hDlg)); SetWindowLongPtr(hDlg, DWLP_USER, (LONG_PTR) lParam); if (lParam == IDD_AUTOSUGGEST_SAVEPASSWORD) { // For "Save" password we default to no. For "Change" and "Delete" we default to yes. SetFocus(GetDlgItem(hDlg, IDNO)); return FALSE; } return TRUE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDCANCEL: // close box case IDYES: // yes button case IDNO: // no button if (IDD_AUTOSUGGEST_SAVEPASSWORD == GetWindowLongPtr(hDlg, DWLP_USER)) { // Check the "don't ask me again" checkbox for the save password dlg if (IsDlgButtonChecked(hDlg, IDC_AUTOSUGGEST_NEVER)) { SHSetValue(HKEY_CURRENT_USER, c_szRegKeySMIEM, c_szRegValAskPasswords, REG_SZ, c_szNo, sizeof(c_szNo)); } } EndDialog(hDlg, LOWORD(wParam)); return TRUE; } break; #ifdef CHECKBOX_HELP case WM_HELP: // Only process WM_HELP for save password dlg if (IDD_AUTOSUGGEST_SAVEPASSWORD == GetWindowLong(hDlg, DWL_USER)) { SHWinHelpOnDemandWrap((HWND) ((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile, HELP_WM_HELP, (DWORD_PTR)(LPTSTR) c_aIFormsHelpIds); } break; case WM_CONTEXTMENU: // right mouse click // Only process WM_HELP for save password dlg if (IDD_AUTOSUGGEST_SAVEPASSWORD == GetWindowLong(hDlg, DWL_USER)) { SHWinHelpOnDemandWrap((HWND) wParam, c_szHelpFile, HELP_CONTEXTMENU, (DWORD_PTR)(LPTSTR) c_aIFormsHelpIds); } break; #endif } return FALSE; } //================================================================================ INT_PTR CALLBACK AskUserDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: CenterWindow(hDlg, GetParent(hDlg)); Animate_OpenEx(GetDlgItem(hDlg, IDD_ANIMATE), HINST_THISDLL, MAKEINTRESOURCE(IDA_AUTOSUGGEST)); return TRUE; case WM_COMMAND: { switch (LOWORD(wParam)) { case IDC_AUTOSUGGEST_HELP: SHHtmlHelpOnDemandWrap(GetParent(hDlg), TEXT("iexplore.chm > iedefault"), HH_DISPLAY_TOPIC, (DWORD_PTR) TEXT("autocomp.htm"), ML_CROSSCODEPAGE); break; case IDYES: case IDNO: { LPCTSTR pszData; DWORD cbData; DWORD dwData=0; if (LOWORD(wParam) == IDYES) { pszData = c_szYes; cbData = sizeof(c_szYes); } else { pszData = c_szNo; cbData = sizeof(c_szNo); } // Write the enabled state into our CPL regkey SHSetValue(HKEY_CURRENT_USER, c_szRegKeySMIEM, c_szRegValUseFormSuggest, REG_SZ, pszData, cbData); // Flag it as "asked user" so we don't ask them again SHSetValue(HKEY_CURRENT_USER, c_szRegKeyIntelliForms, c_szRegValAskUser, REG_DWORD, &dwData, sizeof(dwData)); } // Fall through case IDCANCEL: { EndDialog(hDlg, LOWORD(wParam)); } break; } } return TRUE; case WM_DESTROY: break; } return FALSE; }