mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5326 lines
146 KiB
5326 lines
146 KiB
// iforms.cpp : Implementation of CIntelliForms
|
|
|
|
#include "priv.h"
|
|
#include <iehelpid.h>
|
|
#include <pstore.h>
|
|
#include "hlframe.h"
|
|
#include "iformsp.h"
|
|
#include "shldisp.h"
|
|
#include "opsprof.h"
|
|
#include "resource.h"
|
|
|
|
#include <mluisupp.h>
|
|
|
|
|
|
// {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; i<m_pslPasswords->NumStrings(); 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<ARRAYSIZE(bBuf); i++)
|
|
{
|
|
// Translate each char into 32-96 range
|
|
((LPWSTR)m_pwszUrlHash)[i] = (WCHAR)((bBuf[i] & 0x3F) + 0x20);
|
|
}
|
|
((LPWSTR)m_pwszUrlHash)[i] = L'\0';
|
|
}
|
|
|
|
return m_pwszUrlHash;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// Tells us if passwords are present for this url
|
|
BOOL CIntelliForms::ArePasswordsSaved()
|
|
{
|
|
if (!m_fRestrictedPW)
|
|
{
|
|
DWORD dwVal, dwSize=sizeof(dwVal);
|
|
LPCWSTR pwsz = GetUrlHash();
|
|
|
|
if (pwsz && (ERROR_SUCCESS == SHGetValueW(HKEY_CURRENT_USER, c_wszRegKeyIntelliFormsSPW, pwsz, NULL, &dwVal, &dwSize)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// Will return password list in m_pslPasswords, if passwords are saved
|
|
BOOL CIntelliForms::LoadPasswords()
|
|
{
|
|
if (!m_fCheckedPW)
|
|
{
|
|
m_fCheckedPW = TRUE;
|
|
|
|
// Check if passwords are present without hitting pstore
|
|
if (ArePasswordsSaved())
|
|
{
|
|
// We should have passwords for this url. Hit PStore.
|
|
ReadFromStore(GetUrl(), &m_pslPasswords, TRUE);
|
|
|
|
m_iRestoredIndex = -1;
|
|
}
|
|
}
|
|
else if (m_pslPasswords)
|
|
{
|
|
// If we already have passwords, double check the registry in case the user
|
|
// nuked saved stuff via inetcpl
|
|
if (!ArePasswordsSaved())
|
|
{
|
|
delete m_pslPasswords;
|
|
m_pslPasswords=NULL;
|
|
m_iRestoredIndex = -1;
|
|
}
|
|
}
|
|
|
|
return (m_pslPasswords != NULL);
|
|
}
|
|
|
|
void CIntelliForms::SavePasswords()
|
|
{
|
|
if (m_pslPasswords && m_bstrUrl)
|
|
{
|
|
WriteToStore(m_bstrUrl, m_pslPasswords);
|
|
SetPasswordsAreSaved(TRUE);
|
|
}
|
|
}
|
|
|
|
// Mark that we have passwords saved for this url
|
|
void CIntelliForms::SetPasswordsAreSaved(BOOL fSaved)
|
|
{
|
|
LPCWSTR pwsz = GetUrlHash();
|
|
|
|
if (pwsz)
|
|
{
|
|
if (fSaved)
|
|
{
|
|
DWORD dwSize = sizeof(DWORD);
|
|
DWORD dw = 0;
|
|
SHSetValueW(HKEY_CURRENT_USER, c_wszRegKeyIntelliFormsSPW, pwsz, REG_DWORD, &dw, sizeof(dw));
|
|
}
|
|
else
|
|
{
|
|
SHDeleteValueW(HKEY_CURRENT_USER, c_wszRegKeyIntelliFormsSPW, pwsz);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// enumerates form & gets password fields
|
|
class CDetectLoginForm
|
|
{
|
|
public:
|
|
CDetectLoginForm() { m_pNameEle=m_pPasswordEle=m_pPasswordEle2=NULL; }
|
|
~CDetectLoginForm() { SAFERELEASE(m_pNameEle); SAFERELEASE(m_pPasswordEle); }
|
|
|
|
HRESULT ParseForm(IHTMLFormElement *pFormEle, BOOL fRestoring);
|
|
|
|
IHTMLInputTextElement *GetNameEle() { return m_pNameEle; }
|
|
IHTMLInputTextElement *GetPasswordEle() { return m_pPasswordEle; }
|
|
|
|
protected:
|
|
IHTMLInputTextElement *m_pNameEle;
|
|
IHTMLInputTextElement *m_pPasswordEle;
|
|
|
|
IHTMLInputTextElement *m_pPasswordEle2;
|
|
|
|
static HRESULT s_PasswordCB(IDispatch *pDispEle, DWORD_PTR dwCBData);
|
|
};
|
|
|
|
// if SUCCEEDED(hr), GetNameEle and GetPasswordEle are guaranteed non-NULL
|
|
HRESULT CDetectLoginForm::ParseForm(IHTMLFormElement *pFormEle, BOOL fRestoring)
|
|
{
|
|
if (m_pPasswordEle || m_pNameEle || m_pPasswordEle2)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
|
|
CIntelliForms::CEnumCollection<IHTMLFormElement>::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 && iIndex<m_pslPasswords->NumStrings() && (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; i<cFetched; i++)
|
|
{
|
|
ASSERT(pwszName[i]);
|
|
if (pwszName[i])
|
|
{
|
|
BOOL fDelete = TRUE;
|
|
|
|
// Hack to work around PStore string-case bug: first take their
|
|
// enum value literally, then convert to lowercase and do it
|
|
// again; IE5.0 #71001
|
|
for (int iHack=0; iHack<2; iHack++)
|
|
{
|
|
if (iHack == 1)
|
|
{
|
|
// Convert the pwszName[i] to lowercase... only before
|
|
// the colon...
|
|
WCHAR *pwch = StrRChrW(pwszName[i], NULL, L':');
|
|
if (pwch)
|
|
{
|
|
*pwch = L'\0';
|
|
MyToLower(pwszName[i]);
|
|
*pwch = L':';
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
|
|
if (dwClear != IECMDID_ARG_CLEAR_FORMS_ALL)
|
|
{
|
|
fDelete = FALSE;
|
|
|
|
// See if this is a password item or not
|
|
// This is pretty annoying. Since our string lists are split
|
|
// into two blobs, we need to find out which one this is and
|
|
// load the index for it.
|
|
WCHAR *pwch = StrRChrW(pwszName[i], NULL, L':');
|
|
if (pwch)
|
|
{
|
|
LPWSTR pwszIndexName=NULL;
|
|
if (!StrCmpCW(pwch+1, c_szBlob2Value))
|
|
{
|
|
int iSize = sizeof(WCHAR) * (lstrlenW(pwszName[i])+10);
|
|
pwszIndexName = (LPWSTR) LocalAlloc(LMEM_FIXED, iSize);
|
|
if (pwszIndexName)
|
|
{
|
|
*pwch = L'\0';
|
|
wnsprintfW(pwszIndexName, iSize, L"%s:%s", pwszName[i], c_szBlob1Value);
|
|
*pwch = L':';
|
|
}
|
|
}
|
|
|
|
DWORD dwBlob1Size;
|
|
LPBYTE pBlob1=NULL;
|
|
INT64 iFlags;
|
|
|
|
if (SUCCEEDED(m_pPStore->ReadItem(
|
|
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; i<iNum; i++)
|
|
{
|
|
BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
|
|
|
|
if (bstrEvent)
|
|
{
|
|
pEle2->attachEvent(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; i<iNum; i++)
|
|
{
|
|
BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
|
|
|
|
if (bstrEvent)
|
|
{
|
|
pWin3->attachEvent(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; i<iNum; i++)
|
|
{
|
|
BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
|
|
|
|
if (bstrEvent)
|
|
{
|
|
pEle2->detachEvent(bstrEvent, (IDispatch *)this);
|
|
|
|
SysFreeString(bstrEvent);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CIntelliForms::CEventSink::UnSinkEvents(IHTMLWindow3 *pWin3, int iNum, EVENTS *pEvents)
|
|
{
|
|
for (int i=0; i<iNum; i++)
|
|
{
|
|
BSTR bstrEvent = SysAllocString(CEventSinkCallback::EventsToSink[(int)(pEvents[i])].pwszEventSubscribe);
|
|
|
|
if (bstrEvent)
|
|
{
|
|
pWin3->detachEvent(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; i<ARRAYSIZE(CEventSinkCallback::EventsToSink); i++)
|
|
{
|
|
if (!StrCmpCW(bstrEvent, CEventSinkCallback::EventsToSink[i].pwszEventName))
|
|
{
|
|
Event = (EVENTS) i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
SysFreeString(bstrEvent);
|
|
}
|
|
|
|
if (Event != EVENT_BOGUS)
|
|
{
|
|
IHTMLElement *pEle=NULL;
|
|
|
|
pObj->get_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; i<pslPasswords->NumStrings(); 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; dw<m_psiIndex->dwNumStrings; 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)i<m_psiIndex->dwNumStrings; 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; dwIndex<m_psiIndex->dwNumStrings; 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;
|
|
}
|