jdk/src/windows/native/sun/jkernel/DownloadDialog.cpp
author herrick
Fri, 12 Jun 2009 14:56:32 -0400
changeset 3111 fefdeafb7ab9
child 5381 d6d64a42ff51
permissions -rw-r--r--
6797688: Umbrella: Merge all JDK 6u4 - 6u12 deployment code into JDK7 6845973: Update JDK7 with deployment changes in 6u13, 6u14 4802695: Support 64-bit Java Plug-in and Java webstart on Windows/Linux on AMD64 6825019: DownloadManager should not be loaded and referenced for full JRE 6738770: REGRESSION:JSException throws when use LiveConnect javascript facility 6772884: plugin2 : java.lang.OutOfMemoryError or crash 6707535: Crossing domain hole affecting multiple sites/domains using plug-in 6728071: Non-verification of Update files may allow unintended updates 6704154: Code loaded from local filesystem should not get access to localhost 6727081: Web Start security restrictions bypass using special extension jnlp 6727079: Java Web Start Socket() restriction bypass 6727071: Cache location/user name information disclosure in SingleInstanceImpl. 6716217: AppletClassLoader adds permissions based on codebase regardless of CS 6694892: Java Webstart inclusion via system properties override [CVE-2008-2086] 6704074: localhost socket access due to cache location exposed 6703909: Java webstart arbitrary file creation using nativelib 6665315: browser crashes when deployment.properties has more slashes ( / ) 6660121: Encoding values in JNLP files can cause buffer overflow 6606110: URLConnection.setProxiedHost for resources that are loaded via proxy 6581221: SSV(VISTA): Redirection FAILS to work if user does a downgrade install 6609756: Buffer Overflow in Java ActiveX component 6608712: Bypassing the same origin policy in Java with crafted names 6534630: "gnumake clobber" doesn't 6849953: JDK7 - replacement of bufferoverflowU.lib on amd64 breaks build 6849029: Need some JDK7 merge clean-up after comments on the webrev 6847582: Build problem on JDK7 with isSecureProperty in merge 6827935: JDK 7 deployment merging - problem in Compiler-msvm.gmk 6823215: latest merge fixes from 6u12 -> JDK7 6816153: further mergers for JDK7 deployment integration 6807074: Fix Java Kernel and JQS in initial JDK7 builds Summary: Initial changeset for implementing 6uX Deployment Features into JDK7 Reviewed-by: dgu, billyh

/*
 * Copyright 2008 - 2009 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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 General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define STRICT
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif
#define _ATL_APARTMENT_THREADED

#include <atlbase.h>
//You may derive a class from CComModule and use it if you want to override
//something, but do not change the name of _Module
extern CComModule _Module;
#include <atlcom.h>
#include <atlwin.h>

#include <atlhost.h>
#include <commdlg.h>
#include <commctrl.h>
#include <windowsx.h>
#include <urlmon.h>
#include <wininet.h>
#include <shellapi.h>
#include <time.h>
#include <math.h>
#include <stdio.h>
#include <jni.h>

#include "DownloadDialog.h"

#define UPDATE_INTERVAL 500
#define INITIAL_DELAY 2000
#define POST_DELAY 1000

/////////////////////////////////////////////////////////////////////////////
// CDownloadDialog

typedef BOOL (WINAPI * InitCommonControlsType)();

CDownloadDialog::CDownloadDialog()
{
    m_numDownloadThreadsRunning = 0;

    m_destroyWindowTimerStarted = FALSE;
    m_pszFileName = NULL;
    m_jvm = NULL;

    m_ulProgress = 0;
    m_ulProgressMax = 0;
    m_iProgressFactor = 0;
    m_iMaxProgressFactor = 1;


    m_hCancelEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hDownloadThreadExitEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
    m_hDialogInitializedEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);

    // Load up commctrl.dll
    // Loading dll dynamically we can use latest available version
    // (i.e. latest native components and extended API)
    HMODULE hModComCtl32 = ::LoadLibrary(TEXT("comctl32.dll"));
    if (hModComCtl32 != NULL) {
        /* Initialize controls to ensure proper appearance */
        InitCommonControlsType fn_InitCommonControls = (InitCommonControlsType)
            ::GetProcAddress(hModComCtl32, "InitCommonControls");
        fn_InitCommonControls();

        /* MessageBox replacement introduced in Vista */
        taskDialogFn = (TaskDialogIndirectFn)
            ::GetProcAddress(hModComCtl32, "TaskDialogIndirect");
    }
}


CDownloadDialog::~CDownloadDialog()
{
    ::CloseHandle(m_hCancelEvent);
    ::CloseHandle(m_hDownloadThreadExitEvent);
    ::CloseHandle(m_hDialogInitializedEvent);
}

void CDownloadDialog::addToTotalContentLength(DWORD contentLength) {
     __try
    {
        m_csDownload.Lock();
        if (m_ulProgressMax == 0) {
            // first download this session, initialize start time
            time(&m_startTime);
        }

        m_ulProgressMax = m_ulProgressMax + contentLength;
        logProgress();
    }
    __finally
    {
        m_csDownload.Unlock();
    }
}



void CDownloadDialog::initDialogText(LPCTSTR downloadURL, LPCTSTR bundleName) {

    // reset status text
    HWND hStatusWnd = GetDlgItem(IDC_TIME_REMAINING);
    ::SetWindowText(hStatusWnd, "");

    // reset progress bar
    HWND hProgressWnd = GetDlgItem(IDC_DOWNLOAD_PROGRESS);

    ::PostMessage(hProgressWnd, PBM_SETPOS, (WPARAM) 0, NULL);

    m_hMastheadFont = NULL;
    m_hDialogFont = NULL;
    m_hSixPointFont = NULL;

    m_hMemDC = NULL;

    TCHAR szDownloadText[BUFFER_SIZE];

    HWND hWndDownloadText = GetDlgItem(IDC_DOWNLOAD_TEXT);
    ::LoadString(_Module.GetModuleInstance(), IDS_DOWNLOAD_TEXT, szDownloadText, BUFFER_SIZE);
    ::SetWindowText(hWndDownloadText, szDownloadText);

    TCHAR szMasthead[BUFFER_SIZE];

    HWND hWndMastheadText = GetDlgItem(IDC_MASTHEAD_TEXT);
    ::LoadString(_Module.GetModuleInstance(), IDS_DOWNLOAD, szMasthead, BUFFER_SIZE);
    ::SetWindowText(hWndMastheadText, szMasthead);


}

BOOL CDownloadDialog::isDownloading() {
    return m_numDownloadThreadsRunning > 0;
}


void CDownloadDialog::bundleInstallStart() {
    __try
    {
        m_csNumDownloadThreads.Lock();
        m_numDownloadThreadsRunning++;
        // another download request has came in, kill the destroyWindowTimer
        KillTimer(destroyWindowTimerID);
        m_destroyWindowTimerStarted = FALSE;
    }
    __finally
    {
        m_csNumDownloadThreads.Unlock();
    }
}

void CDownloadDialog::bundleInstallComplete() {
    __try
    {
        m_csNumDownloadThreads.Lock();
        m_numDownloadThreadsRunning = max(m_numDownloadThreadsRunning - 1, 0);
        if (m_numDownloadThreadsRunning == 0) {
            m_ulProgress = m_ulProgressMax;
            logProgress();
        }
        // Signal main thread
        ::SetEvent(m_hDownloadThreadExitEvent);
    }
    __finally
    {
        m_csNumDownloadThreads.Unlock();
    }
}


//=--------------------------------------------------------------------------=
// CDownloadDialog::OnInitDialog
//=--------------------------------------------------------------------------=
// Message handler for WM_INITDIALOG
//
// Parameters:
//      uMsg        Windows Message
//      wParam      WPARAM
//      lParam      LPARAM
//      bHandled    FALSE if not handled
//
// Output:
//      LRESULT
//
// Notes:
//
LRESULT CDownloadDialog::OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
     __try
    {
        m_csDownload.Lock();
    }
    __finally
    {
        m_csDownload.Unlock();
    }
    // Set timer
    SetTimer(iTimerID, UPDATE_INTERVAL);

    m_hMastheadFont = NULL;
    m_hDialogFont = NULL;
    m_hSixPointFont = NULL;
    m_feedbackOnCancel = TRUE;

    m_hMemDC = NULL;

    TCHAR szDownloadText[BUFFER_SIZE];

    HWND hWndDownloadText = GetDlgItem(IDC_DOWNLOAD_TEXT);
    ::LoadString(_Module.GetModuleInstance(), IDS_DOWNLOAD_TEXT, szDownloadText, BUFFER_SIZE);
    ::SetWindowText(hWndDownloadText, szDownloadText);

    TCHAR szMasthead[BUFFER_SIZE];

    HWND hWndMastheadText = GetDlgItem(IDC_MASTHEAD_TEXT);
    ::LoadString(_Module.GetModuleInstance(), IDS_DOWNLOAD, szMasthead, BUFFER_SIZE);
    ::SetWindowText(hWndMastheadText, szMasthead);

    HICON javaCupIcon = ::LoadIcon(_Module.GetModuleInstance(), MAKEINTRESOURCE(IDI_JAVA));
    SetIcon(javaCupIcon, FALSE);

    ::SetEvent(m_hDialogInitializedEvent);

    return 0;  // do not set initial focus to cancel button
}


//=--------------------------------------------------------------------------=
// CDownloadDialog::OnOK
//=--------------------------------------------------------------------------=
// Message handler for WM_COMMAND with IDOK
//
// Parameters:
//      wNotifyCode Notify Code
//      wID         ID of control
//      hWndCtl     HWND of control
//      bHandled    FALSE if not handled
//
// Output:
//      LRESULT
//
// Notes:
//
LRESULT CDownloadDialog::OnOK(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
    // do nothing for now
    return 0;
}



//=--------------------------------------------------------------------------=
// CDownloadDialog::OnCancel
//=--------------------------------------------------------------------------=
// Message handler for WM_COMMAND with IDCANCEL
//
// Parameters:
//      wNotifyCode Notify Code
//      wID         ID of control
//      hWndCtl     HWND of control
//      bHandled    FALSE if not handled
//
// Output:
//      LRESULT
//
// Notes:
//
LRESULT CDownloadDialog::OnCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
{
    // Disable window first to avoid any keyboard input
    EnableWindow(FALSE);

    if (m_feedbackOnCancel) {
      int r = SafeMessageBox(IDS_DOWNLOAD_CANCEL_MESSAGE,
                       IDS_DOWNLOAD_CANCEL_INSTRUCTION,
                       IDS_DOWNLOAD_CANCEL_CAPTION,
                       DIALOG_WARNING_CANCELOK,
                       NULL, NULL);
      if (!::IsWindow(hWndCtl)) {
         /* It is possible that download was finished and download
            window hidden by the time user close this message box.
            If such case we should simply return. */
         return 0;
      }
      if (r == IDCANCEL) {
        EnableWindow(TRUE);
        return 0;
      }
    }

    __try
    {
        m_csDownload.Lock();
        // if we are downloading, signal download thread to stop downloading
        if (m_numDownloadThreadsRunning > 0) {
            SetEvent(m_hCancelEvent);
        }
    }
    __finally
    {
        m_csDownload.Unlock();
    }

    // Kill timer
    KillTimer(iTimerID);
    KillTimer(destroyWindowTimerID);

    FreeGDIResources();

    // Destroy dialog
    EndDialog(wID);

    return 0;
}

void CDownloadDialog::destroyDialog() {
    m_feedbackOnCancel = FALSE;
    ::PostMessage(m_hWnd, WM_COMMAND, IDCANCEL, NULL);
}


void CDownloadDialog::delayedDoModal() {
     __try
    {
         __try
        {
            m_csMessageBox.Lock();
            m_dialogUp = true;
            Sleep(INITIAL_DELAY);
        }
        __finally
        {
            m_csMessageBox.Unlock();
        }

        if (isDownloading())
            DoModal();
    }
    __finally
    {
        m_dialogUp = false;
    }
}


//=--------------------------------------------------------------------------=
// CDownloadDialog::SafeMessageBox
//=--------------------------------------------------------------------------=
// Helper method that uses best availble API to show native error/information
// dialog. In particular, it uses TaskDialog if availble (Vista specific)
// and MessageBox otherwise.
//
// It also ensures that the message box is always displayed on top of
// the progress dialog instead of underneath
//

//helper structures to define XP vs Vista style differences
static TASKDIALOG_COMMON_BUTTON_FLAGS vistaDialogButtons[] = {
    TDCBF_RETRY_BUTTON | TDCBF_CANCEL_BUTTON,
    TDCBF_OK_BUTTON | TDCBF_CANCEL_BUTTON
};
static PCWSTR vistaIcons[] = {
    TD_ERROR_ICON,
    TD_WARNING_ICON
};

static UINT xpStyle[] = {
    MB_ICONERROR | MB_RETRYCANCEL,
    MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2
};

int CDownloadDialog::SafeMessageBox(UINT details, UINT mainInstruction, UINT caption, DialogType type, LPCWSTR instructionArg, LPCWSTR detailsArg) {
    WCHAR textCaption[BUFFER_SIZE+1];
    WCHAR textDetails[BUFFER_SIZE+1];
    WCHAR textInstruction[BUFFER_SIZE+1];
    WCHAR tmpBuffer[BUFFER_SIZE+1];

    /* make sure buffers are terminated */
    textCaption[BUFFER_SIZE] = textDetails[BUFFER_SIZE] = 0;
    textInstruction[BUFFER_SIZE] = tmpBuffer[BUFFER_SIZE] = 0;

    if (detailsArg != NULL) {
        ::LoadStringW(_Module.GetResourceInstance(),
                 details,
                 tmpBuffer,
                 BUFFER_SIZE);
        _snwprintf(textDetails, BUFFER_SIZE, tmpBuffer, detailsArg);
    } else {
        ::LoadStringW(_Module.GetResourceInstance(),
                 details,
                 textDetails,
                 BUFFER_SIZE);
    }

    if (instructionArg != NULL) {
        ::LoadStringW(_Module.GetResourceInstance(),
                 mainInstruction,
                 tmpBuffer,
                 BUFFER_SIZE);
        _snwprintf(textInstruction, BUFFER_SIZE, tmpBuffer, instructionArg);
     } else {
        ::LoadStringW(_Module.GetResourceInstance(),
                 mainInstruction,
                 textInstruction,
                 BUFFER_SIZE);
     }

    ::LoadStringW(_Module.GetResourceInstance(),
                 caption,
                 textCaption,
                 BUFFER_SIZE);

    __try
    {
        m_csMessageBox.Lock();
        if (m_dialogUp) {
            waitUntilInitialized();
        }
        /* If TaskDialog availble - use it! */
        if (taskDialogFn != NULL) {
              TASKDIALOGCONFIG tc = { 0 };
              int nButton;

              tc.cbSize = sizeof(tc);
              tc.hwndParent = ::IsWindow(m_hWnd) ? m_hWnd : NULL;
              tc.dwCommonButtons = vistaDialogButtons[type];
              tc.pszWindowTitle = textCaption;
              tc.pszMainInstruction = textInstruction;
              tc.pszContent = textDetails;
              tc.pszMainIcon = vistaIcons[type];
              /* workaround: we need to make sure Cancel is default
                             for this type of Dialog */
              if (type == DIALOG_WARNING_CANCELOK) {
                  tc.nDefaultButton = IDCANCEL;
              }

              taskDialogFn(&tc, &nButton, NULL, NULL);
              return nButton;
        } else { /* default: use MessageBox */
            /* Note that MessageBox API expects content as single string
               and therefore we need to concatenate instruction
               and details as 2 paragraphs.

               The only exception is empty instruction. */
            if (wcslen(textInstruction) > 0) {
                wcsncat(textInstruction, L"\n\n",
                        BUFFER_SIZE - wcslen(textInstruction));
            }
            wcsncat(textInstruction, textDetails,
                    BUFFER_SIZE - wcslen(textInstruction));

            return ::MessageBoxW(::IsWindow(m_hWnd) ? m_hWnd : NULL,
                textInstruction, textCaption, xpStyle[type]);
        }
    }
    __finally
    {
        m_csMessageBox.Unlock();
    }
}


//=--------------------------------------------------------------------------=
// CDownloadDialog::OnTimer
//=--------------------------------------------------------------------------=
// Message handler for WM_TIMER
//
// Parameters:
//      uMsg        Windows Message
//      wParam      WPARAM
//      lParam      LPARAM
//      bHandled    FALSE if not handled
//
// Output:
//      LRESULT
//
// Notes:
//
LRESULT CDownloadDialog::OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if (destroyWindowTimerID == (int)wParam) {
        KillTimer(destroyWindowTimerID);
        m_destroyWindowTimerStarted = FALSE;
        m_ulProgressMax = max(0, m_ulProgressMax - m_ulProgress);
        logProgress();
        m_ulProgress = 0;
        logProgress();
        m_feedbackOnCancel = FALSE;
        ::PostMessage(m_hWnd, WM_COMMAND, IDCANCEL, NULL);
    }

    if (iTimerID == (int)wParam)
    {

        __try
        {
            m_csDownload.Lock();

            HWND hStatusWnd = GetDlgItem(IDC_TIME_REMAINING);
            HWND hProgressWnd = GetDlgItem(IDC_DOWNLOAD_PROGRESS);

            if (m_ulProgress && m_ulProgressMax)
            {
                ::PostMessage(hProgressWnd, PBM_SETPOS,
                     (WPARAM) (m_ulProgress * 100
                        / m_ulProgressMax), NULL);

                time_t currentTime;
                time(&currentTime);

                double elapsed_time = difftime(currentTime, m_startTime);
                double remain_time = (elapsed_time / m_ulProgress) *
                                      (m_ulProgressMax - m_ulProgress);
                int hr = 0, min = 0;

                if (remain_time > 60 * 60)
                {
                    hr = int(remain_time / (60 * 60));
                    remain_time = remain_time - hr * 60 * 60;
                }

                if (remain_time > 60)
                {
                    min = int(remain_time / 60);
                    remain_time = remain_time - min * 60;
                }

                TCHAR szBuffer[BUFFER_SIZE];
                TCHAR szTimeBuffer[BUFFER_SIZE];

                if (hr > 0)
                {
                    if (hr > 1)
                        LoadString(_Module.GetResourceInstance(), IDS_HOURSMINUTESECOND,
                                   szTimeBuffer, BUFFER_SIZE);
                    else
                        LoadString(_Module.GetResourceInstance(), IDS_HOURMINUTESECOND,
                                   szTimeBuffer, BUFFER_SIZE);

                    sprintf(szBuffer, szTimeBuffer, hr, min, remain_time);
                }
                else
                {
                    if (min > 0)
                    {
                        LoadString(_Module.GetResourceInstance(), IDS_MINUTESECOND,
                                   szTimeBuffer, BUFFER_SIZE);
                        sprintf(szBuffer, szTimeBuffer, min, remain_time);

                    }
                    else
                    {
                        LoadString(_Module.GetResourceInstance(), IDS_SECOND,
                                   szTimeBuffer, BUFFER_SIZE);
                        sprintf(szBuffer, szTimeBuffer, remain_time);

                    }
                }

                if (m_ulProgress == m_ulProgressMax) {
                    // download is done, unpacking bundle now, and waiting
                    // for another download to take place
                    ::LoadString(_Module.GetResourceInstance(),
                            IDS_DOWNLOAD_UNPACKING, szBuffer, BUFFER_SIZE);
                    __try
                    {
                        m_csNumDownloadThreads.Lock();
                        // both download and unpacking is done, start
                        // timer to destroy the progress window in 500ms
                        if (!m_destroyWindowTimerStarted &&
                               m_numDownloadThreadsRunning == 0) {
                            SetTimer(destroyWindowTimerID, POST_DELAY);
                            m_destroyWindowTimerStarted = TRUE;
                        }
                    }
                    __finally
                    {
                        m_csNumDownloadThreads.Unlock();
                    }
                }

                // Update status message
                ::SetWindowText(hStatusWnd, szBuffer);
            }
        }
        __finally
        {
           m_csDownload.Unlock();
        }
    }

    return 0;
}

// Message handler for WM_ONCTLCOLORSTATIC.
// this message is sent each time a static control is drawn.
// we get the Control ID and then set background color and font
// as appropriate for that control.
LRESULT CDownloadDialog::OnCtlColorStatic(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    HDC hdc = (HDC) wParam;
    HWND hwnd = (HWND) lParam;

    int DlgCtrlID = ::GetDlgCtrlID(hwnd);

    if (DlgCtrlID == IDC_DOWNLOAD_TEXT )
    {
        if (m_hDialogFont == NULL)
        {
            m_hDialogFont = CreateDialogFont(hdc, TEXT("MS Shell Dlg"), 8);
        }

        ::SelectObject(hdc, m_hDialogFont);
        return 0;
    }
    else if (DlgCtrlID == IDC_TIME_REMAINING)
    {
        if (m_hSixPointFont == NULL)
        {
            m_hSixPointFont = CreateDialogFont(hdc, TEXT("MS Shell Dlg"), 8);
        }

        ::SelectObject(hdc, m_hSixPointFont);
        return 0;
    }
    else if (DlgCtrlID == IDC_MASTHEAD_TEXT)
    {
        if (m_hMastheadFont == NULL)
        {
            m_hMastheadFont = CreateDialogFont(hdc, TEXT("MS Shell Dlg"), 12, 1);
        }

        ::SelectObject(hdc, m_hMastheadFont);
        return (LRESULT) GetStockObject(WHITE_BRUSH);
    }
    else if (DlgCtrlID == IDC_DOWNLOAD_MASTHEAD)
    {
        if (m_hMemDC == NULL)
        {
            m_hBitmap = LoadBitmap(_Module.GetModuleInstance(),
                                   MAKEINTRESOURCE(IDI_MASTHEAD));
            GetObject(m_hBitmap, sizeof(BITMAP), &m_bmMasthead);
            m_hMemDC = CreateCompatibleDC(NULL);
            SelectObject(m_hMemDC, m_hBitmap);
        }

        RECT rect;
        ::GetClientRect(hwnd, &rect);

        StretchBlt(hdc, rect.left, rect.top, (rect.right - rect.left), (rect.bottom - rect.top),
                   m_hMemDC, 0, 0, m_bmMasthead.bmWidth, m_bmMasthead.bmHeight, SRCCOPY);

        return (LRESULT) GetStockObject(NULL_BRUSH);
    }


    return 0;
}


//=--------------------------------------------------------------------------=
// CDownloadDialog::OnStartBinding
//=--------------------------------------------------------------------------=
// Called when download is started
//
// Parameters:
//
// Output:
//      HRESULT
//
// Notes:
//
STDMETHODIMP CDownloadDialog::OnStartBinding()
{
    __try
    {
        m_csDownload.Lock();
        time(&m_startTime);
    }
    __finally
    {
        m_csDownload.Unlock();
    }

    return S_OK;
}


//=--------------------------------------------------------------------------=
// CDownloadDialog::OnProgress
//=--------------------------------------------------------------------------=
// Called when download is in progress
//
// Parameters: ULONG ulProgress
//
// Output:
//      HRESULT
//
// Notes:
//
STDMETHODIMP CDownloadDialog::OnProgress(ULONG ulProgress)
{
    __try
    {
        m_csDownload.Lock();
        m_ulProgress = m_ulProgress + ulProgress;
        logProgress();

    }
    __finally
    {
        m_csDownload.Unlock();
    }

    return S_OK;
}

void CDownloadDialog::decrementProgressMax(ULONG contentLength, ULONG readSoFar) {
    __try
    {
        m_csDownload.Lock();
        m_ulProgressMax = m_ulProgressMax - contentLength;
        m_ulProgress = m_ulProgress - readSoFar;
        logProgress();
    }
    __finally
    {
        m_csDownload.Unlock();
    }

}

void CDownloadDialog::waitUntilInitialized() {
    // wait until download progress dialog is initialized and ready to show
    WaitForSingleObject(m_hDialogInitializedEvent, INFINITE);
    ResetEvent(m_hDialogInitializedEvent);

}

// Check if download has been cancelled
BOOL CDownloadDialog::isDownloadCancelled() {
    if (WAIT_OBJECT_0 == WaitForSingleObject(m_hCancelEvent, 0)) {
        return TRUE;
    }
    return FALSE;
}



// Create the fonts we need for the download and
// install UE
HFONT CDownloadDialog::CreateDialogFont(HDC hdc, LPCTSTR lpszFaceName, int ptSize, int isBold)
{
    POINT pt;
    FLOAT cxDPI, cyDPI;
    HFONT hFont;
    LOGFONT lf;

    int iDeciPtWidth = 0;
    int iDeciPtHeight = 10 * ptSize;

    int iSavedDC = SaveDC(hdc);

    SetGraphicsMode (hdc, GM_ADVANCED);
    ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
    SetViewportOrgEx (hdc, 0,0, NULL);
    SetWindowOrgEx (hdc, 0,0, NULL);

    cxDPI = (FLOAT) GetDeviceCaps(hdc, LOGPIXELSX);
    cyDPI = (FLOAT) GetDeviceCaps(hdc, LOGPIXELSY);

    pt.x = (int) (iDeciPtWidth * cxDPI / 72);
    pt.y = (int) (iDeciPtHeight * cyDPI / 72);

    DPtoLP(hdc, &pt, 1);

    lf.lfHeight = - (int) (fabs ((double) pt.y) / 10.0 + 0.5);
    lf.lfWidth = 0;
    lf.lfEscapement = 0;
    lf.lfOrientation = 0;
    lf.lfWeight = (isBold > 0) ? FW_BOLD : 0;
    lf.lfItalic = 0;
    lf.lfUnderline = 0;
    lf.lfStrikeOut = 0;
    lf.lfCharSet = 0;
    lf.lfOutPrecision = 0;
    lf.lfClipPrecision = 0;
    lf.lfQuality = 0;
    lf.lfPitchAndFamily = 0;

    TCHAR szLocaleData[BUFFER_SIZE];
    GetLocaleInfo(LOCALE_SYSTEM_DEFAULT, LOCALE_SENGCOUNTRY,
                  szLocaleData, BUFFER_SIZE);

    if (strncmp(szLocaleData, "Japan", 5) == 0) {
        // need special font for _ja locale
        strcpy (lf.lfFaceName, TEXT("MS UI Gothic"));
    } else {
        strcpy (lf.lfFaceName, lpszFaceName);
    }

    hFont = CreateFontIndirect(&lf);

    RestoreDC (hdc, iSavedDC);
    return hFont;
}

void CDownloadDialog::FreeGDIResources ()
{
    ::DeleteObject(m_hMastheadFont);
    m_hMastheadFont = NULL;

    ::DeleteObject(m_hDialogFont);
    m_hDialogFont = NULL;

    ::DeleteObject(m_hSixPointFont);
    m_hSixPointFont = NULL;

    ::DeleteObject(m_hBitmap);
    m_hBitmap = NULL;

    ::DeleteDC(m_hMemDC);
    m_hMemDC = NULL;
}


JNIEnv* CDownloadDialog::getJNIEnv() {
    if (m_jvm == NULL)
        return NULL;
    JNIEnv *env;
    m_jvm->AttachCurrentThread((void**) &env, NULL);
    return env;
}


void CDownloadDialog::log(char *msg) {
    JNIEnv *env = getJNIEnv();
    if (env != NULL) {
        jclass dm = env->FindClass("sun/jkernel/DownloadManager");
        if (dm == NULL) {
            printf("Cound not find class sun.jkernel.DownloadManager\n");
            return;
        }
        jmethodID log = env->GetStaticMethodID(dm, "log", "(Ljava/lang/String;)V");
        if (log == NULL) {
            printf("Could not find method sun.jkernel.DownloadManager.log(String)\n");
            return;
        }
        jstring string = env->NewStringUTF(msg);
        if (string == NULL) {
            printf("Error creating log string\n");
            return;
        }
        env->CallStaticVoidMethod(dm, log, string);
    }
}


void CDownloadDialog::logProgress() {
    char msg[256];
    sprintf(msg, "Progress: %d / %d", m_ulProgress, m_ulProgressMax);
    log(msg);
}