Index: /trunk/src/VBox/Main/include/ProgressImpl.h
===================================================================
--- /trunk/src/VBox/Main/include/ProgressImpl.h	(revision 54991)
+++ /trunk/src/VBox/Main/include/ProgressImpl.h	(revision 54992)
@@ -6,5 +6,5 @@
 
 /*
- * Copyright (C) 2006-2014 Oracle Corporation
+ * Copyright (C) 2006-2015 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -33,49 +33,6 @@
     public ProgressWrap
 {
-protected:
-
-    DECLARE_EMPTY_CTOR_DTOR (Progress)
-
-    void i_checkForAutomaticTimeout(void);
-
-#if !defined (VBOX_COM_INPROC)
-    /** Weak parent. */
-    VirtualBox * const      mParent;
-#endif
-
-    const ComPtr<IUnknown>  mInitiator;
-
-    const Guid mId;
-    const com::Utf8Str mDescription;
-
-    uint64_t m_ullTimestamp;                        // progress object creation timestamp, for ETA computation
-
-    void (*m_pfnCancelCallback)(void *);
-    void *m_pvCancelUserArg;
-
-    /* The fields below are to be properly initialized by subclasses */
-
-    BOOL mCompleted;
-    BOOL mCancelable;
-    BOOL mCanceled;
-    HRESULT mResultCode;
-    ComPtr<IVirtualBoxErrorInfo> mErrorInfo;
-
-    ULONG m_cOperations;                            // number of operations (so that progress dialog can
-                                                    // display something like 1/3)
-    ULONG m_ulTotalOperationsWeight;                // sum of weights of all operations, given to constructor
-
-    ULONG m_ulOperationsCompletedWeight;            // summed-up weight of operations that have been completed; initially 0
-
-    ULONG m_ulCurrentOperation;                     // operations counter, incremented with
-                                                    // each setNextOperation()
-    com::Utf8Str m_operationDescription;            // name of current operation; initially
-                                                    // from constructor, changed with setNextOperation()
-    ULONG m_ulCurrentOperationWeight;               // weight of current operation, given to setNextOperation()
-    ULONG m_ulOperationPercent;                     // percentage of current operation, set with setCurrentOperationProgress()
-    ULONG m_cMsTimeout;                             /**< Automatic timeout value. 0 means none. */
-
 public:
-    DECLARE_NOT_AGGREGATABLE (Progress)
+    DECLARE_NOT_AGGREGATABLE(Progress)
 
     HRESULT FinalConstruct();
@@ -94,5 +51,5 @@
      */
     HRESULT init(
-#if !defined (VBOX_COM_INPROC)
+#if !defined(VBOX_COM_INPROC)
                   VirtualBox *aParent,
 #endif
@@ -102,5 +59,5 @@
     {
         return init(
-#if !defined (VBOX_COM_INPROC)
+#if !defined(VBOX_COM_INPROC)
             aParent,
 #endif
@@ -126,5 +83,5 @@
      */
     HRESULT init(
-#if !defined (VBOX_COM_INPROC)
+#if !defined(VBOX_COM_INPROC)
                   VirtualBox *aParent,
 #endif
@@ -135,5 +92,5 @@
     {
         return init(
-#if !defined (VBOX_COM_INPROC)
+#if !defined(VBOX_COM_INPROC)
             aParent,
 #endif
@@ -148,5 +105,5 @@
 
     HRESULT init(
-#if !defined (VBOX_COM_INPROC)
+#if !defined(VBOX_COM_INPROC)
                   VirtualBox *aParent,
 #endif
@@ -167,6 +124,4 @@
 
     // public methods only for internal purposes
-    HRESULT i_setResultCode(HRESULT aResultCode);
-
     HRESULT i_notifyComplete(HRESULT aResultCode);
     HRESULT i_notifyComplete(HRESULT aResultCode,
@@ -180,18 +135,52 @@
                               const char *aText,
                               va_list va);
+    HRESULT i_notifyCompleteEI(HRESULT aResultCode,
+                               const ComPtr<IVirtualBoxErrorInfo> &aErrorInfo);
+
     bool i_notifyPointOfNoReturn(void);
-
-    // public methods only for internal purposes
     bool i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser);
 
-    // unsafe inline public methods for internal purposes only (ensure there is
-    // a caller and a read lock before calling them!)
-    BOOL i_getCompleted() const { return mCompleted; }
-    HRESULT i_getResultCode() const { return mResultCode; }
-    double i_calcTotalPercent();
+protected:
+    DECLARE_EMPTY_CTOR_DTOR(Progress)
+
+#if !defined(VBOX_COM_INPROC)
+    /** Weak parent. */
+    VirtualBox * const      mParent;
+#endif
+
+    const ComPtr<IUnknown>  mInitiator;
+
+    const Guid mId;
+    const com::Utf8Str mDescription;
+
+    uint64_t m_ullTimestamp;                        // progress object creation timestamp, for ETA computation
+
+    void (*m_pfnCancelCallback)(void *);
+    void *m_pvCancelUserArg;
+
+    /* The fields below are to be properly initialized by subclasses */
+
+    BOOL mCompleted;
+    BOOL mCancelable;
+    BOOL mCanceled;
+    HRESULT mResultCode;
+    ComPtr<IVirtualBoxErrorInfo> mErrorInfo;
+
+    ULONG m_cOperations;                            // number of operations (so that progress dialog can
+                                                    // display something like 1/3)
+    ULONG m_ulTotalOperationsWeight;                // sum of weights of all operations, given to constructor
+
+    ULONG m_ulOperationsCompletedWeight;            // summed-up weight of operations that have been completed; initially 0
+
+    ULONG m_ulCurrentOperation;                     // operations counter, incremented with
+                                                    // each setNextOperation()
+    com::Utf8Str m_operationDescription;            // name of current operation; initially
+                                                    // from constructor, changed with setNextOperation()
+    ULONG m_ulCurrentOperationWeight;               // weight of current operation, given to setNextOperation()
+    ULONG m_ulOperationPercent;                     // percentage of current operation, set with setCurrentOperationProgress()
+    ULONG m_cMsTimeout;                             /**< Automatic timeout value. 0 means none. */
 
 private:
-
-    // Wrapped IProgress Data
+    // wrapped IProgress properties
     HRESULT getId(com::Guid &aId);
     HRESULT getDescription(com::Utf8Str &aDescription);
@@ -211,9 +200,9 @@
     HRESULT getTimeout(ULONG *aTimeout);
     HRESULT setTimeout(ULONG aTimeout);
+
+    // wrapped IProgress methods
     HRESULT setCurrentOperationProgress(ULONG aPercent);
     HRESULT setNextOperation(const com::Utf8Str &aNextOperationDescription,
                              ULONG aNextOperationsWeight);
-
-    // Wrapped Iprogress methods
     HRESULT waitForCompletion(LONG aTimeout);
     HRESULT waitForOperationCompletion(ULONG aOperation,
@@ -222,4 +211,7 @@
     HRESULT cancel();
 
+    // internal helper methods
+    double i_calcTotalPercent();
+    void i_checkForAutomaticTimeout(void);
 
     RTSEMEVENTMULTI mCompletedSem;
Index: /trunk/src/VBox/Main/src-all/ProgressImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-all/ProgressImpl.cpp	(revision 54991)
+++ /trunk/src/VBox/Main/src-all/ProgressImpl.cpp	(revision 54992)
@@ -6,5 +6,5 @@
 
 /*
- * Copyright (C) 2006-2014 Oracle Corporation
+ * Copyright (C) 2006-2015 Oracle Corporation
  *
  * This file is part of VirtualBox Open Source Edition (OSE), as
@@ -54,290 +54,4 @@
 }
 
-
-// IProgress properties
-/////////////////////////////////////////////////////////////////////////////
-
-HRESULT Progress::getId(com::Guid &aId)
-{
-    /* mId is constant during life time, no need to lock */
-    aId = mId;
-
-    return S_OK;
-}
-
-HRESULT Progress::getDescription(com::Utf8Str &aDescription)
-{
-    /* mDescription is constant during life time, no need to lock */
-    aDescription = mDescription;
-
-    return S_OK;
-}
-HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
-{
-
-    /* mInitiator/mParent are constant during life time, no need to lock */
-
-#if !defined(VBOX_COM_INPROC)
-    if (mInitiator)
-        mInitiator.queryInterfaceTo(aInitiator.asOutParam());
-    else
-    {
-        ComObjPtr<VirtualBox> pVirtualBox(mParent);
-        pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
-    }
-#else
-    mInitiator.queryInterfaceTo(aInitiator.asOutParam());
-#endif
-
-    return S_OK;
-}
-
-HRESULT Progress::getCancelable(BOOL *aCancelable)
-{
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aCancelable = mCancelable;
-
-    return S_OK;
-}
-
-/**
- * Internal helper to compute the total percent value based on the member values and
- * returns it as a "double". This is used both by GetPercent (which returns it as a
- * rounded ULONG) and GetTimeRemaining().
- *
- * Requires locking by the caller!
- *
- * @return fractional percentage as a double value.
- */
-double Progress::i_calcTotalPercent()
-{
-    // avoid division by zero
-    if (m_ulTotalOperationsWeight == 0)
-        return 0;
-
-    double dPercent = (    (double)m_ulOperationsCompletedWeight  // weight of operations that have been completed
-                         + ((double)m_ulOperationPercent *
-                            (double)m_ulCurrentOperationWeight / (double)100)  // plus partial weight of the current operation
-                      ) * (double)100 / (double)m_ulTotalOperationsWeight;
-
-    return dPercent;
-}
-
-/**
- * Internal helper for automatically timing out the operation.
- *
- * The caller should hold the object write lock.
- */
-void Progress::i_checkForAutomaticTimeout(void)
-{
-    if (   m_cMsTimeout
-        && mCancelable
-        && !mCanceled
-        && RTTimeMilliTS() - m_ullTimestamp > m_cMsTimeout
-       )
-        Cancel();
-}
-
-HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (mCompleted)
-        *aTimeRemaining = 0;
-    else
-    {
-        double dPercentDone = i_calcTotalPercent();
-        if (dPercentDone < 1)
-            *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
-        else
-        {
-            uint64_t ullTimeNow = RTTimeMilliTS();
-            uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
-            uint64_t ullTimeTotal = (uint64_t)(ullTimeElapsed * 100 / dPercentDone);
-            uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
-
-//          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
-//                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
-
-            *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
-        }
-    }
-
-    return S_OK;
-}
-
-HRESULT Progress::getPercent(ULONG *aPercent)
-{
-    i_checkForAutomaticTimeout();
-
-    /* checkForAutomaticTimeout requires a write lock. */
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (mCompleted && SUCCEEDED(mResultCode))
-        *aPercent = 100;
-    else
-    {
-        ULONG ulPercent = (ULONG)i_calcTotalPercent();
-        // do not report 100% until we're really really done with everything as the Qt GUI dismisses progress dialogs in that case
-        if (    ulPercent == 100
-             && (    m_ulOperationPercent < 100
-                  || (m_ulCurrentOperation < m_cOperations -1)
-                )
-           )
-            *aPercent = 99;
-        else
-            *aPercent = ulPercent;
-    }
-
-    i_checkForAutomaticTimeout();
-
-    return S_OK;
-}
-
-HRESULT Progress::getCompleted(BOOL *aCompleted)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aCompleted = mCompleted;
-
-    return S_OK;
-}
-
-HRESULT Progress::getCanceled(BOOL *aCanceled)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aCanceled = mCanceled;
-
-    return S_OK;
-}
-
-HRESULT Progress::getResultCode(LONG *aResultCode)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (!mCompleted)
-        return setError(E_FAIL,
-                        tr("Result code is not available, operation is still in progress"));
-
-    *aResultCode = mResultCode;
-
-    return S_OK;
-}
-
-HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (!mCompleted)
-        return setError(E_FAIL,
-                        tr("Error info is not available, operation is still in progress"));
-
-    mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
-
-    return S_OK;
-}
-
-HRESULT Progress::getOperationCount(ULONG *aOperationCount)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aOperationCount = m_cOperations;
-
-    return S_OK;
-}
-
-HRESULT Progress::getOperation(ULONG *aOperation)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aOperation = m_ulCurrentOperation;
-
-    return S_OK;
-}
-
-HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
-{
-
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    aOperationDescription = m_operationDescription;
-
-    return S_OK;
-}
-
-HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (mCompleted && SUCCEEDED(mResultCode))
-        *aOperationPercent = 100;
-    else
-        *aOperationPercent = m_ulOperationPercent;
-
-    return S_OK;
-}
-
-HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aOperationWeight = m_ulCurrentOperationWeight;
-
-    return S_OK;
-}
-
-HRESULT Progress::setTimeout(ULONG aTimeout)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (!mCancelable)
-        return setError(VBOX_E_INVALID_OBJECT_STATE,
-                        tr("Operation cannot be canceled"));
-
-    LogThisFunc(("%#x => %#x\n", m_cMsTimeout, aTimeout));
-    m_cMsTimeout = aTimeout;
-    return S_OK;
-}
-
-HRESULT Progress::getTimeout(ULONG *aTimeout)
-{
-    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    *aTimeout = m_cMsTimeout;
-    return S_OK;
-}
-
-// public methods only for internal purposes
-////////////////////////////////////////////////////////////////////////////////
-
-/**
- * Sets the cancelation callback, checking for cancelation first.
- *
- * @returns Success indicator.
- * @retval  true on success.
- * @retval  false if the progress object has already been canceled or is in an
- *          invalid state
- *
- * @param   pfnCallback     The function to be called upon cancelation.
- * @param   pvUser          The callback argument.
- */
-bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
-{
-    AutoCaller autoCaller(this);
-    AssertComRCReturn(autoCaller.rc(), false);
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    i_checkForAutomaticTimeout();
-    if (mCanceled)
-        return false;
-
-    m_pvCancelUserArg   = pvUser;
-    m_pfnCancelCallback = pfnCallback;
-    return true;
-}
 
 HRESULT Progress::FinalConstruct()
@@ -363,11 +77,8 @@
     m_pvCancelUserArg = NULL;
 
-    HRESULT rc = Progress::BaseFinalConstruct();
-    if (FAILED(rc)) return rc;
-
     mCompletedSem = NIL_RTSEMEVENTMULTI;
     mWaitersCount = 0;
 
-    return S_OK;
+    return Progress::BaseFinalConstruct();
 }
 
@@ -607,6 +318,5 @@
     RTSemEventMultiDestroy(mCompletedSem);
 
-    /* release initiator (effective only if mInitiator has been assigned in
- *      * init()) */
+    /* release initiator (effective only if mInitiator has been assigned in init()) */
     unconst(mInitiator).setNull();
 
@@ -624,9 +334,480 @@
 
 
+// public methods only for internal purposes
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Marks the whole task as complete and sets the result code.
+ *
+ * If the result code indicates a failure (|FAILED(@a aResultCode)|) then this
+ * method will import the error info from the current thread and assign it to
+ * the errorInfo attribute (it will return an error if no info is available in
+ * such case).
+ *
+ * If the result code indicates a success (|SUCCEEDED(@a aResultCode)|) then
+ * the current operation is set to the last.
+ *
+ * Note that this method may be called only once for the given Progress object.
+ * Subsequent calls will assert.
+ *
+ * @param aResultCode   Operation result code.
+ */
+HRESULT Progress::i_notifyComplete(HRESULT aResultCode)
+{
+    HRESULT rc;
+    ComPtr<VirtualBoxErrorInfo> errorInfo;
+    if (FAILED(aResultCode))
+    {
+        /* try to import error info from the current thread */
+#if !defined(VBOX_WITH_XPCOM)
+        ComPtr<IErrorInfo> err;
+        rc = ::GetErrorInfo(0, err.asOutParam());
+        if (rc == S_OK && err)
+            rc = err.queryInterfaceTo(mErrorInfo.asOutParam());
+#else /* !defined(VBOX_WITH_XPCOM) */
+        nsCOMPtr<nsIExceptionService> es;
+        es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
+        if (NS_SUCCEEDED(rc))
+        {
+            nsCOMPtr <nsIExceptionManager> em;
+            rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
+            if (NS_SUCCEEDED(rc))
+            {
+                ComPtr<nsIException> ex;
+                rc = em->GetCurrentException(ex.asOutParam());
+                if (NS_SUCCEEDED(rc) && ex)
+                    rc = ex.queryInterfaceTo(errorInfo.asOutParam());
+            }
+        }
+#endif /* !defined(VBOX_WITH_XPCOM) */
+    }
+
+    return i_notifyCompleteEI(aResultCode, errorInfo);
+}
+
+/**
+ * Wrapper around Progress:notifyCompleteV.
+ */
+HRESULT Progress::i_notifyComplete(HRESULT aResultCode,
+                                   const GUID &aIID,
+                                   const char *pcszComponent,
+                                   const char *aText,
+                                   ...)
+{
+    va_list va;
+    va_start(va, aText);
+    HRESULT hrc = i_notifyCompleteV(aResultCode, aIID, pcszComponent, aText, va);
+    va_end(va);
+    return hrc;
+}
+
+/**
+ * Marks the operation as complete and attaches full error info.
+ *
+ * @param aResultCode   Operation result (error) code, must not be S_OK.
+ * @param aIID          IID of the interface that defines the error.
+ * @param aComponent    Name of the component that generates the error.
+ * @param aText         Error message (must not be null), an RTStrPrintf-like
+ *                      format string in UTF-8 encoding.
+ * @param va            List of arguments for the format string.
+ */
+HRESULT Progress::i_notifyCompleteV(HRESULT aResultCode,
+                                    const GUID &aIID,
+                                    const char *pcszComponent,
+                                    const char *aText,
+                                    va_list va)
+{
+    /* expected to be used only in case of error */
+    Assert(FAILED(aResultCode));
+
+    Utf8Str text(aText, va);
+    ComObjPtr<VirtualBoxErrorInfo> errorInfo;
+    HRESULT rc = errorInfo.createObject();
+    AssertComRCReturnRC(rc);
+    errorInfo->init(aResultCode, aIID, pcszComponent, text);
+
+    return i_notifyCompleteEI(aResultCode, errorInfo);
+}
+
+/**
+ * Marks the operation as complete and attaches full error info.
+ *
+ * This is where the actual work is done, the related methods all end up here.
+ *
+ * @param aResultCode   Operation result (error) code, must not be S_OK.
+ * @param aErrorInfo            List of arguments for the format string.
+ */
+HRESULT Progress::i_notifyCompleteEI(HRESULT aResultCode, const ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
+{
+    LogThisFunc(("aResultCode=%d\n", aResultCode));
+    /* on failure we expect error info, on success there must be none */
+    AssertMsg(FAILED(aResultCode) ^ aErrorInfo.isNull(),
+              ("No error info but trying to set a failed result (%08X)!\n",
+               aResultCode));
+
+    AutoCaller autoCaller(this);
+    AssertComRCReturnRC(autoCaller.rc());
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    AssertReturn(mCompleted == FALSE, E_FAIL);
+
+    if (mCanceled && SUCCEEDED(aResultCode))
+        aResultCode = E_FAIL;
+
+    mCompleted = TRUE;
+    mResultCode = aResultCode;
+    if (SUCCEEDED(aResultCode))
+    {
+        m_ulCurrentOperation = m_cOperations - 1; /* last operation */
+        m_ulOperationPercent = 100;
+    }
+    mErrorInfo = aErrorInfo;
+
+#if !defined VBOX_COM_INPROC
+    /* remove from the global collection of pending progress operations */
+    if (mParent)
+        mParent->i_removeProgress(mId.ref());
+#endif
+
+    /* wake up all waiting threads */
+    if (mWaitersCount > 0)
+        RTSemEventMultiSignal(mCompletedSem);
+
+    return S_OK;
+}
+
+/**
+ * Notify the progress object that we're almost at the point of no return.
+ *
+ * This atomically checks for and disables cancelation.  Calls to
+ * IProgress::Cancel() made after a successful call to this method will fail
+ * and the user can be told.  While this isn't entirely clean behavior, it
+ * prevents issues with an irreversible actually operation succeeding while the
+ * user believe it was rolled back.
+ *
+ * @returns Success indicator.
+ * @retval  true on success.
+ * @retval  false if the progress object has already been canceled or is in an
+ *          invalid state
+ */
+bool Progress::i_notifyPointOfNoReturn(void)
+{
+    AutoCaller autoCaller(this);
+    AssertComRCReturn(autoCaller.rc(), false);
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (mCanceled)
+    {
+        LogThisFunc(("returns false\n"));
+        return false;
+    }
+
+    mCancelable = FALSE;
+    LogThisFunc(("returns true\n"));
+    return true;
+}
+
+/**
+ * Sets the cancelation callback, checking for cancelation first.
+ *
+ * @returns Success indicator.
+ * @retval  true on success.
+ * @retval  false if the progress object has already been canceled or is in an
+ *          invalid state
+ *
+ * @param   pfnCallback     The function to be called upon cancelation.
+ * @param   pvUser          The callback argument.
+ */
+bool Progress::i_setCancelCallback(void (*pfnCallback)(void *), void *pvUser)
+{
+    AutoCaller autoCaller(this);
+    AssertComRCReturn(autoCaller.rc(), false);
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    i_checkForAutomaticTimeout();
+    if (mCanceled)
+        return false;
+
+    m_pvCancelUserArg   = pvUser;
+    m_pfnCancelCallback = pfnCallback;
+    return true;
+}
+
+
 // IProgress properties
 /////////////////////////////////////////////////////////////////////////////
 
+HRESULT Progress::getId(com::Guid &aId)
+{
+    /* mId is constant during life time, no need to lock */
+    aId = mId;
+
+    return S_OK;
+}
+
+HRESULT Progress::getDescription(com::Utf8Str &aDescription)
+{
+    /* mDescription is constant during life time, no need to lock */
+    aDescription = mDescription;
+
+    return S_OK;
+}
+HRESULT Progress::getInitiator(ComPtr<IUnknown> &aInitiator)
+{
+    /* mInitiator/mParent are constant during life time, no need to lock */
+#if !defined(VBOX_COM_INPROC)
+    if (mInitiator)
+        mInitiator.queryInterfaceTo(aInitiator.asOutParam());
+    else
+    {
+        ComObjPtr<VirtualBox> pVirtualBox(mParent);
+        pVirtualBox.queryInterfaceTo(aInitiator.asOutParam());
+    }
+#else
+    mInitiator.queryInterfaceTo(aInitiator.asOutParam());
+#endif
+
+    return S_OK;
+}
+
+HRESULT Progress::getCancelable(BOOL *aCancelable)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aCancelable = mCancelable;
+
+    return S_OK;
+}
+
+HRESULT Progress::getPercent(ULONG *aPercent)
+{
+    /* i_checkForAutomaticTimeout requires a write lock. */
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (mCompleted && SUCCEEDED(mResultCode))
+        *aPercent = 100;
+    else
+    {
+        ULONG ulPercent = (ULONG)i_calcTotalPercent();
+        // do not report 100% until we're really really done with everything
+        // as the Qt GUI dismisses progress dialogs in that case
+        if (    ulPercent == 100
+             && (    m_ulOperationPercent < 100
+                  || (m_ulCurrentOperation < m_cOperations -1)
+                )
+           )
+            *aPercent = 99;
+        else
+            *aPercent = ulPercent;
+    }
+
+    i_checkForAutomaticTimeout();
+
+    return S_OK;
+}
+
+HRESULT Progress::getTimeRemaining(LONG *aTimeRemaining)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (mCompleted)
+        *aTimeRemaining = 0;
+    else
+    {
+        double dPercentDone = i_calcTotalPercent();
+        if (dPercentDone < 1)
+            *aTimeRemaining = -1;       // unreliable, or avoid division by 0 below
+        else
+        {
+            uint64_t ullTimeNow = RTTimeMilliTS();
+            uint64_t ullTimeElapsed = ullTimeNow - m_ullTimestamp;
+            uint64_t ullTimeTotal = (uint64_t)(ullTimeElapsed * 100 / dPercentDone);
+            uint64_t ullTimeRemaining = ullTimeTotal - ullTimeElapsed;
+
+//          LogFunc(("dPercentDone = %RI32, ullTimeNow = %RI64, ullTimeElapsed = %RI64, ullTimeTotal = %RI64, ullTimeRemaining = %RI64\n",
+//                   (uint32_t)dPercentDone, ullTimeNow, ullTimeElapsed, ullTimeTotal, ullTimeRemaining));
+
+            *aTimeRemaining = (LONG)(ullTimeRemaining / 1000);
+        }
+    }
+
+    return S_OK;
+}
+
+HRESULT Progress::getCompleted(BOOL *aCompleted)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aCompleted = mCompleted;
+
+    return S_OK;
+}
+
+HRESULT Progress::getCanceled(BOOL *aCanceled)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aCanceled = mCanceled;
+
+    return S_OK;
+}
+
+HRESULT Progress::getResultCode(LONG *aResultCode)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (!mCompleted)
+        return setError(E_FAIL,
+                        tr("Result code is not available, operation is still in progress"));
+
+    *aResultCode = mResultCode;
+
+    return S_OK;
+}
+
+HRESULT Progress::getErrorInfo(ComPtr<IVirtualBoxErrorInfo> &aErrorInfo)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (!mCompleted)
+        return setError(E_FAIL,
+                        tr("Error info is not available, operation is still in progress"));
+
+    mErrorInfo.queryInterfaceTo(aErrorInfo.asOutParam());
+
+    return S_OK;
+}
+
+HRESULT Progress::getOperationCount(ULONG *aOperationCount)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aOperationCount = m_cOperations;
+
+    return S_OK;
+}
+
+HRESULT Progress::getOperation(ULONG *aOperation)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aOperation = m_ulCurrentOperation;
+
+    return S_OK;
+}
+
+HRESULT Progress::getOperationDescription(com::Utf8Str &aOperationDescription)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    aOperationDescription = m_operationDescription;
+
+    return S_OK;
+}
+
+HRESULT Progress::getOperationPercent(ULONG *aOperationPercent)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (mCompleted && SUCCEEDED(mResultCode))
+        *aOperationPercent = 100;
+    else
+        *aOperationPercent = m_ulOperationPercent;
+
+    return S_OK;
+}
+
+HRESULT Progress::getOperationWeight(ULONG *aOperationWeight)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aOperationWeight = m_ulCurrentOperationWeight;
+
+    return S_OK;
+}
+
+HRESULT Progress::getTimeout(ULONG *aTimeout)
+{
+    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    *aTimeout = m_cMsTimeout;
+
+    return S_OK;
+}
+
+HRESULT Progress::setTimeout(ULONG aTimeout)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (!mCancelable)
+        return setError(VBOX_E_INVALID_OBJECT_STATE,
+                        tr("Operation cannot be canceled"));
+    m_cMsTimeout = aTimeout;
+
+    return S_OK;
+}
+
+
 // IProgress methods
 /////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Updates the percentage value of the current operation.
+ *
+ * @param aPercent  New percentage value of the operation in progress
+ *                  (in range [0, 100]).
+ */
+HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
+{
+    AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
+
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    i_checkForAutomaticTimeout();
+    if (mCancelable && mCanceled)
+        AssertReturn(!mCompleted, E_FAIL);
+    AssertReturn(!mCompleted && !mCanceled, E_FAIL);
+
+    m_ulOperationPercent = aPercent;
+
+    return S_OK;
+}
+
+/**
+ * Signals that the current operation is successfully completed and advances to
+ * the next operation. The operation percentage is reset to 0.
+ *
+ * @param aOperationDescription     Description of the next operation.
+ *
+ * @note The current operation must not be the last one.
+ */
+HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
+{
+    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+    if (mCanceled)
+        return E_FAIL;
+    AssertReturn(!mCompleted, E_FAIL);
+    AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
+
+    ++m_ulCurrentOperation;
+    m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
+
+    m_operationDescription = aNextOperationDescription;
+    m_ulCurrentOperationWeight = aNextOperationsWeight;
+    m_ulOperationPercent = 0;
+
+    LogThisFunc(("%s: aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
+                 m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
+
+    /* wake up all waiting threads */
+    if (mWaitersCount > 0)
+        RTSemEventMultiSignal(mCompletedSem);
+
+    return S_OK;
+}
 
 /**
@@ -854,284 +1035,44 @@
 }
 
-/**
- * Updates the percentage value of the current operation.
- *
- * @param aPercent  New percentage value of the operation in progress
- *                  (in range [0, 100]).
- */
-HRESULT Progress::setCurrentOperationProgress(ULONG aPercent)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    AssertMsgReturn(aPercent <= 100, ("%u\n", aPercent), E_INVALIDARG);
-
-    i_checkForAutomaticTimeout();
-    if (mCancelable && mCanceled)
-    {
-        Assert(!mCompleted);
-        return E_FAIL;
-    }
-    AssertReturn(!mCompleted && !mCanceled, E_FAIL);
-
-    m_ulOperationPercent = aPercent;
-
-    return S_OK;
-}
-
-/**
- * Signals that the current operation is successfully completed and advances to
- * the next operation. The operation percentage is reset to 0.
- *
- * @param aOperationDescription     Description of the next operation.
- *
- * @note The current operation must not be the last one.
- */
-HRESULT Progress::setNextOperation(const com::Utf8Str &aNextOperationDescription, ULONG aNextOperationsWeight)
-
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (mCanceled)
-        return E_FAIL;
-    AssertReturn(!mCompleted, E_FAIL);
-    AssertReturn(m_ulCurrentOperation + 1 < m_cOperations, E_FAIL);
-
-    ++m_ulCurrentOperation;
-    m_ulOperationsCompletedWeight += m_ulCurrentOperationWeight;
-
-    m_operationDescription = aNextOperationDescription;
-    m_ulCurrentOperationWeight = aNextOperationsWeight;
-    m_ulOperationPercent = 0;
-
-    Log(("Progress::setNextOperation(%s): aNextOperationsWeight = %d; m_ulCurrentOperation is now %d, m_ulOperationsCompletedWeight is now %d\n",
-         m_operationDescription.c_str(), aNextOperationsWeight, m_ulCurrentOperation, m_ulOperationsCompletedWeight));
-
-    /* wake up all waiting threads */
-    if (mWaitersCount > 0)
-        RTSemEventMultiSignal(mCompletedSem);
-
-    return S_OK;
-}
-
-// public methods only for internal purposes
+
+// private internal helpers
 /////////////////////////////////////////////////////////////////////////////
 
 /**
- * Sets the internal result code and attempts to retrieve additional error
- * info from the current thread. Gets called from Progress::notifyComplete(),
- * but can be called again to override a previous result set with
- * notifyComplete().
- *
- * @param aResultCode
- */
-HRESULT Progress::i_setResultCode(HRESULT aResultCode)
-{
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    mResultCode = aResultCode;
-
-    HRESULT rc = S_OK;
-
-    if (FAILED(aResultCode))
-    {
-        /* try to import error info from the current thread */
-
-#if !defined(VBOX_WITH_XPCOM)
-
-        ComPtr<IErrorInfo> err;
-        rc = ::GetErrorInfo(0, err.asOutParam());
-        if (rc == S_OK && err)
-        {
-            rc = err.queryInterfaceTo(mErrorInfo.asOutParam());
-            if (SUCCEEDED(rc) && !mErrorInfo)
-                rc = E_FAIL;
-        }
-
-#else /* !defined(VBOX_WITH_XPCOM) */
-
-        nsCOMPtr<nsIExceptionService> es;
-        es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &rc);
-        if (NS_SUCCEEDED(rc))
-        {
-            nsCOMPtr <nsIExceptionManager> em;
-            rc = es->GetCurrentExceptionManager(getter_AddRefs(em));
-            if (NS_SUCCEEDED(rc))
-            {
-                ComPtr<nsIException> ex;
-                rc = em->GetCurrentException(ex.asOutParam());
-                if (NS_SUCCEEDED(rc) && ex)
-                {
-                    rc = ex.queryInterfaceTo(mErrorInfo.asOutParam());
-                    if (NS_SUCCEEDED(rc) && !mErrorInfo)
-                        rc = E_FAIL;
-                }
-            }
-        }
-#endif /* !defined(VBOX_WITH_XPCOM) */
-
-        AssertMsg(rc == S_OK, ("Couldn't get error info (rc=%08X) while trying to set a failed result (%08X)!\n",
-                               rc, aResultCode));
-    }
-
-    return rc;
-}
-
-/**
- * Marks the whole task as complete and sets the result code.
- *
- * If the result code indicates a failure (|FAILED(@a aResultCode)|) then this
- * method will import the error info from the current thread and assign it to
- * the errorInfo attribute (it will return an error if no info is available in
- * such case).
- *
- * If the result code indicates a success (|SUCCEEDED(@a aResultCode)|) then
- * the current operation is set to the last.
- *
- * Note that this method may be called only once for the given Progress object.
- * Subsequent calls will assert.
- *
- * @param aResultCode   Operation result code.
- */
-HRESULT Progress::i_notifyComplete(HRESULT aResultCode)
-{
-    AutoCaller autoCaller(this);
-    AssertComRCReturnRC(autoCaller.rc());
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    AssertReturn(mCompleted == FALSE, E_FAIL);
-
-    LogFunc(("aResultCode=%d\n", aResultCode));
-
-    if (mCanceled && SUCCEEDED(aResultCode))
-        aResultCode = E_FAIL;
-
-    HRESULT rc = i_setResultCode(aResultCode);
-
-    mCompleted = TRUE;
-
-    if (!FAILED(aResultCode))
-    {
-        m_ulCurrentOperation = m_cOperations - 1; /* last operation */
-        m_ulOperationPercent = 100;
-    }
-
-#if !defined VBOX_COM_INPROC
-    /* remove from the global collection of pending progress operations */
-    if (mParent)
-        mParent->i_removeProgress(mId.ref());
-#endif
-
-    /* wake up all waiting threads */
-    if (mWaitersCount > 0)
-        RTSemEventMultiSignal(mCompletedSem);
-
-    return rc;
-}
-
-/**
- * Wrapper around Progress:notifyCompleteV.
- */
-HRESULT Progress::i_notifyComplete(HRESULT aResultCode,
-                                   const GUID &aIID,
-                                   const char *pcszComponent,
-                                   const char *aText,
-                                   ...)
-{
-    va_list va;
-    va_start(va, aText);
-    HRESULT hrc = i_notifyCompleteV(aResultCode, aIID, pcszComponent, aText, va);
-    va_end(va);
-    return hrc;
-}
-
-/**
- * Marks the operation as complete and attaches full error info.
- *
- * See VirtualBoxBase::setError(HRESULT, const GUID &, const wchar_t
- * *, const char *, ...) for more info.
- *
- * @param aResultCode   Operation result (error) code, must not be S_OK.
- * @param aIID          IID of the interface that defines the error.
- * @param aComponent    Name of the component that generates the error.
- * @param aText         Error message (must not be null), an RTStrPrintf-like
- *                      format string in UTF-8 encoding.
- * @param va            List of arguments for the format string.
- */
-HRESULT Progress::i_notifyCompleteV(HRESULT aResultCode,
-                                    const GUID &aIID,
-                                    const char *pcszComponent,
-                                    const char *aText,
-                                    va_list va)
-{
-    Utf8Str text(aText, va);
-
-    AutoCaller autoCaller(this);
-    AssertComRCReturnRC(autoCaller.rc());
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    AssertReturn(mCompleted == FALSE, E_FAIL);
-
-    if (mCanceled && SUCCEEDED(aResultCode))
-        aResultCode = E_FAIL;
-
-    mCompleted = TRUE;
-    mResultCode = aResultCode;
-
-    AssertReturn(FAILED(aResultCode), E_FAIL);
-
-    ComObjPtr<VirtualBoxErrorInfo> errorInfo;
-    HRESULT rc = errorInfo.createObject();
-    AssertComRC(rc);
-    if (SUCCEEDED(rc))
-    {
-        errorInfo->init(aResultCode, aIID, pcszComponent, text);
-        errorInfo.queryInterfaceTo(mErrorInfo.asOutParam());
-    }
-
-#if !defined VBOX_COM_INPROC
-    /* remove from the global collection of pending progress operations */
-    if (mParent)
-        mParent->i_removeProgress(mId.ref());
-#endif
-
-    /* wake up all waiting threads */
-    if (mWaitersCount > 0)
-        RTSemEventMultiSignal(mCompletedSem);
-
-    return rc;
-}
-
-/**
- * Notify the progress object that we're almost at the point of no return.
- *
- * This atomically checks for and disables cancelation.  Calls to
- * IProgress::Cancel() made after a successful call to this method will fail
- * and the user can be told.  While this isn't entirely clean behavior, it
- * prevents issues with an irreversible actually operation succeeding while the
- * user believe it was rolled back.
- *
- * @returns Success indicator.
- * @retval  true on success.
- * @retval  false if the progress object has already been canceled or is in an
- *          invalid state
- */
-bool Progress::i_notifyPointOfNoReturn(void)
-{
-    AutoCaller autoCaller(this);
-    AssertComRCReturn(autoCaller.rc(), false);
-
-    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
-
-    if (mCanceled)
-    {
-        LogThisFunc(("returns false\n"));
-        return false;
-    }
-
-    mCancelable = FALSE;
-    LogThisFunc(("returns true\n"));
-    return true;
-}
-
+ * Internal helper to compute the total percent value based on the member values and
+ * returns it as a "double". This is used both by GetPercent (which returns it as a
+ * rounded ULONG) and GetTimeRemaining().
+ *
+ * Requires locking by the caller!
+ *
+ * @return fractional percentage as a double value.
+ */
+double Progress::i_calcTotalPercent()
+{
+    // avoid division by zero
+    if (m_ulTotalOperationsWeight == 0)
+        return 0.0;
+
+    double dPercent = (    (double)m_ulOperationsCompletedWeight  // weight of operations that have been completed
+                         + ((double)m_ulOperationPercent *
+                            (double)m_ulCurrentOperationWeight / 100.0)  // plus partial weight of the current operation
+                      ) * 100.0 / (double)m_ulTotalOperationsWeight;
+
+    return dPercent;
+}
+
+/**
+ * Internal helper for automatically timing out the operation.
+ *
+ * The caller must hold the object write lock.
+ */
+void Progress::i_checkForAutomaticTimeout(void)
+{
+    AssertReturnVoid(isWriteLockOnCurrentThread());
+
+    if (   m_cMsTimeout
+        && mCancelable
+        && !mCanceled
+        && RTTimeMilliTS() - m_ullTimestamp > m_cMsTimeout)
+        Cancel();
+}
