Bug 377243 - [SoC] Implement Download Resume. Patch by Brahmana <om.brahmana@gmail.com> and Shawn Wilsher <me@shawnwilsher.com>. r=sdwilsh, r=mconnor, a=[wanted-firefox3]

This commit is contained in:
sdwilsh@shawnwilsher.com 2007-09-05 21:59:34 -07:00
parent ec3ff32ae8
commit 0b5f01f577
4 changed files with 262 additions and 11 deletions

View File

@ -23,6 +23,7 @@
* Blake Ross <blaker@netscape.com> (Original Author)
* Ben Goodger <ben@netscape.com> (Original Author)
* Shawn Wilsher <me@shawnwilsher.com>
* Srirang G Doddihal <brahmana@doddihal.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -71,6 +72,7 @@
#include "nsIHttpChannel.h"
#include "nsIDownloadManagerUI.h"
#include "nsTArray.h"
#include "nsIResumableChannel.h"
#ifdef XP_WIN
#include <shlobj.h>
@ -89,7 +91,7 @@ static PRBool gStoppingDownloads = PR_FALSE;
static const PRInt64 gUpdateInterval = 400 * PR_USEC_PER_MSEC;
#define DM_SCHEMA_VERSION 3
#define DM_SCHEMA_VERSION 4
#define DM_DB_NAME NS_LITERAL_STRING("downloads.sqlite")
#define DM_DB_CORRUPT_FILENAME NS_LITERAL_STRING("downloads.sqlite.corrupt")
@ -147,10 +149,66 @@ nsDownloadManager::CompleteDownload(nsDownload *aDownload)
{
// we've stopped, so break the cycle we created at download start
aDownload->mCancelable = nsnull;
aDownload->mEntityID.Truncate();
if (aDownload->mWasResumed)
(void)ExecuteDesiredAction(aDownload);
(void)mCurrentDownloads.RemoveObject(aDownload);
}
nsresult
nsDownloadManager::ExecuteDesiredAction(nsDownload *aDownload)
{
// If we have a temp file and we have resumed, we have to do what the external
// helper app service would have done.
if (!aDownload->mTempFile && !aDownload->mWasResumed)
return NS_OK;
// Find out if it was a SaveToDisk kind of a download
nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
nsresult rv;
if (aDownload->mMIMEInfo) {
rv = aDownload->mMIMEInfo->GetPreferredAction(&action);
NS_ENSURE_SUCCESS(rv, rv);
}
switch (action) {
case nsIMIMEInfo::saveToDisk:
// For this instance, we need to move the file to the proper location
{
nsCOMPtr<nsILocalFile> target;
rv = aDownload->GetTargetFile(getter_AddRefs(target));
NS_ENSURE_SUCCESS(rv, rv);
// MoveTo will fail if the file already exists, but we've already
// obtained confirmation from the user that this is OK. So, we have
// to remove it if it exists.
PRBool fileExists;
if (NS_SUCCEEDED(target->Exists(&fileExists)) && fileExists) {
rv = target->Remove(PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
}
// extract the new leaf name from the file location
nsAutoString fileName;
rv = target->GetLeafName(fileName);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> dir;
rv = target->GetParent(getter_AddRefs(dir));
NS_ENSURE_SUCCESS(rv, rv);
if (dir) {
rv = aDownload->mTempFile->MoveTo(dir, fileName);
NS_ENSURE_SUCCESS(rv, rv);
}
}
break;
default:
break;
}
return NS_OK;
}
nsresult
nsDownloadManager::InitDB(PRBool *aDoImport)
{
@ -277,6 +335,20 @@ nsDownloadManager::InitDB(PRBool *aDoImport)
}
// Fallthrough to the next upgrade
case 3: // This version adds a column to the database (entityID)
{
rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
"ALTER TABLE moz_downloads "
"ADD COLUMN entityID TEXT"));
NS_ENSURE_SUCCESS(rv, rv);
// Finally, update the schemaVersion variable and the database schema
schemaVersion = 4;
rv = mDBConn->SetSchemaVersion(schemaVersion);
NS_ENSURE_SUCCESS(rv, rv);
}
// Fallthrough to the next upgrade
case DM_SCHEMA_VERSION:
break;
@ -303,7 +375,8 @@ nsDownloadManager::InitDB(PRBool *aDoImport)
{
nsCOMPtr<mozIStorageStatement> stmt;
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, name, source, target, startTime, endTime, state, referrer "
"SELECT id, name, source, target, startTime, endTime, state, referrer, "
"entityID "
"FROM moz_downloads"), getter_AddRefs(stmt));
if (NS_SUCCEEDED(rv))
break;
@ -344,7 +417,8 @@ nsDownloadManager::CreateTable()
"startTime INTEGER, "
"endTime INTEGER, "
"state INTEGER, "
"referrer TEXT"
"referrer TEXT, "
"entityID TEXT"
")"));
}
@ -631,8 +705,8 @@ nsDownloadManager::Init()
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"UPDATE moz_downloads "
"SET startTime = ?1, endTime = ?2, state = ?3, referrer = ?4 "
"WHERE id = ?5"), getter_AddRefs(mUpdateDownloadStatement));
"SET startTime = ?1, endTime = ?2, state = ?3, referrer = ?4, entityID = ?5 "
"WHERE id = ?6"), getter_AddRefs(mUpdateDownloadStatement));
NS_ENSURE_SUCCESS(rv, rv);
// The following three AddObserver calls must be the last lines in this function,
@ -676,7 +750,7 @@ nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
// First, let's query the database and see if it even exists
nsCOMPtr<mozIStorageStatement> stmt;
nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
"SELECT id, state, startTime, source, target, name, referrer "
"SELECT id, state, startTime, source, target, name, referrer, entityID "
"FROM moz_downloads "
"WHERE id = ?1"), getter_AddRefs(stmt));
NS_ENSURE_SUCCESS(rv, rv);
@ -742,6 +816,9 @@ nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
dl->mCurrBytes = 0;
}
rv = stmt->GetUTF8String(7, dl->mEntityID);
NS_ENSURE_SUCCESS(rv, rv);
// Addrefing and returning
NS_ADDREF(*retVal = dl);
return NS_OK;
@ -1413,6 +1490,7 @@ nsDownload::nsDownload() : mDownloadState(nsIDownloadManager::DOWNLOAD_NOTSTARTE
mStartTime(0),
mLastUpdate(PR_Now() - (PRUint32)gUpdateInterval),
mPaused(PR_FALSE),
mWasResumed(PR_FALSE),
mSpeed(0)
{
}
@ -1635,6 +1713,13 @@ nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
}
}
//Fetch the entityID
nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(aRequest));
if (resumableChannel) {
rv = resumableChannel->GetEntityID(mEntityID);
NS_ENSURE_SUCCESS(rv, rv);
}
// Update the state and the database
rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
NS_ENSURE_SUCCESS(rv, rv);
@ -1956,15 +2041,94 @@ nsDownload::PauseResume(PRBool aPause)
if (mPaused == aPause || !mRequest)
return NS_OK;
if (aPause) {
nsresult rv = mRequest->Suspend();
nsHandlerInfoAction action = nsIMIMEInfo::saveToDisk;
nsresult rv;
if (mMIMEInfo) {
rv = mMIMEInfo->GetPreferredAction(&action);
NS_ENSURE_SUCCESS(rv, rv);
}
PRBool resumable = PR_FALSE;
if (action == nsIMIMEInfo::saveToDisk && !mEntityID.IsEmpty())
resumable = PR_TRUE;
if (aPause) {
if (resumable) {
rv = mCancelable->Cancel(NS_BINDING_ABORTED);
NS_ENSURE_SUCCESS(rv, rv);
} else {
// This is for non-resumable downloads and downloads that are used with
// "Open With...".
rv = mRequest->Suspend();
NS_ENSURE_SUCCESS(rv, rv);
}
mPaused = PR_TRUE;
return SetState(nsIDownloadManager::DOWNLOAD_PAUSED);
}
nsresult rv = mRequest->Resume();
NS_ENSURE_SUCCESS(rv, rv);
if (resumable) {
mWasResumed = PR_TRUE;
nsCOMPtr<nsIWebBrowserPersist> wbp =
do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = wbp->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
NS_ENSURE_SUCCESS(rv, rv);
// Create a new channel for the source URI
nsCOMPtr<nsIChannel> channel;
nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(wbp));
rv = NS_NewChannel(getter_AddRefs(channel), mSource, nsnull, nsnull, ir);
NS_ENSURE_SUCCESS(rv, rv);
// Get the size of the temporary or target file to be used as offset.
PRInt64 fileSize;
nsCOMPtr<nsILocalFile> targetLocalFile(mTempFile);
if (!targetLocalFile) {
rv = GetTargetFile(getter_AddRefs(targetLocalFile));
NS_ENSURE_SUCCESS(rv, rv);
}
// We need to get a new nsIFile though because of caching issues with the
// file size. Cloning it takes care of this :(
nsCOMPtr<nsIFile> clone;
rv = targetLocalFile->Clone(getter_AddRefs(clone));
NS_ENSURE_SUCCESS(rv, rv);
rv = clone->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, rv);
// Set the channel to resume at the right position along with the entityID
nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(channel));
if (!resumableChannel)
return NS_ERROR_UNEXPECTED;
rv = resumableChannel->ResumeAt(fileSize, mEntityID);
NS_ENSURE_SUCCESS(rv, rv);
// Set the referrer
if (mReferrer) {
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (httpChannel) {
rv = httpChannel->SetReferrer(mReferrer);
NS_ENSURE_SUCCESS(rv, rv);
}
}
// Creates a cycle that will be broken when the download finishes
mCancelable = wbp;
(void)wbp->SetProgressListener(this);
// Save the channel using nsIWBP.
rv = wbp->SaveChannel(channel, targetLocalFile);
if (NS_FAILED(rv)) {
mCancelable = nsnull;
(void)wbp->SetProgressListener(nsnull);
return rv;
}
} else {
rv = mRequest->Resume();
NS_ENSURE_SUCCESS(rv, rv);
}
mPaused = PR_FALSE;
return SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
}
@ -2000,8 +2164,12 @@ nsDownload::UpdateDB()
}
NS_ENSURE_SUCCESS(rv, rv);
//entityID
rv = stmt->BindUTF8StringParameter(4, mEntityID);
NS_ENSURE_SUCCESS(rv, rv);
// id
rv = stmt->BindInt64Parameter(4, mID);
rv = stmt->BindInt64Parameter(5, mID);
NS_ENSURE_SUCCESS(rv, rv);
return stmt->Execute();

View File

@ -23,6 +23,7 @@
* Blake Ross <blaker@netscape.com>
* Ben Goodger <ben@netscape.com>
* Shawn Wilsher <me@shawnwilsher.com>
* Srirang G Doddihal <brahmana@doddihal.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -158,6 +159,7 @@ protected:
const PRUnichar* aDontCancelButton);
PRInt32 GetRetentionBehavior();
nsresult ExecuteDesiredAction(nsDownload *aDownload);
static PRBool IsInFinalStage(DownloadState aState)
{
@ -227,6 +229,7 @@ protected:
private:
nsString mDisplayName;
nsCString mEntityID;
nsCOMPtr<nsIURI> mSource;
nsCOMPtr<nsIURI> mReferrer;
@ -245,6 +248,7 @@ private:
PRTime mStartTime;
PRTime mLastUpdate;
PRBool mPaused;
PRBool mWasResumed;
double mSpeed;
friend class nsDownloadManager;

View File

@ -0,0 +1,79 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Download Manager Test Code.
*
* The Initial Developer of the Original Code is
* Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Shawn Wilsher <me@shawnwilsher.com> (Original Author)
* Srirang G Doddihal <brahmana@doddihal.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by declaring the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not declare
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
// This file tests migration from v3 to v4
function run_test()
{
// First import the downloads.sqlite file
importDatabaseFile("v3.sqlite");
// ok, now it is OK to init the download manager - this will perform the
// migration!
var dm = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
var dbConn = dm.DBConnection;
var stmt = null;
// check schema version
do_check_true(dbConn.schemaVersion >= 4);
// Check that the column exists (statement should not throw)
stmt = dbConn.createStatement("SELECT entityID FROM moz_downloads");
// now we check the entries
stmt = dbConn.createStatement(
"SELECT name, source, target, startTime, endTime, state, referrer, entityID " +
"FROM moz_downloads " +
"WHERE id = 27");
stmt.executeStep();
do_check_eq("Firefox 2.0.0.6.dmg", stmt.getString(0));
do_check_eq("http://ftp-mozilla.netscape.com/pub/mozilla.org/firefox/releases/2.0.0.6/mac/en-US/Firefox%202.0.0.6.dmg",
stmt.getUTF8String(1));
do_check_eq("file:///Users/sdwilsh/Desktop/Firefox%202.0.0.6.dmg",
stmt.getUTF8String(2));
do_check_eq(1187390974170783, stmt.getInt64(3));
do_check_eq(1187391001257446, stmt.getInt64(4));
do_check_eq(1, stmt.getInt32(5));
do_check_eq("http://www.mozilla.com/en-US/products/download.html?product=firefox-2.0.0.6&os=osx&lang=en-US",stmt.getUTF8String(6));
do_check_true(stmt.getIsNull(7));
stmt.reset();
cleanup();
}