bug 983747 - Add 'download' method to Browser API r=kanru,paolo

This commit is contained in:
Ghislain 'Aus' Lacroix 2014-06-09 08:34:54 -07:00
parent 8a53a70162
commit 6ff9f6b846
8 changed files with 186 additions and 2 deletions

View File

@ -135,6 +135,7 @@ function BrowserElementParent(frameLoader, hasRemoteFrame, isPendingFrame) {
defineNoReturnMethod('goForward', this._goForward);
defineNoReturnMethod('reload', this._reload);
defineNoReturnMethod('stop', this._stop);
defineMethod('download', this._download);
defineDOMRequestMethod('purgeHistory', 'purge-history');
defineMethod('getScreenshot', this._getScreenshot);
defineMethod('addNextPaintListener', this._addNextPaintListener);
@ -620,6 +621,106 @@ BrowserElementParent.prototype = {
this._sendAsyncMsg('stop');
},
_download: function(_url, _options) {
let ioService =
Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
let uri = ioService.newURI(_url, null, null);
let url = uri.QueryInterface(Ci.nsIURL);
// Ensure we have _options, we always use it to send the filename.
_options = _options || {};
if (!_options.filename) {
_options.filename = url.fileName;
}
debug('_options = ' + uneval(_options));
// Ensure we have a filename.
if (!_options.filename) {
throw Components.Exception("Invalid argument", Cr.NS_ERROR_INVALID_ARG);
}
let interfaceRequestor =
this._frameLoader.loadContext.QueryInterface(Ci.nsIInterfaceRequestor);
let req = Services.DOMRequest.createRequest(this._window);
function DownloadListener() {
debug('DownloadListener Constructor');
}
DownloadListener.prototype = {
extListener: null,
onStartRequest: function(aRequest, aContext) {
debug('DownloadListener - onStartRequest');
let extHelperAppSvc =
Cc['@mozilla.org/uriloader/external-helper-app-service;1'].
getService(Ci.nsIExternalHelperAppService);
let channel = aRequest.QueryInterface(Ci.nsIChannel);
this.extListener =
extHelperAppSvc.doContent(
channel.contentType,
aRequest,
interfaceRequestor,
true);
this.extListener.onStartRequest(aRequest, aContext);
},
onStopRequest: function(aRequest, aContext, aStatusCode) {
debug('DownloadListener - onStopRequest (aStatusCode = ' +
aStatusCode + ')');
if (aStatusCode == Cr.NS_OK) {
// Everything looks great.
debug('DownloadListener - Download Successful.');
this.services.DOMRequest.fireSuccess(this.req, aStatusCode);
}
else {
// In case of failure, we'll simply return the failure status code.
debug('DownloadListener - Download Failed!');
this.services.DOMRequest.fireError(this.req, aStatusCode);
}
if (this.extListener) {
this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
}
},
onDataAvailable: function(aRequest, aContext, aInputStream,
aOffset, aCount) {
this.extListener.onDataAvailable(aRequest, aContext, aInputStream,
aOffset, aCount);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIStreamListener,
Ci.nsIRequestObserver])
};
let channel = ioService.newChannelFromURI(url);
// XXX We would set private browsing information prior to calling this.
channel.notificationCallbacks = interfaceRequestor;
// Since we're downloading our own local copy we'll want to bypass the
// cache and local cache if the channel let's us specify this.
let flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS |
Ci.nsIChannel.LOAD_BYPASS_CACHE;
if (channel instanceof Ci.nsICachingChannel) {
debug('This is a caching channel. Forcing bypass.');
flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
}
channel.loadFlags |= flags;
if (channel instanceof Ci.nsIHttpChannel) {
debug('Setting HTTP referrer = ' + this._window.document.documentURIObject);
channel.referrer = this._window.document.documentURIObject;
if (channel instanceof Ci.nsIHttpChannelInternal) {
channel.forceAllowThirdPartyCookie = true;
}
}
// Set-up complete, let's get things started.
channel.asyncOpen(new DownloadListener(), null);
return req;
},
_getScreenshot: function(_width, _height, _mimeType) {
let width = parseInt(_width);
let height = parseInt(_height);

View File

@ -0,0 +1,37 @@
/* Any copyright is dedicated to the public domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Bug 983747 - Test 'download' method on iframe.
"use strict";
SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
var iframe;
var downloadURL = 'http://test/tests/dom/browser-element/mochitest/file_download_bin.sjs';
function runTest() {
iframe = document.createElement('iframe');
SpecialPowers.wrap(iframe).mozbrowser = true;
iframe.addEventListener('mozbrowserloadend', loadend);
iframe.src = 'data:text/html,<html><body>hello</body></html>';
iframe.setAttribute('remote', 'true');
document.body.appendChild(iframe);
}
function loadend() {
var req = iframe.download(downloadURL, { filename: 'test.bin' });
req.onsuccess = function() {
ok(true, 'Download finished as expected.');
SimpleTest.finish();
}
req.onerror = function() {
ok(false, 'Expected no error, got ' + req.error);
}
}
addEventListener('testready', runTest);

View File

@ -0,0 +1,4 @@
function handleRequest(request, response) {
response.setHeader("Content-Type", "application/octet-stream", false);
response.write("BIN");
}

View File

@ -30,6 +30,8 @@ skip-if = (toolkit == 'gonk' && !debug)
[test_browserElement_oop_DOMRequestError.html]
[test_browserElement_oop_DataURI.html]
[test_browserElement_oop_DocumentFirstPaint.html]
[test_browserElement_oop_Download.html]
disabled = bug 1022281
[test_browserElement_oop_ErrorSecurity.html]
skip-if = (toolkit == 'gonk' && !debug)
[test_browserElement_oop_FirstPaint.html]

View File

@ -21,6 +21,7 @@ support-files =
browserElement_DOMRequestError.js
browserElement_DataURI.js
browserElement_DocumentFirstPaint.js
browserElement_Download.js
browserElement_ErrorSecurity.js
browserElement_ExposableURI.js
browserElement_FirstPaint.js
@ -96,6 +97,7 @@ support-files =
file_browserElement_XFrameOptionsSameOrigin.html
file_bug709759.sjs
file_bug741717.sjs
file_download_bin.sjs
file_empty.html
file_empty_script.js
file_focus.html
@ -134,6 +136,8 @@ skip-if = buildapp == 'b2g'
[test_browserElement_inproc_DOMRequestError.html]
[test_browserElement_inproc_DataURI.html]
[test_browserElement_inproc_DocumentFirstPaint.html]
[test_browserElement_inproc_Download.html]
disabled = bug 1022281
[test_browserElement_inproc_ExposableURI.html]
[test_browserElement_inproc_FirstPaint.html]
[test_browserElement_inproc_ForwardName.html]

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 983747</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_Download.js">
</script>
</body>
</html>

View File

@ -0,0 +1,13 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Bug 983747</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="application/javascript;version=1.7" src="browserElement_Download.js">
</script>
</body>
</html>

View File

@ -1391,8 +1391,18 @@ this.DownloadSaver.prototype = {
// The start time is always available when we reach this point.
let startPRTime = this.download.startTime.getTime() * 1000;
gDownloadHistory.addDownload(sourceUri, referrerUri, startPRTime,
targetUri);
try {
gDownloadHistory.addDownload(sourceUri, referrerUri, startPRTime,
targetUri);
}
catch(ex if ex instanceof Components.Exception &&
ex.result == Cr.NS_ERROR_NOT_AVAILABLE) {
//
// Under normal operation the download history service may not
// be available. We don't want all downloads that are public to fail
// when this happens so we'll ignore this error and this error only!
//
}
},
/**