fix lifetime of downloadItem class when defaultdownload canceled
This commit is contained in:
parent
045e42a10c
commit
2819af9586
6 changed files with 100 additions and 27 deletions
|
@ -12,6 +12,7 @@
|
||||||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||||
#include "atom/common/node_includes.h"
|
#include "atom/common/node_includes.h"
|
||||||
#include "base/memory/linked_ptr.h"
|
#include "base/memory/linked_ptr.h"
|
||||||
|
#include "base/message_loop/message_loop.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "native_mate/dictionary.h"
|
#include "native_mate/dictionary.h"
|
||||||
#include "net/base/filename_util.h"
|
#include "net/base/filename_util.h"
|
||||||
|
@ -70,8 +71,17 @@ DownloadItem::DownloadItem(content::DownloadItem* download_item) :
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadItem::~DownloadItem() {
|
DownloadItem::~DownloadItem() {
|
||||||
if (download_item_)
|
Destroy(download_item_);
|
||||||
OnDownloadDestroyed(download_item_);
|
}
|
||||||
|
|
||||||
|
void DownloadItem::Destroy(content::DownloadItem* item) {
|
||||||
|
if (item) {
|
||||||
|
OnDownloadDestroyed(item);
|
||||||
|
MarkDestroyed();
|
||||||
|
|
||||||
|
// Destroy the native class in next tick.
|
||||||
|
base::MessageLoop::current()->PostTask(FROM_HERE, GetDestroyClosure());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
|
void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
|
||||||
|
@ -79,8 +89,8 @@ void DownloadItem::OnDownloadUpdated(content::DownloadItem* item) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download_item) {
|
void DownloadItem::OnDownloadDestroyed(content::DownloadItem* download_item) {
|
||||||
download_item_->RemoveObserver(this);
|
download_item->RemoveObserver(this);
|
||||||
auto iter = g_download_item_objects.find(download_item_->GetId());
|
auto iter = g_download_item_objects.find(download_item->GetId());
|
||||||
if (iter != g_download_item_objects.end())
|
if (iter != g_download_item_objects.end())
|
||||||
g_download_item_objects.erase(iter);
|
g_download_item_objects.erase(iter);
|
||||||
download_item_ = nullptr;
|
download_item_ = nullptr;
|
||||||
|
|
|
@ -36,6 +36,8 @@ class DownloadItem : public mate::TrackableObject<DownloadItem>,
|
||||||
static void BuildPrototype(v8::Isolate* isolate,
|
static void BuildPrototype(v8::Isolate* isolate,
|
||||||
v8::Local<v8::ObjectTemplate> prototype);
|
v8::Local<v8::ObjectTemplate> prototype);
|
||||||
|
|
||||||
|
void Destroy(content::DownloadItem* download_item);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
explicit DownloadItem(content::DownloadItem* download_item);
|
explicit DownloadItem(content::DownloadItem* download_item);
|
||||||
~DownloadItem();
|
~DownloadItem();
|
||||||
|
|
|
@ -292,12 +292,16 @@ void Session::OnDownloadCreated(content::DownloadManager* manager,
|
||||||
auto web_contents = item->GetWebContents();
|
auto web_contents = item->GetWebContents();
|
||||||
if (SavePageHandler::IsSavePageTypes(item->GetMimeType()))
|
if (SavePageHandler::IsSavePageTypes(item->GetMimeType()))
|
||||||
return;
|
return;
|
||||||
|
auto download_item_handle = DownloadItem::Create(isolate(), item);
|
||||||
bool prevent_default = Emit(
|
bool prevent_default = Emit(
|
||||||
"will-download",
|
"will-download",
|
||||||
DownloadItem::Create(isolate(), item),
|
download_item_handle,
|
||||||
api::WebContents::CreateFrom(isolate(), web_contents));
|
api::WebContents::CreateFrom(isolate(), web_contents));
|
||||||
if (prevent_default)
|
if (prevent_default) {
|
||||||
item->Cancel(true);
|
item->Cancel(true);
|
||||||
|
download_item_handle->Destroy(item);
|
||||||
|
item->Remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
||||||
|
|
|
@ -60,7 +60,8 @@ The following events are available on instances of `Session`:
|
||||||
|
|
||||||
Emitted when Electron is about to download `item` in `webContents`.
|
Emitted when Electron is about to download `item` in `webContents`.
|
||||||
|
|
||||||
Calling `event.preventDefault()` will cancel the download.
|
Calling `event.preventDefault()` will cancel the download and `item` will not be
|
||||||
|
available from next tick of the process.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
session.defaultSession.on('will-download', function(event, item, webContents) {
|
session.defaultSession.on('will-download', function(event, item, webContents) {
|
||||||
|
|
|
@ -144,6 +144,50 @@ describe('session module', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('session will-download event', function() {
|
||||||
|
var w = null;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
width: 400,
|
||||||
|
height: 400
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
return w.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can cancel default download behavior', function(done) {
|
||||||
|
const mockFile = new Buffer(1024);
|
||||||
|
const contentDisposition = 'inline; filename="mockFile.txt"';
|
||||||
|
const downloadServer = http.createServer(function(req, res) {
|
||||||
|
res.writeHead(200, {
|
||||||
|
'Content-Length': mockFile.length,
|
||||||
|
'Content-Type': 'application/plain',
|
||||||
|
'Content-Disposition': contentDisposition
|
||||||
|
});
|
||||||
|
res.end(mockFile);
|
||||||
|
downloadServer.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
downloadServer.listen(0, '127.0.0.1', function() {
|
||||||
|
const port = downloadServer.address().port;
|
||||||
|
const url = "http://127.0.0.1:" + port + '/';
|
||||||
|
|
||||||
|
ipcRenderer.sendSync('set-download-option', false, true);
|
||||||
|
w.loadURL(url);
|
||||||
|
ipcRenderer.once('download-error', function(event, downloadUrl, filename, error) {
|
||||||
|
assert.equal(downloadUrl, url);
|
||||||
|
assert.equal(filename, 'mockFile.txt');
|
||||||
|
assert.equal(error, 'Object has been destroyed');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return describe('DownloadItem', function() {
|
return describe('DownloadItem', function() {
|
||||||
var assertDownload, contentDisposition, downloadFilePath, downloadServer, mockPDF;
|
var assertDownload, contentDisposition, downloadFilePath, downloadServer, mockPDF;
|
||||||
mockPDF = new Buffer(1024 * 1024 * 5);
|
mockPDF = new Buffer(1024 * 1024 * 5);
|
||||||
|
@ -173,7 +217,7 @@ describe('session module', function() {
|
||||||
return downloadServer.listen(0, '127.0.0.1', function() {
|
return downloadServer.listen(0, '127.0.0.1', function() {
|
||||||
var port;
|
var port;
|
||||||
port = downloadServer.address().port;
|
port = downloadServer.address().port;
|
||||||
ipcRenderer.sendSync('set-download-option', false);
|
ipcRenderer.sendSync('set-download-option', false, false);
|
||||||
w.loadURL(url + ":" + port);
|
w.loadURL(url + ":" + port);
|
||||||
return ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
|
return ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
|
||||||
assertDownload(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port);
|
assertDownload(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename, port);
|
||||||
|
@ -185,7 +229,7 @@ describe('session module', function() {
|
||||||
return downloadServer.listen(0, '127.0.0.1', function() {
|
return downloadServer.listen(0, '127.0.0.1', function() {
|
||||||
var port, webview;
|
var port, webview;
|
||||||
port = downloadServer.address().port;
|
port = downloadServer.address().port;
|
||||||
ipcRenderer.sendSync('set-download-option', false);
|
ipcRenderer.sendSync('set-download-option', false, false);
|
||||||
webview = new WebView;
|
webview = new WebView;
|
||||||
webview.src = "file://" + fixtures + "/api/blank.html";
|
webview.src = "file://" + fixtures + "/api/blank.html";
|
||||||
webview.addEventListener('did-finish-load', function() {
|
webview.addEventListener('did-finish-load', function() {
|
||||||
|
@ -199,11 +243,11 @@ describe('session module', function() {
|
||||||
return document.body.appendChild(webview);
|
return document.body.appendChild(webview);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return it('can cancel download', function(done) {
|
it('can cancel download', function(done) {
|
||||||
return downloadServer.listen(0, '127.0.0.1', function() {
|
return downloadServer.listen(0, '127.0.0.1', function() {
|
||||||
var port;
|
var port;
|
||||||
port = downloadServer.address().port;
|
port = downloadServer.address().port;
|
||||||
ipcRenderer.sendSync('set-download-option', true);
|
ipcRenderer.sendSync('set-download-option', true, false);
|
||||||
w.loadURL(url + ":" + port + "/");
|
w.loadURL(url + ":" + port + "/");
|
||||||
return ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
|
return ipcRenderer.once('download-done', function(event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) {
|
||||||
assert.equal(state, 'cancelled');
|
assert.equal(state, 'cancelled');
|
||||||
|
|
|
@ -102,23 +102,35 @@ app.on('ready', function() {
|
||||||
// For session's download test, listen 'will-download' event in browser, and
|
// For session's download test, listen 'will-download' event in browser, and
|
||||||
// reply the result to renderer for verifying
|
// reply the result to renderer for verifying
|
||||||
var downloadFilePath = path.join(__dirname, '..', 'fixtures', 'mock.pdf');
|
var downloadFilePath = path.join(__dirname, '..', 'fixtures', 'mock.pdf');
|
||||||
ipcMain.on('set-download-option', function(event, need_cancel) {
|
ipcMain.on('set-download-option', function(event, need_cancel, prevent_default) {
|
||||||
window.webContents.session.once('will-download',
|
window.webContents.session.once('will-download', function(e, item, webContents) {
|
||||||
function(e, item, webContents) {
|
if (prevent_default) {
|
||||||
item.setSavePath(downloadFilePath);
|
e.preventDefault();
|
||||||
item.on('done', function(e, state) {
|
const url = item.getURL();
|
||||||
window.webContents.send('download-done',
|
const filename = item.getFilename();
|
||||||
state,
|
setImmediate(function() {
|
||||||
item.getURL(),
|
try {
|
||||||
item.getMimeType(),
|
item.getURL();
|
||||||
item.getReceivedBytes(),
|
} catch(err) {
|
||||||
item.getTotalBytes(),
|
window.webContents.send('download-error', url, filename, err.message);
|
||||||
item.getContentDisposition(),
|
}
|
||||||
item.getFilename());
|
|
||||||
});
|
|
||||||
if (need_cancel)
|
|
||||||
item.cancel();
|
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
item.setSavePath(downloadFilePath);
|
||||||
|
item.on('done', function(e, state) {
|
||||||
|
window.webContents.send('download-done',
|
||||||
|
state,
|
||||||
|
item.getURL(),
|
||||||
|
item.getMimeType(),
|
||||||
|
item.getReceivedBytes(),
|
||||||
|
item.getTotalBytes(),
|
||||||
|
item.getContentDisposition(),
|
||||||
|
item.getFilename());
|
||||||
|
});
|
||||||
|
if (need_cancel)
|
||||||
|
item.cancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
event.returnValue = "done";
|
event.returnValue = "done";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue