mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
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:
parent
ec3ff32ae8
commit
0b5f01f577
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
BIN
toolkit/components/downloads/test/schema_migration/v3.sqlite
Normal file
BIN
toolkit/components/downloads/test/schema_migration/v3.sqlite
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user