mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 901097 - FileReader API in workers, r=sicking, r=nfroyd
This commit is contained in:
parent
36fe774c56
commit
ac410a8dd0
@ -6,34 +6,34 @@
|
||||
|
||||
#include "FileReader.h"
|
||||
|
||||
#include "nsContentCID.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIFile.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/FileReaderBinding.h"
|
||||
#include "mozilla/dom/ProgressEvent.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "xpcpublic.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamTransportService.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace workers;
|
||||
|
||||
#define ABORT_STR "abort"
|
||||
#define LOAD_STR "load"
|
||||
#define LOADSTART_STR "loadstart"
|
||||
@ -76,6 +76,20 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
NS_IMPL_ADDREF_INHERITED(FileReader, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(FileReader, DOMEventTargetHelper)
|
||||
|
||||
class MOZ_RAII FileReaderDecreaseBusyCounter
|
||||
{
|
||||
RefPtr<FileReader> mFileReader;
|
||||
public:
|
||||
explicit FileReaderDecreaseBusyCounter(FileReader* aFileReader)
|
||||
: mFileReader(aFileReader)
|
||||
{}
|
||||
|
||||
~FileReaderDecreaseBusyCounter()
|
||||
{
|
||||
mFileReader->DecreaseBusyCounter();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
FileReader::RootResultArrayBuffer()
|
||||
{
|
||||
@ -84,7 +98,8 @@ FileReader::RootResultArrayBuffer()
|
||||
|
||||
//FileReader constructors/initializers
|
||||
|
||||
FileReader::FileReader(nsPIDOMWindow* aWindow)
|
||||
FileReader::FileReader(nsPIDOMWindow* aWindow,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mFileData(nullptr)
|
||||
, mDataLen(0)
|
||||
@ -95,7 +110,12 @@ FileReader::FileReader(nsPIDOMWindow* aWindow)
|
||||
, mReadyState(EMPTY)
|
||||
, mTotal(0)
|
||||
, mTransferred(0)
|
||||
, mTarget(do_GetCurrentThread())
|
||||
, mBusyCount(0)
|
||||
, mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerPrivate && !aWindow);
|
||||
MOZ_ASSERT_IF(NS_IsMainThread(), !mWorkerPrivate);
|
||||
SetDOMStringToNull(mResult);
|
||||
}
|
||||
|
||||
@ -111,9 +131,17 @@ FileReader::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
// The owner can be null when this object is used by chrome code.
|
||||
nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<FileReader> fileReader = new FileReader(owner);
|
||||
WorkerPrivate* workerPrivate = nullptr;
|
||||
|
||||
if (!owner && nsContentUtils::IsCallerChrome()) {
|
||||
if (!NS_IsMainThread()) {
|
||||
JSContext* cx = aGlobal.Context();
|
||||
workerPrivate = GetWorkerPrivateFromContext(cx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
}
|
||||
|
||||
RefPtr<FileReader> fileReader = new FileReader(owner, workerPrivate);
|
||||
|
||||
if (!owner && nsContentUtils::ThreadsafeIsCallerChrome()) {
|
||||
// Instead of grabbing some random global from the context stack,
|
||||
// let's use the default one (junk scope) for now.
|
||||
// We should move away from this Init...
|
||||
@ -215,7 +243,17 @@ FileReader::DoOnLoadEnd(nsresult aStatus,
|
||||
switch (mDataFormat) {
|
||||
case FILE_AS_ARRAYBUFFER: {
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(DOMEventTargetHelper::GetParentObject()))) {
|
||||
nsCOMPtr<nsIGlobalObject> globalObject;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
globalObject = do_QueryInterface(GetParentObject());
|
||||
} else {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
MOZ_ASSERT(mBusyCount);
|
||||
globalObject = mWorkerPrivate->GlobalScope();
|
||||
}
|
||||
|
||||
if (!globalObject || !jsapi.Init(globalObject)) {
|
||||
FreeFileData();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
@ -256,9 +294,29 @@ FileReader::DoOnLoadEnd(nsresult aStatus,
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
|
||||
FileReader::DoAsyncWait()
|
||||
{
|
||||
MOZ_ASSERT(aStream);
|
||||
nsresult rv = IncreaseBusyCounter();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mAsyncStream->AsyncWait(this,
|
||||
/* aFlags*/ 0,
|
||||
/* aRequestedCount */ 0,
|
||||
mTarget);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
DecreaseBusyCounter();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileReader::DoReadData(uint64_t aCount)
|
||||
{
|
||||
MOZ_ASSERT(mAsyncStream);
|
||||
|
||||
if (mDataFormat == FILE_AS_BINARY) {
|
||||
//Continuously update our binary string as data comes in
|
||||
@ -271,8 +329,8 @@ FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
|
||||
NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
uint32_t bytesRead = 0;
|
||||
aStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
|
||||
&bytesRead);
|
||||
mAsyncStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
|
||||
&bytesRead);
|
||||
NS_ASSERTION(bytesRead == aCount, "failed to read data");
|
||||
}
|
||||
else {
|
||||
@ -287,7 +345,7 @@ FileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
|
||||
}
|
||||
|
||||
uint32_t bytesRead = 0;
|
||||
aStream->Read(mFileData + mDataLen, aCount, &bytesRead);
|
||||
mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead);
|
||||
NS_ASSERTION(bytesRead == aCount, "failed to read data");
|
||||
}
|
||||
|
||||
@ -361,10 +419,7 @@ FileReader::ReadFileContent(Blob& aBlob,
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = mAsyncStream->AsyncWait(this,
|
||||
/* aFlags*/ 0,
|
||||
/* aRequestedCount */ 0,
|
||||
NS_GetCurrentThread());
|
||||
aRv = DoAsyncWait();
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
@ -467,6 +522,7 @@ FileReader::StartProgressEventTimer()
|
||||
mProgressEventWasDelayed = false;
|
||||
mTimerIsActive = true;
|
||||
mProgressNotifier->Cancel();
|
||||
mProgressNotifier->SetTarget(mTarget);
|
||||
mProgressNotifier->InitWithCallback(this, NS_PROGRESS_EVENT_INTERVAL,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
@ -550,18 +606,20 @@ FileReader::OnInputStreamReady(nsIAsyncInputStream* aStream)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// We use this class to decrease the busy counter at the end of this method.
|
||||
// In theory we can do it immediatelly but, for debugging reasons, we want to
|
||||
// be 100% sure we have a feature when OnLoadEnd() is called.
|
||||
FileReaderDecreaseBusyCounter RAII(this);
|
||||
|
||||
uint64_t aCount;
|
||||
nsresult rv = aStream->Available(&aCount);
|
||||
|
||||
if (NS_SUCCEEDED(rv) && aCount) {
|
||||
rv = DoReadData(aStream, aCount);
|
||||
rv = DoReadData(aCount);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = aStream->AsyncWait(this,
|
||||
/* aFlags*/ 0,
|
||||
/* aRequestedCount */ 0,
|
||||
NS_GetCurrentThread());
|
||||
rv = DoAsyncWait();
|
||||
}
|
||||
|
||||
if (NS_FAILED(rv) || !aCount) {
|
||||
@ -643,5 +701,44 @@ FileReader::Abort(ErrorResult& aRv)
|
||||
DispatchProgressEvent(NS_LITERAL_STRING(LOADEND_STR));
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileReader::IncreaseBusyCounter()
|
||||
{
|
||||
if (mWorkerPrivate && mBusyCount++ == 0 &&
|
||||
!mWorkerPrivate->AddFeature(mWorkerPrivate->GetJSContext(), this)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FileReader::DecreaseBusyCounter()
|
||||
{
|
||||
MOZ_ASSERT_IF(mWorkerPrivate, mBusyCount);
|
||||
if (mWorkerPrivate && --mBusyCount == 0) {
|
||||
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
FileReader::Notify(JSContext* aCx, Status aStatus)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (aStatus > Running) {
|
||||
if (mAsyncStream) {
|
||||
mAsyncStream->Close();
|
||||
mAsyncStream = nullptr;
|
||||
}
|
||||
|
||||
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // dom namespace
|
||||
} // mozilla namespace
|
||||
|
@ -9,37 +9,45 @@
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/DOMError.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsISupportsUtils.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "prtime.h"
|
||||
#include "WorkerFeature.h"
|
||||
|
||||
#define NS_PROGRESS_EVENT_INTERVAL 50
|
||||
|
||||
class nsITimer;
|
||||
class nsIEventTarget;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Blob;
|
||||
class DOMError;
|
||||
|
||||
namespace workers {
|
||||
class WorkerPrivate;
|
||||
}
|
||||
|
||||
extern const uint64_t kUnknownSize;
|
||||
|
||||
class FileReaderDecreaseBusyCounter;
|
||||
|
||||
class FileReader final : public DOMEventTargetHelper,
|
||||
public nsIInterfaceRequestor,
|
||||
public nsSupportsWeakReference,
|
||||
public nsIInputStreamCallback,
|
||||
public nsITimerCallback
|
||||
public nsITimerCallback,
|
||||
public workers::WorkerFeature
|
||||
{
|
||||
friend class FileReaderDecreaseBusyCounter;
|
||||
|
||||
public:
|
||||
explicit FileReader(nsPIDOMWindow* aWindow);
|
||||
FileReader(nsPIDOMWindow* aWindow,
|
||||
workers::WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
@ -47,9 +55,11 @@ public:
|
||||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader, DOMEventTargetHelper)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(FileReader,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
// WebIDL
|
||||
static already_AddRefed<FileReader>
|
||||
@ -96,6 +106,9 @@ public:
|
||||
ReadFileContent(aBlob, EmptyString(), FILE_AS_BINARY, aRv);
|
||||
}
|
||||
|
||||
// WorkerFeature
|
||||
bool Notify(JSContext* aCx, workers::Status) override;
|
||||
|
||||
private:
|
||||
virtual ~FileReader();
|
||||
|
||||
@ -131,7 +144,8 @@ private:
|
||||
void DispatchError(nsresult rv, nsAString& finalEvent);
|
||||
nsresult DispatchProgressEvent(const nsAString& aType);
|
||||
|
||||
nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount);
|
||||
nsresult DoAsyncWait();
|
||||
nsresult DoReadData(uint64_t aCount);
|
||||
|
||||
nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
|
||||
nsAString& aTerminationEvent);
|
||||
@ -143,6 +157,9 @@ private:
|
||||
mDataLen = 0;
|
||||
}
|
||||
|
||||
nsresult IncreaseBusyCounter();
|
||||
void DecreaseBusyCounter();
|
||||
|
||||
char *mFileData;
|
||||
RefPtr<Blob> mBlob;
|
||||
nsCString mCharset;
|
||||
@ -166,6 +183,13 @@ private:
|
||||
|
||||
uint64_t mTotal;
|
||||
uint64_t mTransferred;
|
||||
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
|
||||
uint64_t mBusyCount;
|
||||
|
||||
// Kept alive with a WorkerFeature.
|
||||
workers::WorkerPrivate* mWorkerPrivate;
|
||||
};
|
||||
|
||||
} // dom namespace
|
||||
|
@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
[Constructor,
|
||||
Exposed=(Window,System)]
|
||||
Exposed=(Window,Worker,System)]
|
||||
interface FileReader : EventTarget {
|
||||
// async read methods
|
||||
[Throws]
|
||||
|
29
dom/workers/test/fileapi_chromeScript.js
Normal file
29
dom/workers/test/fileapi_chromeScript.js
Normal file
@ -0,0 +1,29 @@
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.importGlobalProperties(["File"]);
|
||||
|
||||
var fileNum = 1;
|
||||
|
||||
function createFileWithData(fileData) {
|
||||
var willDelete = fileData === null;
|
||||
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||
var testFile = dirSvc.get("ProfD", Ci.nsIFile);
|
||||
testFile.append("fileAPItestfile" + fileNum);
|
||||
fileNum++;
|
||||
var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
|
||||
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
|
||||
0666, 0);
|
||||
if (willDelete) {
|
||||
fileData = "some irrelevant test data\n";
|
||||
}
|
||||
outStream.write(fileData, fileData.length);
|
||||
outStream.close();
|
||||
var domFile = new File(testFile);
|
||||
if (willDelete) {
|
||||
testFile.remove(/* recursive: */ false);
|
||||
}
|
||||
return domFile;
|
||||
}
|
||||
|
||||
addMessageListener("files.open", function (message) {
|
||||
sendAsyncMessage("files.opened", message.map(createFileWithData));
|
||||
});
|
@ -119,6 +119,8 @@ support-files =
|
||||
worker_referrer.js
|
||||
websocket_https.html
|
||||
websocket_https_worker.js
|
||||
worker_fileReader.js
|
||||
fileapi_chromeScript.js
|
||||
|
||||
[test_404.html]
|
||||
[test_atob.html]
|
||||
@ -236,3 +238,4 @@ skip-if = (os == "win") || (os == "mac") || toolkit == 'android' #bug 798220
|
||||
[test_referrer.html]
|
||||
[test_sharedWorker_ports.html]
|
||||
[test_sharedWorker_lifetime.html]
|
||||
[test_fileReader.html]
|
||||
|
@ -114,6 +114,8 @@ var interfaceNamesInGlobalScope =
|
||||
"FetchEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"File",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileReader",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileReaderSync",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
100
dom/workers/test/test_fileReader.html
Normal file
100
dom/workers/test/test_fileReader.html
Normal file
@ -0,0 +1,100 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for FileReader in workers</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script type="text/javascript;version=1.7">
|
||||
|
||||
const minFileSize = 20000;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Create strings containing data we'll test with. We'll want long
|
||||
// strings to ensure they span multiple buffers while loading
|
||||
var testTextData = "asd b\tlah\u1234w\u00a0r";
|
||||
while (testTextData.length < minFileSize) {
|
||||
testTextData = testTextData + testTextData;
|
||||
}
|
||||
|
||||
var testASCIIData = "abcdef 123456\n";
|
||||
while (testASCIIData.length < minFileSize) {
|
||||
testASCIIData = testASCIIData + testASCIIData;
|
||||
}
|
||||
|
||||
var testBinaryData = "";
|
||||
for (var i = 0; i < 256; i++) {
|
||||
testBinaryData += String.fromCharCode(i);
|
||||
}
|
||||
while (testBinaryData.length < minFileSize) {
|
||||
testBinaryData = testBinaryData + testBinaryData;
|
||||
}
|
||||
|
||||
var dataurldata0 = testBinaryData.substr(0, testBinaryData.length -
|
||||
testBinaryData.length % 3);
|
||||
var dataurldata1 = testBinaryData.substr(0, testBinaryData.length - 2 -
|
||||
testBinaryData.length % 3);
|
||||
var dataurldata2 = testBinaryData.substr(0, testBinaryData.length - 1 -
|
||||
testBinaryData.length % 3);
|
||||
|
||||
|
||||
//Set up files for testing
|
||||
var openerURL = SimpleTest.getTestFileURL("fileapi_chromeScript.js");
|
||||
var opener = SpecialPowers.loadChromeScript(openerURL);
|
||||
opener.addMessageListener("files.opened", onFilesOpened);
|
||||
opener.sendAsyncMessage("files.open", [
|
||||
testASCIIData,
|
||||
testBinaryData,
|
||||
null,
|
||||
convertToUTF8(testTextData),
|
||||
convertToUTF16(testTextData),
|
||||
"",
|
||||
dataurldata0,
|
||||
dataurldata1,
|
||||
dataurldata2,
|
||||
]);
|
||||
|
||||
function onFilesOpened(message) {
|
||||
var worker = new Worker('worker_fileReader.js');
|
||||
worker.postMessage({ blobs: message,
|
||||
testTextData: testTextData,
|
||||
testASCIIData: testASCIIData,
|
||||
testBinaryData: testBinaryData,
|
||||
dataurldata0: dataurldata0,
|
||||
dataurldata1: dataurldata1,
|
||||
dataurldata2: dataurldata2 });
|
||||
|
||||
worker.onmessage = function(e) {
|
||||
var msg = e.data;
|
||||
if (msg.type == 'finish') {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.type == 'check') {
|
||||
ok(msg.status, msg.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
ok(false, "Unknown message.");
|
||||
}
|
||||
}
|
||||
|
||||
function convertToUTF16(s) {
|
||||
res = "";
|
||||
for (var i = 0; i < s.length; ++i) {
|
||||
c = s.charCodeAt(i);
|
||||
res += String.fromCharCode(c & 255, c >>> 8);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function convertToUTF8(s) {
|
||||
return unescape(encodeURIComponent(s));
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -106,6 +106,8 @@ var interfaceNamesInGlobalScope =
|
||||
"EventTarget",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"File",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileReader",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"FileReaderSync",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
417
dom/workers/test/worker_fileReader.js
Normal file
417
dom/workers/test/worker_fileReader.js
Normal file
@ -0,0 +1,417 @@
|
||||
var testRanCounter = 0;
|
||||
var expectedTestCount = 0;
|
||||
var testSetupFinished = false;
|
||||
|
||||
function ok(a, msg) {
|
||||
postMessage({type: 'check', status: !!a, msg: msg });
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
ok(a === b, msg);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
postMessage({type: 'finish'});
|
||||
}
|
||||
|
||||
function convertToUTF16(s) {
|
||||
res = "";
|
||||
for (var i = 0; i < s.length; ++i) {
|
||||
c = s.charCodeAt(i);
|
||||
res += String.fromCharCode(c & 255, c >>> 8);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
function convertToUTF8(s) {
|
||||
return unescape(encodeURIComponent(s));
|
||||
}
|
||||
|
||||
function convertToDataURL(s) {
|
||||
return "data:application/octet-stream;base64," + btoa(s);
|
||||
}
|
||||
|
||||
onmessage = function(message) {
|
||||
is(FileReader.EMPTY, 0, "correct EMPTY value");
|
||||
is(FileReader.LOADING, 1, "correct LOADING value");
|
||||
is(FileReader.DONE, 2, "correct DONE value");
|
||||
|
||||
// List of blobs.
|
||||
var asciiFile = message.data.blobs.shift();
|
||||
var binaryFile = message.data.blobs.shift();
|
||||
var nonExistingFile = message.data.blobs.shift();
|
||||
var utf8TextFile = message.data.blobs.shift();
|
||||
var utf16TextFile = message.data.blobs.shift();
|
||||
var emptyFile = message.data.blobs.shift();
|
||||
var dataUrlFile0 = message.data.blobs.shift();
|
||||
var dataUrlFile1 = message.data.blobs.shift();
|
||||
var dataUrlFile2 = message.data.blobs.shift();
|
||||
|
||||
// List of buffers for testing.
|
||||
var testTextData = message.data.testTextData;
|
||||
var testASCIIData = message.data.testASCIIData;
|
||||
var testBinaryData = message.data.testBinaryData;
|
||||
var dataurldata0 = message.data.dataurldata0;
|
||||
var dataurldata1 = message.data.dataurldata1;
|
||||
var dataurldata2 = message.data.dataurldata2;
|
||||
|
||||
// Test that plain reading works and fires events as expected, both
|
||||
// for text and binary reading
|
||||
|
||||
var onloadHasRunText = false;
|
||||
var onloadStartHasRunText = false;
|
||||
r = new FileReader();
|
||||
is(r.readyState, FileReader.EMPTY, "correct initial text readyState");
|
||||
r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "plain reading");
|
||||
r.addEventListener("load", function() { onloadHasRunText = true }, false);
|
||||
r.addEventListener("loadstart", function() { onloadStartHasRunText = true }, false);
|
||||
r.readAsText(asciiFile);
|
||||
is(r.readyState, FileReader.LOADING, "correct loading text readyState");
|
||||
is(onloadHasRunText, false, "text loading must be async");
|
||||
is(onloadStartHasRunText, true, "text loadstart should fire sync");
|
||||
expectedTestCount++;
|
||||
|
||||
var onloadHasRunBinary = false;
|
||||
var onloadStartHasRunBinary = false;
|
||||
r = new FileReader();
|
||||
is(r.readyState, FileReader.EMPTY, "correct initial binary readyState");
|
||||
r.addEventListener("load", function() { onloadHasRunBinary = true }, false);
|
||||
r.addEventListener("loadstart", function() { onloadStartHasRunBinary = true }, false);
|
||||
r.readAsBinaryString(binaryFile);
|
||||
r.onload = getLoadHandler(testBinaryData, testBinaryData.length, "binary reading");
|
||||
is(r.readyState, FileReader.LOADING, "correct loading binary readyState");
|
||||
is(onloadHasRunBinary, false, "binary loading must be async");
|
||||
is(onloadStartHasRunBinary, true, "binary loadstart should fire sync");
|
||||
expectedTestCount++;
|
||||
|
||||
var onloadHasRunArrayBuffer = false;
|
||||
var onloadStartHasRunArrayBuffer = false;
|
||||
r = new FileReader();
|
||||
is(r.readyState, FileReader.EMPTY, "correct initial arrayBuffer readyState");
|
||||
r.addEventListener("load", function() { onloadHasRunArrayBuffer = true }, false);
|
||||
r.addEventListener("loadstart", function() { onloadStartHasRunArrayBuffer = true }, false);
|
||||
r.readAsArrayBuffer(binaryFile);
|
||||
r.onload = getLoadHandlerForArrayBuffer(testBinaryData, testBinaryData.length, "array buffer reading");
|
||||
is(r.readyState, FileReader.LOADING, "correct loading arrayBuffer readyState");
|
||||
is(onloadHasRunArrayBuffer, false, "arrayBuffer loading must be async");
|
||||
is(onloadStartHasRunArrayBuffer, true, "arrayBuffer loadstart should fire sync");
|
||||
expectedTestCount++;
|
||||
|
||||
// Test a variety of encodings, and make sure they work properly
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "no encoding reading");
|
||||
r.readAsText(asciiFile, "");
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "iso8859 reading");
|
||||
r.readAsText(asciiFile, "iso-8859-1");
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(testTextData,
|
||||
convertToUTF8(testTextData).length,
|
||||
"utf8 reading");
|
||||
r.readAsText(utf8TextFile, "utf8");
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.readAsText(utf16TextFile, "utf-16");
|
||||
r.onload = getLoadHandler(testTextData,
|
||||
convertToUTF16(testTextData).length,
|
||||
"utf16 reading");
|
||||
expectedTestCount++;
|
||||
|
||||
// Test get result without reading
|
||||
r = new FileReader();
|
||||
is(r.readyState, FileReader.EMPTY,
|
||||
"readyState in test reader get result without reading");
|
||||
is(r.error, null,
|
||||
"no error in test reader get result without reading");
|
||||
is(r.result, null,
|
||||
"result in test reader get result without reading");
|
||||
|
||||
// Test loading an empty file works (and doesn't crash!)
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler("", 0, "empty no encoding reading");
|
||||
r.readAsText(emptyFile, "");
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler("", 0, "empty utf8 reading");
|
||||
r.readAsText(emptyFile, "utf8");
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler("", 0, "empty utf16 reading");
|
||||
r.readAsText(emptyFile, "utf-16");
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler("", 0, "empty binary string reading");
|
||||
r.readAsBinaryString(emptyFile);
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandlerForArrayBuffer("", 0, "empty array buffer reading");
|
||||
r.readAsArrayBuffer(emptyFile);
|
||||
expectedTestCount++;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(convertToDataURL(""), 0, "empt binary string reading");
|
||||
r.readAsDataURL(emptyFile);
|
||||
expectedTestCount++;
|
||||
|
||||
// Test reusing a FileReader to read multiple times
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(testASCIIData,
|
||||
testASCIIData.length,
|
||||
"to-be-reused reading text")
|
||||
var makeAnotherReadListener = function(event) {
|
||||
r = event.target;
|
||||
r.removeEventListener("load", makeAnotherReadListener, false);
|
||||
r.onload = getLoadHandler(testASCIIData,
|
||||
testASCIIData.length,
|
||||
"reused reading text");
|
||||
r.readAsText(asciiFile);
|
||||
};
|
||||
r.addEventListener("load", makeAnotherReadListener, false);
|
||||
r.readAsText(asciiFile);
|
||||
expectedTestCount += 2;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(testBinaryData,
|
||||
testBinaryData.length,
|
||||
"to-be-reused reading binary")
|
||||
var makeAnotherReadListener2 = function(event) {
|
||||
r = event.target;
|
||||
r.removeEventListener("load", makeAnotherReadListener2, false);
|
||||
r.onload = getLoadHandler(testBinaryData,
|
||||
testBinaryData.length,
|
||||
"reused reading binary");
|
||||
r.readAsBinaryString(binaryFile);
|
||||
};
|
||||
r.addEventListener("load", makeAnotherReadListener2, false);
|
||||
r.readAsBinaryString(binaryFile);
|
||||
expectedTestCount += 2;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(convertToDataURL(testBinaryData),
|
||||
testBinaryData.length,
|
||||
"to-be-reused reading data url")
|
||||
var makeAnotherReadListener3 = function(event) {
|
||||
r = event.target;
|
||||
r.removeEventListener("load", makeAnotherReadListener3, false);
|
||||
r.onload = getLoadHandler(convertToDataURL(testBinaryData),
|
||||
testBinaryData.length,
|
||||
"reused reading data url");
|
||||
r.readAsDataURL(binaryFile);
|
||||
};
|
||||
r.addEventListener("load", makeAnotherReadListener3, false);
|
||||
r.readAsDataURL(binaryFile);
|
||||
expectedTestCount += 2;
|
||||
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandlerForArrayBuffer(testBinaryData,
|
||||
testBinaryData.length,
|
||||
"to-be-reused reading arrayBuffer")
|
||||
var makeAnotherReadListener4 = function(event) {
|
||||
r = event.target;
|
||||
r.removeEventListener("load", makeAnotherReadListener4, false);
|
||||
r.onload = getLoadHandlerForArrayBuffer(testBinaryData,
|
||||
testBinaryData.length,
|
||||
"reused reading arrayBuffer");
|
||||
r.readAsArrayBuffer(binaryFile);
|
||||
};
|
||||
r.addEventListener("load", makeAnotherReadListener4, false);
|
||||
r.readAsArrayBuffer(binaryFile);
|
||||
expectedTestCount += 2;
|
||||
|
||||
// Test first reading as ArrayBuffer then read as something else
|
||||
// (BinaryString) and doesn't crash
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandlerForArrayBuffer(testBinaryData,
|
||||
testBinaryData.length,
|
||||
"to-be-reused reading arrayBuffer")
|
||||
var makeAnotherReadListener5 = function(event) {
|
||||
r = event.target;
|
||||
r.removeEventListener("load", makeAnotherReadListener5, false);
|
||||
r.onload = getLoadHandler(testBinaryData,
|
||||
testBinaryData.length,
|
||||
"reused reading binary string");
|
||||
r.readAsBinaryString(binaryFile);
|
||||
};
|
||||
r.addEventListener("load", makeAnotherReadListener5, false);
|
||||
r.readAsArrayBuffer(binaryFile);
|
||||
expectedTestCount += 2;
|
||||
|
||||
//Test data-URI encoding on differing file sizes
|
||||
is(dataurldata0.length % 3, 0, "Want to test data with length % 3 == 0");
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(convertToDataURL(dataurldata0),
|
||||
dataurldata0.length,
|
||||
"dataurl reading, %3 = 0");
|
||||
r.readAsDataURL(dataUrlFile0);
|
||||
expectedTestCount++;
|
||||
|
||||
is(dataurldata1.length % 3, 1, "Want to test data with length % 3 == 1");
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(convertToDataURL(dataurldata1),
|
||||
dataurldata1.length,
|
||||
"dataurl reading, %3 = 1");
|
||||
r.readAsDataURL(dataUrlFile1);
|
||||
expectedTestCount++;
|
||||
|
||||
is(dataurldata2.length % 3, 2, "Want to test data with length % 3 == 2");
|
||||
r = new FileReader();
|
||||
r.onload = getLoadHandler(convertToDataURL(dataurldata2),
|
||||
dataurldata2.length,
|
||||
"dataurl reading, %3 = 2");
|
||||
r.readAsDataURL(dataUrlFile2),
|
||||
expectedTestCount++;
|
||||
|
||||
|
||||
// Test abort()
|
||||
var abortHasRun = false;
|
||||
var loadEndHasRun = false;
|
||||
r = new FileReader();
|
||||
r.onabort = function (event) {
|
||||
is(abortHasRun, false, "abort should only fire once");
|
||||
is(loadEndHasRun, false, "loadend shouldn't have fired yet");
|
||||
abortHasRun = true;
|
||||
is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort");
|
||||
is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads");
|
||||
is(event.target.result, null, "file data should be null on aborted reads");
|
||||
}
|
||||
r.onloadend = function (event) {
|
||||
is(abortHasRun, true, "abort should fire before loadend");
|
||||
is(loadEndHasRun, false, "loadend should only fire once");
|
||||
loadEndHasRun = true;
|
||||
is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort");
|
||||
is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads");
|
||||
is(event.target.result, null, "file data should be null on aborted reads");
|
||||
}
|
||||
r.onload = function() { ok(false, "load should not fire for aborted reads") };
|
||||
r.onerror = function() { ok(false, "error should not fire for aborted reads") };
|
||||
r.onprogress = function() { ok(false, "progress should not fire for aborted reads") };
|
||||
var abortThrew = false;
|
||||
try {
|
||||
r.abort();
|
||||
} catch(e) {
|
||||
abortThrew = true;
|
||||
}
|
||||
is(abortThrew, true, "abort() must throw if not loading");
|
||||
is(abortHasRun, false, "abort() is a no-op unless loading");
|
||||
r.readAsText(asciiFile);
|
||||
r.abort();
|
||||
is(abortHasRun, true, "abort should fire sync");
|
||||
is(loadEndHasRun, true, "loadend should fire sync");
|
||||
|
||||
// Test calling readAsX to cause abort()
|
||||
var reuseAbortHasRun = false;
|
||||
r = new FileReader();
|
||||
r.onabort = function (event) {
|
||||
is(reuseAbortHasRun, false, "abort should only fire once");
|
||||
reuseAbortHasRun = true;
|
||||
is(event.target.readyState, FileReader.DONE, "should be DONE while firing onabort");
|
||||
is(event.target.error.name, "AbortError", "error set to AbortError for aborted reads");
|
||||
is(event.target.result, null, "file data should be null on aborted reads");
|
||||
}
|
||||
r.onload = function() { ok(false, "load should not fire for aborted reads") };
|
||||
var abortThrew = false;
|
||||
try {
|
||||
r.abort();
|
||||
} catch(e) {
|
||||
abortThrew = true;
|
||||
}
|
||||
is(abortThrew, true, "abort() must throw if not loading");
|
||||
is(reuseAbortHasRun, false, "abort() is a no-op unless loading");
|
||||
r.readAsText(asciiFile);
|
||||
r.readAsText(asciiFile);
|
||||
is(reuseAbortHasRun, true, "abort should fire sync");
|
||||
r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading");
|
||||
expectedTestCount++;
|
||||
|
||||
|
||||
// Test reading from nonexistent files
|
||||
r = new FileReader();
|
||||
var didThrow = false;
|
||||
r.onerror = function (event) {
|
||||
is(event.target.readyState, FileReader.DONE, "should be DONE while firing onerror");
|
||||
is(event.target.error.name, "NotFoundError", "error set to NotFoundError for nonexistent files");
|
||||
is(event.target.result, null, "file data should be null on aborted reads");
|
||||
testHasRun();
|
||||
};
|
||||
r.onload = function (event) {
|
||||
is(false, "nonexistent file shouldn't load! (FIXME: bug 1122788)");
|
||||
testHasRun();
|
||||
};
|
||||
try {
|
||||
r.readAsDataURL(nonExistingFile);
|
||||
expectedTestCount++;
|
||||
} catch(ex) {
|
||||
didThrow = true;
|
||||
}
|
||||
// Once this test passes, we should test that onerror gets called and
|
||||
// that the FileReader object is in the right state during that call.
|
||||
is(didThrow, false, "shouldn't throw when opening nonexistent file, should fire error instead");
|
||||
|
||||
|
||||
function getLoadHandler(expectedResult, expectedLength, testName) {
|
||||
return function (event) {
|
||||
is(event.target.readyState, FileReader.DONE,
|
||||
"readyState in test " + testName);
|
||||
is(event.target.error, null,
|
||||
"no error in test " + testName);
|
||||
is(event.target.result, expectedResult,
|
||||
"result in test " + testName);
|
||||
is(event.lengthComputable, true,
|
||||
"lengthComputable in test " + testName);
|
||||
is(event.loaded, expectedLength,
|
||||
"loaded in test " + testName);
|
||||
is(event.total, expectedLength,
|
||||
"total in test " + testName);
|
||||
testHasRun();
|
||||
}
|
||||
}
|
||||
|
||||
function getLoadHandlerForArrayBuffer(expectedResult, expectedLength, testName) {
|
||||
return function (event) {
|
||||
is(event.target.readyState, FileReader.DONE,
|
||||
"readyState in test " + testName);
|
||||
is(event.target.error, null,
|
||||
"no error in test " + testName);
|
||||
is(event.lengthComputable, true,
|
||||
"lengthComputable in test " + testName);
|
||||
is(event.loaded, expectedLength,
|
||||
"loaded in test " + testName);
|
||||
is(event.total, expectedLength,
|
||||
"total in test " + testName);
|
||||
is(event.target.result.byteLength, expectedLength,
|
||||
"array buffer size in test " + testName);
|
||||
var u8v = new Uint8Array(event.target.result);
|
||||
is(String.fromCharCode.apply(String, u8v), expectedResult,
|
||||
"array buffer contents in test " + testName);
|
||||
u8v = null;
|
||||
is(event.target.result.byteLength, expectedLength,
|
||||
"array buffer size after gc in test " + testName);
|
||||
u8v = new Uint8Array(event.target.result);
|
||||
is(String.fromCharCode.apply(String, u8v), expectedResult,
|
||||
"array buffer contents after gc in test " + testName);
|
||||
testHasRun();
|
||||
}
|
||||
}
|
||||
|
||||
function testHasRun() {
|
||||
//alert(testRanCounter);
|
||||
++testRanCounter;
|
||||
if (testRanCounter == expectedTestCount) {
|
||||
is(testSetupFinished, true, "test setup should have finished; check for exceptions");
|
||||
is(onloadHasRunText, true, "onload text should have fired by now");
|
||||
is(onloadHasRunBinary, true, "onload binary should have fired by now");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
testSetupFinished = true;
|
||||
}
|
@ -41,136 +41,3 @@
|
||||
|
||||
[FileList interface: attribute length]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: existence and properties of interface object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface object length]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: existence and properties of interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: existence and properties of interface prototype object's "constructor" property]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: operation readAsArrayBuffer(Blob)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: operation readAsText(Blob,DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: operation readAsDataURL(Blob)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: operation abort()]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: constant EMPTY on interface object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: constant EMPTY on interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: constant LOADING on interface object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: constant LOADING on interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: constant DONE on interface object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: constant DONE on interface prototype object]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute readyState]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute result]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute error]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute onloadstart]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute onprogress]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute onload]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute onabort]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute onerror]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: attribute onloadend]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader must be primary interface of new FileReader()]
|
||||
expected: FAIL
|
||||
|
||||
[Stringification of new FileReader()]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "readAsArrayBuffer" with the proper type (0)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: calling readAsArrayBuffer(Blob) on new FileReader() with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "readAsText" with the proper type (1)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: calling readAsText(Blob,DOMString) on new FileReader() with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "readAsDataURL" with the proper type (2)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: calling readAsDataURL(Blob) on new FileReader() with too few arguments must throw TypeError]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "abort" with the proper type (3)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "EMPTY" with the proper type (4)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "LOADING" with the proper type (5)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "DONE" with the proper type (6)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "readyState" with the proper type (7)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "result" with the proper type (8)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "error" with the proper type (9)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "onloadstart" with the proper type (10)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "onprogress" with the proper type (11)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "onload" with the proper type (12)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "onabort" with the proper type (13)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "onerror" with the proper type (14)]
|
||||
expected: FAIL
|
||||
|
||||
[FileReader interface: new FileReader() must inherit property "onloadend" with the proper type (15)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "nsIPipe.h"
|
||||
#include "nsICloneableInputStream.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsICancelableRunnable.h"
|
||||
#include "nsISafeOutputStream.h"
|
||||
#include "nsString.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
@ -25,8 +25,11 @@ using namespace mozilla;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This is a nsICancelableRunnable because we can dispatch it to Workers and
|
||||
// those can be shut down at any time, and in these cases, Cancel() is called
|
||||
// instead of Run().
|
||||
class nsInputStreamReadyEvent final
|
||||
: public nsIRunnable
|
||||
: public nsICancelableRunnable
|
||||
, public nsIInputStreamCallback
|
||||
{
|
||||
public:
|
||||
@ -95,19 +98,28 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
mCallback = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAsyncInputStream> mStream;
|
||||
nsCOMPtr<nsIInputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsInputStreamReadyEvent, nsIRunnable,
|
||||
nsIInputStreamCallback)
|
||||
NS_IMPL_ISUPPORTS(nsInputStreamReadyEvent, nsICancelableRunnable,
|
||||
nsIRunnable, nsIInputStreamCallback)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This is a nsICancelableRunnable because we can dispatch it to Workers and
|
||||
// those can be shut down at any time, and in these cases, Cancel() is called
|
||||
// instead of Run().
|
||||
class nsOutputStreamReadyEvent final
|
||||
: public nsIRunnable
|
||||
: public nsICancelableRunnable
|
||||
, public nsIOutputStreamCallback
|
||||
{
|
||||
public:
|
||||
@ -176,14 +188,20 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
mCallback = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAsyncOutputStream> mStream;
|
||||
nsCOMPtr<nsIOutputStreamCallback> mCallback;
|
||||
nsCOMPtr<nsIEventTarget> mTarget;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsOutputStreamReadyEvent, nsIRunnable,
|
||||
nsIOutputStreamCallback)
|
||||
NS_IMPL_ISUPPORTS(nsOutputStreamReadyEvent, nsICancelableRunnable,
|
||||
nsIRunnable, nsIOutputStreamCallback)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@ -216,7 +234,7 @@ NS_NewOutputStreamReadyEvent(nsIOutputStreamCallback* aCallback,
|
||||
class nsAStreamCopier
|
||||
: public nsIInputStreamCallback
|
||||
, public nsIOutputStreamCallback
|
||||
, public nsIRunnable
|
||||
, public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
@ -433,6 +451,8 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() MOZ_MUST_OVERRIDE override = 0;
|
||||
|
||||
nsresult PostContinuationEvent()
|
||||
{
|
||||
// we cannot post a continuation event if there is currently
|
||||
@ -489,6 +509,7 @@ protected:
|
||||
NS_IMPL_ISUPPORTS(nsAStreamCopier,
|
||||
nsIInputStreamCallback,
|
||||
nsIOutputStreamCallback,
|
||||
nsICancelableRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
class nsStreamCopierIB final : public nsAStreamCopier
|
||||
@ -527,7 +548,8 @@ public:
|
||||
return state->mSinkCondition;
|
||||
}
|
||||
|
||||
uint32_t DoCopy(nsresult* aSourceCondition, nsresult* aSinkCondition)
|
||||
uint32_t DoCopy(nsresult* aSourceCondition,
|
||||
nsresult* aSinkCondition) override
|
||||
{
|
||||
ReadSegmentsState state;
|
||||
state.mSink = mSink;
|
||||
@ -539,6 +561,11 @@ public:
|
||||
*aSinkCondition = state.mSinkCondition;
|
||||
return n;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class nsStreamCopierOB final : public nsAStreamCopier
|
||||
@ -577,7 +604,8 @@ public:
|
||||
return state->mSourceCondition;
|
||||
}
|
||||
|
||||
uint32_t DoCopy(nsresult* aSourceCondition, nsresult* aSinkCondition)
|
||||
uint32_t DoCopy(nsresult* aSourceCondition,
|
||||
nsresult* aSinkCondition) override
|
||||
{
|
||||
WriteSegmentsState state;
|
||||
state.mSource = mSource;
|
||||
@ -589,6 +617,11 @@ public:
|
||||
*aSourceCondition = state.mSourceCondition;
|
||||
return n;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -127,10 +127,23 @@ public:
|
||||
|
||||
} // namespace
|
||||
|
||||
class nsTimerEvent : public nsRunnable
|
||||
// This is a nsICancelableRunnable because we can dispatch it to Workers and
|
||||
// those can be shut down at any time, and in these cases, Cancel() is called
|
||||
// instead of Run().
|
||||
class nsTimerEvent : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD Run();
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
NS_IMETHOD Cancel() override
|
||||
{
|
||||
// Since nsTimerImpl is not thread-safe, we should release |mTimer|
|
||||
// here in the target thread to avoid race condition. Otherwise,
|
||||
// ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
|
||||
// timer thread and result in race condition.
|
||||
mTimer = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTimerEvent()
|
||||
: mTimer()
|
||||
@ -253,6 +266,8 @@ nsTimerEvent::DeleteAllocatorIfNeeded()
|
||||
NS_IMETHODIMP
|
||||
nsTimerEvent::Run()
|
||||
{
|
||||
MOZ_ASSERT(mTimer);
|
||||
|
||||
if (mGeneration != mTimer->GetGeneration()) {
|
||||
return NS_OK;
|
||||
}
|
||||
@ -265,13 +280,10 @@ nsTimerEvent::Run()
|
||||
}
|
||||
|
||||
mTimer->Fire();
|
||||
// Since nsTimerImpl is not thread-safe, we should release |mTimer|
|
||||
// here in the target thread to avoid race condition. Otherwise,
|
||||
// ~nsTimerEvent() which calls nsTimerImpl::Release() could run in the
|
||||
// timer thread and result in race condition.
|
||||
mTimer = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
// We call Cancel() to correctly release mTimer.
|
||||
// Read more in the Cancel() implementation.
|
||||
return Cancel();
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -16,6 +16,7 @@ interface nsICancelableRunnable : nsIRunnable
|
||||
/**
|
||||
* Cancels a pending task. If the task has already been executed this will
|
||||
* be a no-op. Calling this method twice is considered an error.
|
||||
* If cancel() is called, run() will not be called.
|
||||
*
|
||||
* @throws NS_ERROR_UNEXPECTED
|
||||
* Indicates that the runnable has already been canceled.
|
||||
|
Loading…
Reference in New Issue
Block a user