winamp/Src/Wasabi/api/skin/widgets/mb/iebrowser.cpp
2024-09-24 14:54:57 +02:00

638 lines
12 KiB
C++

#include <precomp.h>
#include "iebrowser.h"
#include "mbsvc.h"
#include "main.h"
#include "../nu/ns_wc.h"
#include "../Winamp/buildtype.h"
#include <api/config/items/cfgitem.h>
#include <wa2frontend.h>
#include <windows.h>
#include <Mshtml.h>
#include "minibrowserCOM.h"
class BrowserMsgProc: public ifc_messageprocessor
{
public:
BrowserMsgProc(BrowserWnd *pBrowser) : pTarget(pBrowser) {}
~BrowserMsgProc(void) {}
public:
bool ProcessMessage(MSG *pMsg)
{
if (WM_KEYFIRST <= pMsg->message && WM_KEYLAST >= pMsg->message)
{
HWND hwndHost;
hwndHost = pTarget->gethWnd();
if ((hwndHost == pMsg->hwnd || IsChild(hwndHost, pMsg->hwnd)) && IsWindowVisible(pMsg->hwnd))
{
if (!(GetAsyncKeyState(VK_CONTROL)&0x8000) && !(GetAsyncKeyState(VK_MENU)&0x8000))
{
if (pTarget->TranslateKey(pMsg)) return true;
}
switch(pMsg->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_KEYUP:
case WM_SYSKEYUP:
switch(pMsg->wParam)
{
case VK_F1:
if (!(GetAsyncKeyState(VK_SHIFT)&0x8000)) pMsg->hwnd = plugin.hwndParent;
break;
case VK_F4:
if ((pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN) && (GetAsyncKeyState(VK_MENU)&0x8000))
SendMessageW(plugin.hwndParent, WM_CLOSE, 0, 0);
pMsg->message = WM_NULL;
break;
case 'P':
case 'K':
case 'H':
case VK_TAB:
if ((GetAsyncKeyState(VK_CONTROL)&0x8000) && !(GetAsyncKeyState(VK_MENU)&0x8000)) pMsg->hwnd = plugin.hwndParent;
break;
case '3':
case VK_UP:
case VK_DOWN:
break;
default:
if ((GetAsyncKeyState(VK_MENU)&0x8000) && !(GetAsyncKeyState(VK_CONTROL)&0x8000)) pMsg->hwnd = plugin.hwndParent;
break;
}
break;
}
}
}
return /*(IsDialogMessageW(pTarget->gethWnd(), pMsg)) ? true :*/ false;
}
protected:
BrowserWnd *pTarget;
RECVS_DISPATCH;
};
extern HINSTANCE hInstance;
STDAPI WriteBSTR(BSTR *pstrDest, LPCWSTR szSrc)
{
*pstrDest = SysAllocString( szSrc );
if ( !(*pstrDest) ) return E_OUTOFMEMORY;
return NOERROR;
}
STDAPI FreeBSTR(BSTR* pstr)
{
if ( *pstr == NULL ) return S_FALSE;
SysFreeString( *pstr );
return NOERROR;
}
HRESULT writeBString(BSTR* psz, const char *str)
{
WCHAR WideStr[WA_MAX_PATH] = {0};
String s = str;
if (s.isempty()) s = "";
MultiByteToWideCharSZ(CP_ACP, 0, s, -1, WideStr, WA_MAX_PATH);
return WriteBSTR(psz, WideStr);
}
BrowserWnd::BrowserWnd() : HTMLContainer2(NULL, NULL), processor(NULL)
{
setVirtual(0);
oleOk = FALSE;
homepage = L"about:blank";
timerset1 = 0;
timerset2 = 0;
cancelIEErrorPage = false;
scrollbarsflag = BROWSER_SCROLLBARS_DEFAULT;
}
BrowserWnd::~BrowserWnd()
{
if (processor)
{
if (WASABI_API_APP) WASABI_API_APP->app_removeMessageProcessor(processor);
free(processor);
processor = NULL;
}
if (timerset1)
{
killTimer(MB_TIMERID1);
timerset1 = 0;
}
if (timerset2)
{
killTimer(MB_TIMERID2);
timerset2 = 0;
}
freeBrowserStuff();
}
bool BrowserWnd::InitializeLibrary()
{
return (FALSE != HTMLContainer2_Initialize());
}
void BrowserWnd::UninitializeLibrary()
{
HTMLContainer2_Uninitialize();
}
int BrowserWnd::onInit()
{
BROWSER_PARENT::onInit();
if (isVisible())
onSetVisible(1);
updateScrollbars();
return 1;
}
void BrowserWnd::onSetVisible(int show)
{
if (show) initBrowserStuff();
}
int BrowserWnd::initBrowserStuff()
{
if (pUnk) return 1;
if (SUCCEEDED(OleInitialize(NULL))) oleOk = TRUE;
if (!oleOk) return 1;
// {280876CF-48C0-40bc-8E86-73CE6BB462E5}
const GUID options_guid =
{ 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } };
hParent = gethWnd();
int usemozilla = 0;
#ifdef WASABI_COMPILE_CONFIG
usemozilla = _intVal(WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid), L"Use Mozilla instead of IE for minibrowser");
#endif
if (SUCCEEDED(Initialize()))
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
pWeb2->put_RegisterAsBrowser(VARIANT_TRUE);
if (deferednavigate.isempty())
{
if (!homepage.isempty())
minibrowser_navigateUrl(homepage);
else
minibrowser_navigateUrl(L"about:blank");
}
else minibrowser_navigateUrl(deferednavigate);
}
}
if (!processor && WASABI_API_APP)
{
processor = new BrowserMsgProc(this);
WASABI_API_APP->app_addMessageProcessor(processor);
}
ShowWindow(hParent, SW_SHOWNA);
return 1;
}
void BrowserWnd::freeBrowserStuff()
{
if (oleOk)
{
Finish();
OleUninitialize();
oleOk = FALSE;
#ifndef WASABINOMAINAPI
api->hint_garbageCollect();
#endif
}
}
int BrowserWnd::minibrowser_navigateUrl(const wchar_t *url)
{
HRESULT hr;
curpage = url;
hr = NavigateToName(url, 0);
if (FAILED(hr))
{
deferednavigate = url;
return 0;
}
return 1;
}
int BrowserWnd::minibrowser_back()
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
pWeb2->GoBack();
pWeb2->Release();
}
return 1;
}
int BrowserWnd::minibrowser_forward()
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
pWeb2->GoForward();
pWeb2->Release();
}
return 1;
}
int BrowserWnd::minibrowser_refresh()
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
pWeb2->Refresh();
pWeb2->Release();
}
return 1;
}
int BrowserWnd::minibrowser_home()
{
minibrowser_navigateUrl(homepage);
return 1;
}
int BrowserWnd::minibrowser_stop()
{
HRESULT hr;
IWebBrowser2 *pWeb2;
hr = GetIWebBrowser2(&pWeb2);
if (SUCCEEDED(hr))
{
pWeb2->Stop();
pWeb2->Release();
}
return 1;
}
HWND BrowserWnd::getOSHandle()
{
return ::GetWindow(gethWnd(), GW_CHILD); // assumes setVirtual(0) in constructor
}
void BrowserWnd::onTargetNameTimer()
{
updateTargetName();
}
void BrowserWnd::onScrollbarsFlagTimer()
{
updateScrollbars();
}
void BrowserWnd::timerCallback(int id)
{
switch (id)
{
case MB_TIMERID1:
onTargetNameTimer();
return ;
case MB_TIMERID2:
onScrollbarsFlagTimer();
return ;
}
BROWSER_PARENT::timerCallback(id);
}
void BrowserWnd::minibrowser_setTargetName(const wchar_t *name)
{
targetname = name;
updateTargetName();
}
void BrowserWnd::updateTargetName()
{
if (!doSetTargetName(targetname))
{
if (!timerset1) { setTimer(MB_TIMERID1, 100); timerset1 = 1; }
return ;
}
else
{
if (timerset1) { killTimer(MB_TIMERID1); timerset1 = 0; }
}
}
int BrowserWnd::doSetTargetName(const wchar_t *name)
{
HRESULT hr;
IWebBrowser2 *pWeb2;
IDispatch *id;
hr = GetIWebBrowser2(&pWeb2);
if (FAILED(hr)) return FALSE;
if (SUCCEEDED(pWeb2->get_Document(&id)) && id)
{
IHTMLDocument2 *doc;
if (SUCCEEDED(id->QueryInterface(IID_IHTMLDocument2, (void **)&doc)) && doc)
{
IHTMLWindow2 *w;
if (SUCCEEDED(doc->get_parentWindow(&w)) && w)
{
w->put_name(SysAllocString(targetname.getValue()));
w->Release();
doc->Release();
id->Release();
pWeb2->Release();
return 1;
}
doc->Release();
}
id->Release();
}
pWeb2->Release();
return 0;
}
const wchar_t *BrowserWnd::minibrowser_getTargetName()
{
return targetname;
}
void BrowserWnd::OnBeforeNavigate(IDispatch *pDispatch, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel)
{
int i = 0;
foreach(callbacks)
int r = callbacks.getfor()->minibrowsercb_onBeforeNavigate(URL->bstrVal, Flags->intVal, TargetFrameName->bstrVal);
if (i++ == 0) *Cancel = (r) ? VARIANT_TRUE : VARIANT_FALSE;
endfor;
updateScrollbars();
}
void BrowserWnd::minibrowser_setScrollbarsFlag(int a)
{
scrollbarsflag = a;
updateScrollbars();
}
void BrowserWnd::updateScrollbars()
{
if (!doSetScrollbars())
{
if (!timerset2) { setTimer(MB_TIMERID2, 100); timerset2 = 1; }
return ;
}
else
{
if (timerset2) { killTimer(MB_TIMERID2); timerset2 = 0; }
}
}
void BrowserWnd::OnDocumentComplete(IDispatch *pDispatch, VARIANT *URL)
{
if (!targetname.isempty())
minibrowser_setTargetName(targetname);
foreach(callbacks)
callbacks.getfor()->minibrowsercb_onDocumentComplete(URL->bstrVal);
endfor;
updateScrollbars();
}
void BrowserWnd::OnDocumentReady(IDispatch *pDispatch, VARIANT *URL)
{
if (!targetname.isempty())
minibrowser_setTargetName(targetname);
foreach(callbacks)
callbacks.getfor()->minibrowsercb_onDocumentReady(URL->bstrVal);
endfor;
updateScrollbars();
}
void BrowserWnd::OnNavigateError(IDispatch *pDispatch, VARIANT *URL, VARIANT *TargetFrameName, VARIANT *StatusCode, VARIANT_BOOL *Cancel)
{
if (TargetFrameName->bstrVal != NULL)
return; //TODO: send targetframe via api to script
foreach(callbacks)
callbacks.getfor()->minibrowsercb_onNavigateError(URL->bstrVal, StatusCode->intVal);
endfor;
if (cancelIEErrorPage) *Cancel = -1;
}
const wchar_t* BrowserWnd::messageToMaki(wchar_t* str1, wchar_t* str2, int i1, int i2, int i3)
{
const wchar_t* ret = 0;
foreach(callbacks)
ret = callbacks.getfor()->minibrowsercb_messageToMaki(str1, str2, i1, i2, i3);
if (ret) break;
endfor;
return ret;
}
const wchar_t* BrowserWnd::minibrowser_messageToJS(const wchar_t* str1, const wchar_t* str2, int i1, int i2, int i3)
{
// TODO feed JS w/ this info
return 0;
}
void BrowserWnd::minibrowser_scrape()
{
IWebBrowser2 *browser=0;
GetIWebBrowser2(&browser);
IDispatch *docDisp=0;
IHTMLDocument2 *document = 0;
if (browser)
{
browser->get_Document(&docDisp);
if (docDisp)
{
docDisp->QueryInterface(&document);
docDisp->Release();
}
browser->Release();
}
if (document)
{
IHTMLElementCollection *links=0;
document->get_all(&links);
if (links)
{
IDispatch *anchorDisp=0;
VARIANT index;
VariantInit(&index);
index.vt = VT_I4;
index.intVal = 0;
links->item(index, index, &anchorDisp);
while (anchorDisp)
{
IHTMLAnchorElement *anchor=0;
anchorDisp->QueryInterface(&anchor);
if (anchor)
{
BSTR href=0;
anchor->get_href(&href);
if (href && (wa2.CanPlay(href) || wa2.IsPlaylist(href)))
{
foreach(callbacks)
callbacks.getfor()->minibrowsercb_onMediaLink(href);
endfor;
}
SysFreeString(href);
anchor->Release();
}
index.intVal++;
anchorDisp->Release();
links->item(index, index, &anchorDisp);
}
links->Release();
}
document->Release();
}
}
void BrowserWnd::minibrowser_getDocumentTitle(wchar_t *str, size_t len)
{
IWebBrowser2 *browser=0;
GetIWebBrowser2(&browser);
IDispatch *docDisp=0;
IHTMLDocument2 *document = 0;
if (browser)
{
browser->get_Document(&docDisp);
if (docDisp)
{
docDisp->QueryInterface(&document);
docDisp->Release();
}
browser->Release();
}
if (document)
{
BSTR title_bstr;
document->get_title(&title_bstr);
document->Release();
WCSCPYN(str, title_bstr, len);
// the COM object SysAllocString'd this for us, so we need to free it via COM also
SysFreeString(title_bstr);
}
else
str[0]=0;
}
void BrowserWnd::minibrowser_setCancelIEErrorPage (bool cancel)
{
cancelIEErrorPage = cancel;
}
int BrowserWnd::doSetScrollbars()
{
HRESULT hr;
IWebBrowser2 *pWeb2;
IDispatch *id;
hr = GetIWebBrowser2(&pWeb2);
if (FAILED(hr)) return 0;
if (scrollbarsflag == BROWSER_SCROLLBARS_DEFAULT) return 1;
if (SUCCEEDED(pWeb2->get_Document(&id)) && id)
{
IHTMLDocument2 *doc;
if (SUCCEEDED(id->QueryInterface(IID_IHTMLDocument2, (void **)&doc)) && doc)
{
IHTMLElement *e;
if (SUCCEEDED(doc->get_body(&e)))
{
IHTMLStyle *s;
if (SUCCEEDED(e->get_style(&s)))
{
BSTR a;
switch (scrollbarsflag)
{
case BROWSER_SCROLLBARS_ALWAYS:
writeBString(&a, "scroll");
break;
case BROWSER_SCROLLBARS_AUTO:
writeBString(&a, "auto");
break;
case BROWSER_SCROLLBARS_NEVER:
writeBString(&a, "hidden");
break;
default:
a = NULL;
break;
}
if (a) s->put_overflow(a);
FreeBSTR(&a);
s->Release();
pWeb2->Release();
return 1;
}
e->Release();
}
doc->Release();
}
id->Release();
}
pWeb2->Release();
return 0;
}
const wchar_t *BrowserWnd::minibrowser_getCurrentUrl()
{
return curpage;
}
STDMETHODIMP BrowserWnd::GetExternal(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch)
{
*ppDispatch = (IDispatch*) new MinibrowserCOM(this); //TODO we might need to delete this as well!
return S_OK;
}
DWORD BrowserWnd::OnGetDownlodFlags(void)
{
return DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_PRAGMA_NO_CACHE
#ifdef WINAMP_FINAL_BUILD
|DLCTL_SILENT
#endif
;
}
#define CBCLASS BrowserMsgProc
START_DISPATCH;
CB(IFC_MESSAGEPROCESSOR_PROCESS_MESSAGE, ProcessMessage)
END_DISPATCH;
#undef CBCLASS