diff --git a/dlls/mshtml/En.rc b/dlls/mshtml/En.rc index 40c0534511..087229e44e 100644 --- a/dlls/mshtml/En.rc +++ b/dlls/mshtml/En.rc @@ -1,5 +1,5 @@ /* - * Copyright 2005 Jacek Caban + * Copyright 2005-2006 Jacek Caban * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,6 +23,21 @@ STRINGTABLE DISCARDABLE { IDS_HTMLDISABLED "HTML rendering is currently disabled." IDS_HTMLDOCUMENT "HTML Document" + IDS_DOWNLOADING "Downloading..." + IDS_INSTALLING "Installing..." +} + +ID_DWL_DIALOG DIALOG LOADONCALL MOVEABLE DISCARDABLE 0, 0, 260, 85 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Wine Gecko Installer" +FONT 8, "MS Shell Dlg" +{ + LTEXT "This application is trying to show an HTML page. Wine needs Gecko (Mozilla HTML " \ + "engine) to be installed to show the page. Click install if you want Wine to " \ + "automatically download and install Gecko.", ID_DWL_STATUS, 10, 10, 240, 30, SS_CENTER + CONTROL "Progress", ID_DWL_PROGRESS, PROGRESS_CLASSA, WS_BORDER|PBS_SMOOTH, 10, 40, 240, 12 + PUSHBUTTON "Cancel", IDCANCEL, 140, 60, 50, 15, WS_GROUP | WS_TABSTOP + PUSHBUTTON "Install", ID_DWL_INSTALL, 200, 60, 50, 15, WS_GROUP | WS_TABSTOP } /* FIXME: This should be in shdoclc.dll */ diff --git a/dlls/mshtml/Makefile.in b/dlls/mshtml/Makefile.in index e6044c779e..bb25990e60 100644 --- a/dlls/mshtml/Makefile.in +++ b/dlls/mshtml/Makefile.in @@ -21,6 +21,7 @@ C_SRCS = \ htmlselect.c \ htmltextcont.c \ htmltextarea.c \ + install.c \ main.c \ navigate.c \ nsembed.c \ diff --git a/dlls/mshtml/install.c b/dlls/mshtml/install.c new file mode 100644 index 0000000000..cd2a12438a --- /dev/null +++ b/dlls/mshtml/install.c @@ -0,0 +1,373 @@ +/* + * Copyright 2006 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "config.h" + +#include + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "ole2.h" +#include "commctrl.h" +#include "advpub.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "mshtml_private.h" +#include "resource.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mshtml); + +static HWND install_dialog = NULL; +static LPWSTR tmp_file_name = NULL; +static HANDLE tmp_file = INVALID_HANDLE_VALUE; +static LPWSTR url = NULL; + +static void clean_up(void) +{ + if(tmp_file != INVALID_HANDLE_VALUE) + CloseHandle(tmp_file); + + if(tmp_file_name) { + DeleteFileW(tmp_file_name); + HeapFree(GetProcessHeap(), 0, tmp_file_name); + tmp_file_name = NULL; + } + + if(tmp_file != INVALID_HANDLE_VALUE) { + CloseHandle(tmp_file); + tmp_file = INVALID_HANDLE_VALUE; + } + + if(install_dialog) + EndDialog(install_dialog, 0); +} + +static void set_status(DWORD id) +{ + HWND status = GetDlgItem(install_dialog, ID_DWL_STATUS); + WCHAR buf[64]; + + LoadStringW(hInst, id, buf, sizeof(buf)/sizeof(WCHAR)); + SendMessageW(status, WM_SETTEXT, 0, (LPARAM)buf); +} + +static void set_registry(LPCSTR install_dir) +{ + LPWSTR gecko_path; + HKEY hkey; + DWORD res, len, size; + + static const WCHAR wszMshtmlKey[] = { + 'S','o','f','t','w','a','r','e','\\','W','i','n','e', + '\\','M','S','H','T','M','L',0}; + static const WCHAR wszGeckoPath[] = {'G','e','c','k','o','P','a','t','h',0}; + static const WCHAR wszWineGecko[] = {'w','i','n','e','_','g','e','c','k','o',0}; + + /* @@ Wine registry key: HKCU\Software\Wine\MSHTML */ + res = RegOpenKeyW(HKEY_CURRENT_USER, wszMshtmlKey, &hkey); + if(res != ERROR_SUCCESS) { + ERR("Faild to open MSHTML key: %ld\n", res); + return; + } + + len = MultiByteToWideChar(CP_ACP, 0, install_dir, -1, NULL, 0)-1; + gecko_path = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR)+sizeof(wszWineGecko)); + MultiByteToWideChar(CP_ACP, 0, install_dir, -1, gecko_path, -1); + memcpy(gecko_path+len, wszWineGecko, sizeof(wszWineGecko)); + + size = len*sizeof(WCHAR)+sizeof(wszWineGecko); + res = RegSetValueExW(hkey, wszGeckoPath, 0, REG_SZ, (LPVOID)gecko_path, + len*sizeof(WCHAR)+sizeof(wszWineGecko)); + HeapFree(GetProcessHeap(), 0, gecko_path); + RegCloseKey(hkey); + if(res != ERROR_SUCCESS) + ERR("Failed to set GeckoPath value: %08lx\n", res); +} + +static HRESULT WINAPI InstallCallback_QueryInterface(IBindStatusCallback *iface, + REFIID riid, void **ppv) +{ + if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IBindStatusCallback, riid)) { + *ppv = iface; + return S_OK; + } + + return E_INVALIDARG; +} + +static ULONG WINAPI InstallCallback_AddRef(IBindStatusCallback *iface) +{ + return 2; +} + +static ULONG WINAPI InstallCallback_Release(IBindStatusCallback *iface) +{ + return 1; +} + +static HRESULT WINAPI InstallCallback_OnStartBinding(IBindStatusCallback *iface, + DWORD dwReserved, IBinding *pib) +{ + WCHAR tmp_dir[MAX_PATH]; + + set_status(IDS_DOWNLOADING); + + GetTempPathW(sizeof(tmp_dir)/sizeof(WCHAR), tmp_dir); + + tmp_file_name = HeapAlloc(GetProcessHeap(), 0, MAX_PATH*sizeof(WCHAR)); + GetTempFileNameW(tmp_dir, NULL, 0, tmp_file_name); + + TRACE("creating temp file %s\n", debugstr_w(tmp_file_name)); + + tmp_file = CreateFileW(tmp_file_name, GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if(tmp_file == INVALID_HANDLE_VALUE) { + ERR("Could not create file: %ld\n", GetLastError()); + clean_up(); + return E_FAIL; + } + + return S_OK; +} + +static HRESULT WINAPI InstallCallback_GetPriority(IBindStatusCallback *iface, + LONG *pnPriority) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI InstallCallback_OnLowResource(IBindStatusCallback *iface, + DWORD dwReserved) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI InstallCallback_OnProgress(IBindStatusCallback *iface, ULONG ulProgress, + ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) +{ + HWND progress = GetDlgItem(install_dialog, ID_DWL_PROGRESS); + + if(ulProgressMax) + SendMessageW(progress, PBM_SETRANGE32, 0, ulProgressMax); + if(ulProgress) + SendMessageW(progress, PBM_SETPOS, ulProgress, 0); + + return S_OK; +} + +static HRESULT WINAPI InstallCallback_OnStopBinding(IBindStatusCallback *iface, + HRESULT hresult, LPCWSTR szError) +{ + LPSTR file_name; + DWORD len; + HMODULE advpack; + char program_files[MAX_PATH]; + typeof(ExtractFilesA) *pExtractFilesA; + HRESULT hres; + + static const WCHAR wszAdvpack[] = {'a','d','v','p','a','c','k','.','d','l','l',0}; + + if(FAILED(hresult)) { + ERR("Binding failed %08lx\n", hresult); + clean_up(); + return S_OK; + } + + CloseHandle(tmp_file); + tmp_file = INVALID_HANDLE_VALUE; + + set_status(IDS_INSTALLING); + + advpack = LoadLibraryW(wszAdvpack); + pExtractFilesA = (typeof(ExtractFilesA)*)GetProcAddress(advpack, "ExtractFiles"); + + len = WideCharToMultiByte(CP_ACP, 0, tmp_file_name, -1, NULL, 0, NULL, NULL); + file_name = HeapAlloc(GetProcessHeap(), 0, len); + WideCharToMultiByte(CP_ACP, 0, tmp_file_name, -1, file_name, -1, NULL, NULL); + + GetEnvironmentVariableA("ProgramFiles", program_files, sizeof(program_files)); + + /* FIXME: Use unicode version (not yet implemented) */ + hres = pExtractFilesA(file_name, program_files, 0, NULL, NULL, 0); + FreeLibrary(advpack); + HeapFree(GetProcessHeap(), 0, file_name); + if(FAILED(hres)) { + ERR("Could not extract package: %08lx\n", hres); + clean_up(); + } + + set_registry(program_files); + clean_up(); + + return S_OK; +} + +static HRESULT WINAPI InstallCallback_GetBindInfo(IBindStatusCallback *iface, + DWORD* grfBINDF, BINDINFO* pbindinfo) +{ + /* FIXME */ + *grfBINDF = 0; + return S_OK; +} + +static HRESULT WINAPI InstallCallback_OnDataAvailable(IBindStatusCallback *iface, DWORD grfBSCF, + DWORD dwSize, FORMATETC* pformatetc, STGMEDIUM* pstgmed) +{ + IStream *str = pstgmed->u.pstm; + BYTE buf[1024]; + DWORD size; + HRESULT hres; + + do { + size = 0; + hres = IStream_Read(str, buf, sizeof(buf), &size); + if(size) + WriteFile(tmp_file, buf, size, NULL, NULL); + }while(hres == S_OK); + + return S_OK; +} + +static HRESULT WINAPI InstallCallback_OnObjectAvailable(IBindStatusCallback *iface, + REFIID riid, IUnknown* punk) +{ + ERR("\n"); + return E_NOTIMPL; +} + +static IBindStatusCallbackVtbl InstallCallbackVtbl = { + InstallCallback_QueryInterface, + InstallCallback_AddRef, + InstallCallback_Release, + InstallCallback_OnStartBinding, + InstallCallback_GetPriority, + InstallCallback_OnLowResource, + InstallCallback_OnProgress, + InstallCallback_OnStopBinding, + InstallCallback_GetBindInfo, + InstallCallback_OnDataAvailable, + InstallCallback_OnObjectAvailable +}; + +static IBindStatusCallback InstallCallback = { &InstallCallbackVtbl }; + +static LPWSTR get_url(void) +{ + HKEY hkey; + DWORD res, type; + DWORD size = 512*sizeof(WCHAR); + LPWSTR url; + + static const WCHAR wszMshtmlKey[] = { + 'S','o','f','t','w','a','r','e','\\','W','i','n','e', + '\\','M','S','H','T','M','L',0}; + static const WCHAR wszGeckoUrl[] = {'G','e','c','k','o','U','r','l',0}; + + /* @@ Wine registry key: HKCU\Software\Wine\MSHTML */ + res = RegOpenKeyW(HKEY_CURRENT_USER, wszMshtmlKey, &hkey); + if(res != ERROR_SUCCESS) + return NULL; + + url = HeapAlloc(GetProcessHeap(), 0, size); + + res = RegQueryValueExW(hkey, wszGeckoUrl, NULL, &type, (LPBYTE)url, &size); + RegCloseKey(hkey); + if(res != ERROR_SUCCESS || type != REG_SZ) { + HeapFree(GetProcessHeap(), 0, url); + return NULL; + } + + return url; +} + +static DWORD WINAPI download_proc(PVOID arg) +{ + IMoniker *mon; + IBindCtx *bctx; + IStream *str = NULL; + HRESULT hres; + + CreateURLMoniker(NULL, url, &mon); + HeapFree(GetProcessHeap(), 0, url); + url = NULL; + + CreateAsyncBindCtx(0, &InstallCallback, 0, &bctx); + + hres = IMoniker_BindToStorage(mon, bctx, NULL, &IID_IStream, (void**)&str); + IBindCtx_Release(bctx); + if(FAILED(hres)) { + ERR("BindToStorage failed: %08lx\n", hres); + return 0; + } + + if(str) + IStream_Release(str); + + return 0; +} + +static INT_PTR CALLBACK installer_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) { + case WM_INITDIALOG: + install_dialog = hwnd; + return TRUE; + + case WM_COMMAND: + switch(wParam) { + case IDCANCEL: + EndDialog(hwnd, 0); + return FALSE; + + case ID_DWL_INSTALL: + EnableWindow(GetDlgItem(hwnd, ID_DWL_INSTALL), 0); + EnableWindow(GetDlgItem(hwnd, IDCANCEL), 0); /* FIXME */ + CreateThread(NULL, 0, download_proc, NULL, 0, NULL); + return FALSE; + } + } + + return FALSE; +} + +void install_wine_gecko(void) +{ + HANDLE hsem; + + SetLastError(ERROR_SUCCESS); + hsem = CreateSemaphoreA( NULL, 0, 1, "mshtml_install_semaphore"); + + if(GetLastError() == ERROR_ALREADY_EXISTS) { + WaitForSingleObject(hsem, INFINITE); + }else { + if((url = get_url())) + DialogBoxW(hInst, MAKEINTRESOURCEW(ID_DWL_DIALOG), 0, installer_proc); + } + + ReleaseSemaphore(hsem, 1, NULL); + CloseHandle(hsem); +} diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 17e44e60bf..654c0e2ecf 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -310,6 +310,8 @@ HRESULT HTMLElement_QI(HTMLElement*,REFIID,void**); HTMLDOMNode *get_node(HTMLDocument*,nsIDOMNode*); void release_nodes(HTMLDocument*); +void install_wine_gecko(void); + DEFINE_GUID(CLSID_AboutProtocol, 0x3050F406, 0x98B5, 0x11CF, 0xBB,0x82, 0x00,0xAA,0x00,0xBD,0xCE,0x0B); DEFINE_GUID(CLSID_JSProtocol, 0x3050F3B2, 0x98B5, 0x11CF, 0xBB,0x82, 0x00,0xAA,0x00,0xBD,0xCE,0x0B); DEFINE_GUID(CLSID_MailtoProtocol, 0x3050F3DA, 0x98B5, 0x11CF, 0xBB,0x82, 0x00,0xAA,0x00,0xBD,0xCE,0x0B); diff --git a/dlls/mshtml/nsembed.c b/dlls/mshtml/nsembed.c index d12326b1be..d883164d1a 100644 --- a/dlls/mshtml/nsembed.c +++ b/dlls/mshtml/nsembed.c @@ -266,8 +266,11 @@ static BOOL load_gecko(void) if(!get_wine_gecko_path(gre_path) && !get_mozctl_path(gre_path) && !get_mozilla_path(gre_path)) { - MESSAGE("Could not load Mozilla. HTML rendering will be disabled.\n"); - return FALSE; + install_wine_gecko(); + if(!get_wine_gecko_path(gre_path)) { + MESSAGE("Could not load Mozilla. HTML rendering will be disabled.\n"); + return FALSE; + } } TRACE("found path %s\n", debugstr_w(gre_path)); diff --git a/dlls/mshtml/resource.h b/dlls/mshtml/resource.h index d8c8ba190d..5535d04e46 100644 --- a/dlls/mshtml/resource.h +++ b/dlls/mshtml/resource.h @@ -18,6 +18,13 @@ #define IDS_HTMLDISABLED 7500 #define IDS_HTMLDOCUMENT 7501 +#define IDS_DOWNLOADING 7502 +#define IDS_INSTALLING 7503 + +#define ID_DWL_DIALOG 7600 +#define ID_DWL_PROGRESS 7601 +#define ID_DWL_INSTALL 7602 +#define ID_DWL_STATUS 7603 #define IDR_BROWSE_CONTEXT_MENU 24641 diff --git a/dlls/mshtml/rsrc.rc b/dlls/mshtml/rsrc.rc index ee91df4cc5..1bf708cc42 100644 --- a/dlls/mshtml/rsrc.rc +++ b/dlls/mshtml/rsrc.rc @@ -17,7 +17,8 @@ */ #include "windef.h" -#include "winbase.h" +#include "winuser.h" +#include "commctrl.h" #include "resource.h" diff --git a/dlls/mshtml/tests/Makefile.in b/dlls/mshtml/tests/Makefile.in index ed9e90e8c2..c2d9ecb8ec 100644 --- a/dlls/mshtml/tests/Makefile.in +++ b/dlls/mshtml/tests/Makefile.in @@ -3,7 +3,7 @@ TOPOBJDIR = ../../.. SRCDIR = @srcdir@ VPATH = @srcdir@ TESTDLL = mshtml.dll -IMPORTS = ole32 user32 urlmon kernel32 +IMPORTS = ole32 user32 urlmon advapi32 kernel32 EXTRALIBS = -luuid CTESTS = \ diff --git a/dlls/mshtml/tests/htmldoc.c b/dlls/mshtml/tests/htmldoc.c index 1835d7340d..fca371b9dc 100644 --- a/dlls/mshtml/tests/htmldoc.c +++ b/dlls/mshtml/tests/htmldoc.c @@ -2071,8 +2071,40 @@ static void test_HTMLDocument_hlink(void) ok(ref == 0, "ref=%ld, expected 0\n", ref); } +static void gecko_installer_workaround(BOOL disable) +{ + HKEY hkey; + DWORD res; + + static BOOL has_url = FALSE; + static char url[2048]; + + if(!disable && !has_url) + return; + + res = RegOpenKey(HKEY_CURRENT_USER, "Software\\Wine\\MSHTML", &hkey); + if(res != ERROR_SUCCESS) + return; + + if(disable) { + DWORD type, size = sizeof(url); + + res = RegQueryValueEx(hkey, "GeckoUrl", NULL, &type, (PVOID)url, &size); + if(res == ERROR_SUCCESS && type == REG_SZ) + has_url = TRUE; + + RegDeleteValue(hkey, "GeckoUrl"); + }else { + RegSetValueEx(hkey, "GeckoUrl", 0, REG_SZ, (PVOID)url, lstrlenA(url)+1); + } + + RegCloseKey(hkey); +} + START_TEST(htmldoc) { + gecko_installer_workaround(TRUE); + CoInitialize(NULL); container_hwnd = create_container_window(); @@ -2085,4 +2117,6 @@ START_TEST(htmldoc) DestroyWindow(container_hwnd); CoUninitialize(); + + gecko_installer_workaround(FALSE); }