merge mozilla-central to autoland. r=merge a=merge

This commit is contained in:
Sebastian Hengst 2017-12-01 01:30:16 +02:00
Родитель e77ee731e9 7cbd9c3050
Коммит 5e55af8f82
28 изменённых файлов: 1694 добавлений и 1111 удалений

Просмотреть файл

@ -1,8 +1,8 @@
[DEFAULT]
support-files =
support-files = script_file.js
[test_ipcBlob_fileReaderSync.html]
support-files = script_file.js
[test_ipcBlob_workers.html]
[test_ipcBlob_createImageBitmap.html]
support-files = green.jpg
[test_ipcBlob_emptyMultiplex.html]

Просмотреть файл

@ -7,7 +7,7 @@ addMessageListener("file.open", function (e) {
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
testFile.append("ipc_fileReader_testing");
testFile.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
testFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
@ -22,3 +22,21 @@ addMessageListener("file.open", function (e) {
sendAsyncMessage("file.opened", { file });
});
});
addMessageListener("emptyfile.open", function (e) {
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
testFile.append("ipc_fileReader_testing");
testFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600);
var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
File.createFromNsIFile(testFile).then(function(file) {
sendAsyncMessage("emptyfile.opened", { file });
});
});

Просмотреть файл

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test an empty IPCBlob together with other parts</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<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">
function checkContent(msg, content) {
return new Promise(resolve => {
let fr = new FileReader();
fr.readAsText(new Blob(content));
fr.onloadend = () => {
is(fr.result, "Hello world!", "The content matches: " + msg);
resolve();
};
});
}
SimpleTest.waitForExplicitFinish();
let url = SimpleTest.getTestFileURL("script_file.js");
let script = SpecialPowers.loadChromeScript(url);
script.addMessageListener("emptyfile.opened", message => {
checkContent("middle", ["Hello ", message.file, "world!"]).
then(() => checkContent("begin", [message.file, "Hello world!"])).
then(() => checkContent("end", ["Hello world!", message.file])).
then(() => checkContent("random", [message.file, message.file, "Hello world!", message.file])).
then(SimpleTest.finish);
});
script.sendAsyncMessage("emptyfile.open");
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -75,6 +75,8 @@
is(v2.videoHeight, v1.videoHeight / 2, "sink is half the height of source");
stream.getTracks().forEach(track => track.stop());
v1.srcObject = v2.srcObject = null;
pc1.close()
pc2.close()
}
pushPrefs(['media.peerconnection.video.lock_scaling', true]).then(() => {

Просмотреть файл

@ -35,12 +35,13 @@ function handleRequest(request, response)
switch (query[0]) {
case "blocked":
var alreadyUnblocked = getGlobalState(query[1]);
response.processAsync();
if (alreadyUnblocked) {
// the unblock request came before the blocked request, just go on and finish synchronously
finishBlockedRequest(request, response, query);
} else {
setGlobalState(response, query[1]);
response.processAsync();
}
break;

Просмотреть файл

@ -353,7 +353,7 @@ AsyncImagePipelineManager::ApplyAsyncImages()
builder.Finalize(builderContentSize, dl);
mApi->SetDisplayList(gfx::Color(0.f, 0.f, 0.f, 0.f), epoch, LayerSize(pipeline->mScBounds.Width(), pipeline->mScBounds.Height()),
pipelineId, builderContentSize,
dl.dl_desc, dl.dl.inner.data, dl.dl.inner.length,
dl.dl_desc, dl.dl,
resourceUpdates);
}
}

Просмотреть файл

@ -114,6 +114,7 @@ gecko_profiler_unregister_thread()
}
namespace mozilla {
namespace layers {
using namespace mozilla::gfx;
@ -598,6 +599,9 @@ WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntSize& aSize,
return IPC_FAIL(this, "Failed to deserialize resource updates");
}
wr::Vec_u8 dlData(Move(dl));
// If id namespaces do not match, it means the command is obsolete, probably
// because the tab just moved to a new window.
// In that case do not send the commands to webrender.
@ -609,7 +613,7 @@ WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntSize& aSize,
gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
mApi->SetDisplayList(clearColor, wr::NewEpoch(wrEpoch), LayerSize(aSize.width, aSize.height),
mPipelineId, aContentSize,
dlDesc, dl.mData, dl.mLen,
dlDesc, dlData,
resources);
ScheduleGenerateFrame();

Просмотреть файл

@ -265,8 +265,7 @@ WebRenderAPI::SetDisplayList(gfx::Color aBgColor,
wr::WrPipelineId pipeline_id,
const LayoutSize& content_size,
wr::BuiltDisplayListDescriptor dl_descriptor,
uint8_t *dl_data,
size_t dl_size,
wr::Vec_u8& dl_data,
ResourceUpdateQueue& aResources)
{
wr_api_set_display_list(mDocHandle,
@ -276,8 +275,7 @@ WebRenderAPI::SetDisplayList(gfx::Color aBgColor,
pipeline_id,
content_size,
dl_descriptor,
dl_data,
dl_size,
&dl_data.inner,
aResources.Raw());
}

Просмотреть файл

@ -161,8 +161,7 @@ public:
wr::WrPipelineId pipeline_id,
const wr::LayoutSize& content_size,
wr::BuiltDisplayListDescriptor dl_descriptor,
uint8_t *dl_data,
size_t dl_size,
wr::Vec_u8& dl_data,
ResourceUpdateQueue& aResources);
void ClearDisplayList(Epoch aEpoch, wr::WrPipelineId pipeline_id);

Просмотреть файл

@ -0,0 +1,24 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "WebRenderTypes.h"
#include "mozilla/ipc/ByteBuf.h"
namespace mozilla {
namespace wr {
Vec_u8::Vec_u8(mozilla::ipc::ByteBuf&& aSrc) {
inner.data = aSrc.mData;
inner.length = aSrc.mLen;
inner.capacity = aSrc.mCapacity;
aSrc.mData = nullptr;
aSrc.mLen = 0;
aSrc.mCapacity = 0;
}
} // namespace wr
} // namespace mozilla

Просмотреть файл

@ -21,6 +21,11 @@
#include "nsStyleConsts.h"
namespace mozilla {
namespace ipc {
class ByteBuf;
} // namespace ipc
namespace wr {
typedef wr::WrWindowId WindowId;
@ -576,6 +581,8 @@ struct Vec_u8 {
src.SetEmpty();
}
explicit Vec_u8(mozilla::ipc::ByteBuf&& aSrc);
Vec_u8&
operator=(Vec_u8&& src) {
inner = src.inner;

Просмотреть файл

@ -29,6 +29,7 @@ UNIFIED_SOURCES += [
'RenderTextureHostOGL.cpp',
'RenderThread.cpp',
'WebRenderAPI.cpp',
'WebRenderTypes.cpp',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':

Просмотреть файл

@ -958,8 +958,7 @@ pub unsafe extern "C" fn wr_api_set_display_list(
pipeline_id: WrPipelineId,
content_size: LayoutSize,
dl_descriptor: BuiltDisplayListDescriptor,
dl_data: *mut u8,
dl_size: usize,
dl_data: &mut WrVecU8,
resources: &mut ResourceUpdates,
) {
let resource_updates = mem::replace(resources, ResourceUpdates::new());
@ -971,10 +970,7 @@ pub unsafe extern "C" fn wr_api_set_display_list(
// but I suppose it is a good default.
let preserve_frame_state = true;
let dl_slice = make_slice(dl_data, dl_size);
let mut dl_vec = Vec::new();
// XXX: see if we can get rid of the copy here
dl_vec.extend_from_slice(dl_slice);
let dl_vec = dl_data.flush_into_vec();
let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
dh.api.set_display_list(

Просмотреть файл

@ -1068,8 +1068,7 @@ void wr_api_set_display_list(DocumentHandle *aDh,
WrPipelineId aPipelineId,
LayoutSize aContentSize,
BuiltDisplayListDescriptor aDlDescriptor,
uint8_t *aDlData,
size_t aDlSize,
WrVecU8 *aDlData,
ResourceUpdates *aResources)
WR_FUNC;

Просмотреть файл

@ -0,0 +1,25 @@
// |jit-test| --ion-offthread-compile=off
load(libdir + "asm.js");
if (!isAsmJSCompilationAvailable())
quit(0);
if (!('oomTest' in this))
quit();
oomTest(
function() {
eval(`
function f(stdlib, foreign, buffer) {
"use asm";
var i32 = new stdlib.Int32Array(buffer);
function set(v) {
v=v|0;
i32[5] = v;
}
return set;
}
`);
}
);

Просмотреть файл

@ -739,18 +739,6 @@ JitRuntime::getVMWrapper(const VMFunction& f) const
return trampolineCode(p->value());
}
void
JitCodeHeader::init(JitCode* jitCode)
{
jitCode_ = jitCode;
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
// On AMD Bobcat processors that may have eratas, insert a NOP slide to reduce crashes
if (CPUInfo::NeedAmdBugWorkaround())
memset((char *)&nops_, X86Encoding::OneByteOpcodeID::OP_NOP, sizeof(nops_));
#endif
}
template <AllowGC allowGC>
JitCode*
JitCode::New(JSContext* cx, uint8_t* code, uint32_t bufferSize, uint32_t headerSize,
@ -779,10 +767,9 @@ JitCode::New<NoGC>(JSContext* cx, uint8_t* code, uint32_t bufferSize, uint32_t h
void
JitCode::copyFrom(MacroAssembler& masm)
{
// Store the JitCode pointer in the JitCodeHeader so we can recover the
// gcthing from relocation tables.
JitCodeHeader::FromExecutable(code_)->init(this);
// Store the JitCode pointer right before the code buffer, so we can
// recover the gcthing from relocation tables.
*(JitCode**)(code_ - sizeof(JitCode*)) = this;
insnSize_ = masm.instructionsSize();
masm.executableCopy(code_);

Просмотреть файл

@ -29,30 +29,10 @@ class MacroAssembler;
class PatchableBackedge;
class IonBuilder;
class IonICEntry;
class JitCode;
typedef Vector<JSObject*, 4, JitAllocPolicy> ObjectVector;
typedef Vector<TraceLoggerEvent, 0, SystemAllocPolicy> TraceLoggerEventVector;
// Header at start of raw code buffer
struct JitCodeHeader
{
// Link back to corresponding gcthing
JitCode* jitCode_;
// !!! NOTE !!!
// If we are running on AMD Bobcat, insert a NOP-slide at end of the JitCode
// header so we can try to recover when the CPU screws up the branch landing
// site. See Bug 1281759.
void* nops_;
void init(JitCode* jitCode);
static JitCodeHeader* FromExecutable(uint8_t* buffer) {
return (JitCodeHeader*)(buffer - sizeof(JitCodeHeader));
}
};
class JitCode : public gc::TenuredCell
{
protected:
@ -149,7 +129,7 @@ class JitCode : public gc::TenuredCell
void copyFrom(MacroAssembler& masm);
static JitCode* FromExecutable(uint8_t* buffer) {
JitCode* code = JitCodeHeader::FromExecutable(buffer)->jitCode_;
JitCode* code = *(JitCode**)(buffer - sizeof(JitCode*));
MOZ_ASSERT(code->raw() == buffer);
return code;
}

Просмотреть файл

@ -540,6 +540,23 @@ MIRTypeToTag(MIRType type)
return JSVAL_TYPE_TO_TAG(ValueTypeFromMIRType(type));
}
static inline size_t
MIRTypeToSize(MIRType type)
{
switch (type) {
case MIRType::Int32:
return 4;
case MIRType::Int64:
return 8;
case MIRType::Float32:
return 4;
case MIRType::Double:
return 8;
default:
MOZ_CRASH("MIRTypeToSize - unhandled case");
}
}
static inline const char*
StringFromMIRType(MIRType type)
{

Просмотреть файл

@ -23,35 +23,27 @@ Linker::newCode(JSContext* cx, CodeKind kind, bool hasPatchableBackedges /* = fa
if (masm.oom())
return fail(cx);
static const size_t ExecutableAllocatorAlignment = sizeof(void*);
static_assert(CodeAlignment >= ExecutableAllocatorAlignment,
"Unexpected alignment requirements");
// We require enough bytes for the code, header, and worst-case alignment padding.
size_t bytesNeeded = masm.bytesNeeded() +
sizeof(JitCodeHeader) +
(CodeAlignment - ExecutableAllocatorAlignment);
ExecutablePool* pool;
size_t bytesNeeded = masm.bytesNeeded() + sizeof(JitCode*) + CodeAlignment;
if (bytesNeeded >= MAX_BUFFER_SIZE)
return fail(cx);
// ExecutableAllocator requires bytesNeeded to be aligned.
bytesNeeded = AlignBytes(bytesNeeded, ExecutableAllocatorAlignment);
// ExecutableAllocator requires bytesNeeded to be word-size aligned.
bytesNeeded = AlignBytes(bytesNeeded, sizeof(void*));
ExecutableAllocator& execAlloc = hasPatchableBackedges
? cx->runtime()->jitRuntime()->backedgeExecAlloc()
: cx->runtime()->jitRuntime()->execAlloc();
ExecutablePool* pool;
uint8_t* result = (uint8_t*)execAlloc.alloc(cx, bytesNeeded, &pool, kind);
if (!result)
return fail(cx);
// The JitCodeHeader will be stored right before the code buffer.
uint8_t* codeStart = result + sizeof(JitCodeHeader);
// The JitCode pointer will be stored right before the code buffer.
uint8_t* codeStart = result + sizeof(JitCode*);
// Bump the code up to a nice alignment.
codeStart = (uint8_t*)AlignBytes((uintptr_t)codeStart, CodeAlignment);
MOZ_ASSERT(codeStart + masm.bytesNeeded() <= result + bytesNeeded);
uint32_t headerSize = codeStart - result;
JitCode* code = JitCode::New<allowGC>(cx, codeStart, bytesNeeded - headerSize,
headerSize, pool, kind);

Просмотреть файл

@ -1471,16 +1471,24 @@ class MacroAssembler : public MacroAssemblerSpecific
DEFINED_ON(arm);
// wasm specific methods, used in both the wasm baseline compiler and ion.
void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm, mips32, mips64);
void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm, mips_shared);
void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
void wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry) PER_ARCH;
void wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry) PER_SHARED_ARCH;
void outOfLineWasmTruncateDoubleToInt32(FloatRegister input, bool isUnsigned,
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);
void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86, x64, arm, mips32, mips64);
void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) DEFINED_ON(x86_shared, arm, mips_shared);
void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
void wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry) PER_ARCH;
void wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry) PER_SHARED_ARCH;
void outOfLineWasmTruncateFloat32ToInt32(FloatRegister input, bool isUnsigned,
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);
void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned, wasm::BytecodeOffset off, Label* rejoin) DEFINED_ON(x86_shared);
void outOfLineWasmTruncateDoubleToInt64(FloatRegister input, bool isUnsigned,
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);
void outOfLineWasmTruncateFloat32ToInt64(FloatRegister input, bool isUnsigned,
wasm::BytecodeOffset off, Label* rejoin)
DEFINED_ON(x86_shared);
// This function takes care of loading the callee's TLS and pinned regs but
// it is the caller's responsibility to save/restore TLS or pinned regs.

Просмотреть файл

@ -932,6 +932,33 @@ MacroAssembler::comment(const char* msg)
Assembler::comment(msg);
}
// ========================================================================
// wasm support
void
MacroAssembler::wasmTruncateDoubleToUInt32(FloatRegister input, Register output, Label* oolEntry)
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateDoubleToInt32(FloatRegister input, Register output, Label* oolEntry)
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateFloat32ToUInt32(FloatRegister input, Register output, Label* oolEntry)
{
MOZ_CRASH("NYI");
}
void
MacroAssembler::wasmTruncateFloat32ToInt32(FloatRegister input, Register output, Label* oolEntry)
{
MOZ_CRASH("NYI");
}
//}}} check_macroassembler_style
} // namespace jit

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -897,6 +897,8 @@ class FunctionCompiler
checkOffsetAndAlignmentAndBounds(access, &base);
load = MWasmLoad::New(alloc(), memoryBase, base, *access, ToMIRType(result));
}
if (!load)
return nullptr;
curBlock_->add(load);
return load;
}
@ -917,6 +919,8 @@ class FunctionCompiler
checkOffsetAndAlignmentAndBounds(access, &base);
store = MWasmStore::New(alloc(), memoryBase, base, *access, v);
}
if (!store)
return;
curBlock_->add(store);
}
@ -941,6 +945,8 @@ class FunctionCompiler
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
MInstruction* cas = MWasmCompareExchangeHeap::New(alloc(), bytecodeOffset(), memoryBase,
base, *access, oldv, newv, tlsPointer_);
if (!cas)
return nullptr;
curBlock_->add(cas);
if (isSmallerAccessForI64(result, access)) {
@ -968,6 +974,8 @@ class FunctionCompiler
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
MInstruction* xchg = MWasmAtomicExchangeHeap::New(alloc(), bytecodeOffset(), memoryBase,
base, *access, value, tlsPointer_);
if (!xchg)
return nullptr;
curBlock_->add(xchg);
if (isSmallerAccessForI64(result, access)) {
@ -995,6 +1003,8 @@ class FunctionCompiler
MWasmLoadTls* memoryBase = maybeLoadMemoryBase();
MInstruction* binop = MWasmAtomicBinopHeap::New(alloc(), bytecodeOffset(), op, memoryBase,
base, *access, value, tlsPointer_);
if (!binop)
return nullptr;
curBlock_->add(binop);
if (isSmallerAccessForI64(result, access)) {

Просмотреть файл

@ -109,6 +109,8 @@ class VerifyToolsMixin(object):
repo_tests_path = os.path.join("testing", "web-platform", "tests")
tests_path = os.path.join("tests", "web-platform", "tests")
for (type, path, test) in man:
if type not in ["testharness", "reftest", "wdspec"]:
continue
repo_path = os.path.join(repo_tests_path, path)
# manifest paths use os.sep (like backslash on Windows) but
# automation-relevance uses posixpath.sep

Просмотреть файл

@ -9,4 +9,5 @@ support-files =
[browser_host_name.js]
[browser_request_summary.js]
[browser_show_dialog.js]
skip-if = os == 'win' && debug # bug 1418385
[browser_total.js]

Просмотреть файл

@ -2167,6 +2167,10 @@ nsWindow::UserActivity()
if (mIdleService) {
mIdleService->ResetIdleTimeOut(0);
}
if (FindTopLevel() != nsWindow::TopWindow()) {
BringToFront();
}
}
TextEventDispatcherListener*

Просмотреть файл

@ -55,6 +55,23 @@ public:
void AsyncWaitCompleted();
struct StreamData
{
void Initialize(nsIInputStream* aStream)
{
mStream = aStream;
mAsyncStream = do_QueryInterface(aStream);
mSeekableStream = do_QueryInterface(aStream);
}
nsCOMPtr<nsIInputStream> mStream;
// This can be null.
nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
// This can be null.
nsCOMPtr<nsISeekableStream> mSeekableStream;
};
private:
~nsMultiplexInputStream()
{
@ -62,7 +79,7 @@ private:
// This method updates mSeekableStreams, mIPCSerializableStreams,
// mCloneableStreams and mAsyncInputStreams values.
void UpdateQIMap(nsIInputStream* aStream, int32_t aCount);
void UpdateQIMap(StreamData& aStream, int32_t aCount);
struct MOZ_STACK_CLASS ReadSegmentsState
{
@ -83,7 +100,9 @@ private:
bool IsAsyncInputStream() const;
Mutex mLock; // Protects access to all data members.
nsTArray<nsCOMPtr<nsIInputStream>> mStreams;
nsTArray<StreamData> mStreams;
uint32_t mCurrentStream;
bool mStartedReadingCurrent;
nsresult mStatus;
@ -121,19 +140,20 @@ NS_IMPL_CI_INTERFACE_GETTER(nsMultiplexInputStream,
nsISeekableStream)
static nsresult
AvailableMaybeSeek(nsIInputStream* aStream, uint64_t* aResult)
AvailableMaybeSeek(nsMultiplexInputStream::StreamData& aStream,
uint64_t* aResult)
{
nsresult rv = aStream->Available(aResult);
nsresult rv = aStream.mStream->Available(aResult);
if (rv == NS_BASE_STREAM_CLOSED) {
// Blindly seek to the current position if Available() returns
// NS_BASE_STREAM_CLOSED.
// If nsIFileInputStream is closed in Read() due to CLOSE_ON_EOF flag,
// Seek() could reopen the file if REOPEN_ON_REWIND flag is set.
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(aStream);
if (seekable) {
nsresult rvSeek = seekable->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
if (aStream.mSeekableStream) {
nsresult rvSeek =
aStream.mSeekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, 0);
if (NS_SUCCEEDED(rvSeek)) {
rv = aStream->Available(aResult);
rv = aStream.mStream->Available(aResult);
}
}
}
@ -180,11 +200,21 @@ NS_IMETHODIMP
nsMultiplexInputStream::AppendStream(nsIInputStream* aStream)
{
MutexAutoLock lock(mLock);
if (!mStreams.AppendElement(aStream)) {
StreamData* streamData = mStreams.AppendElement();
if (NS_WARN_IF(!streamData)) {
return NS_ERROR_OUT_OF_MEMORY;
}
UpdateQIMap(aStream, 1);
streamData->Initialize(aStream);
UpdateQIMap(*streamData, 1);
if (mStatus == NS_BASE_STREAM_CLOSED) {
// We were closed, but now we have more data to read.
mStatus = NS_OK;
}
return NS_OK;
}
@ -192,16 +222,23 @@ NS_IMETHODIMP
nsMultiplexInputStream::InsertStream(nsIInputStream* aStream, uint32_t aIndex)
{
MutexAutoLock lock(mLock);
if (!mStreams.InsertElementAt(aIndex, aStream)) {
StreamData* streamData = mStreams.InsertElementAt(aIndex);
if (NS_WARN_IF(!streamData)) {
return NS_ERROR_OUT_OF_MEMORY;
}
streamData->Initialize(aStream);
if (mCurrentStream > aIndex ||
(mCurrentStream == aIndex && mStartedReadingCurrent)) {
++mCurrentStream;
} else if (mStatus == NS_BASE_STREAM_CLOSED) {
// We were closed, but now we have more data to read.
mStatus = NS_OK;
}
UpdateQIMap(aStream, 1);
UpdateQIMap(*streamData, 1);
return NS_OK;
}
@ -229,12 +266,15 @@ NS_IMETHODIMP
nsMultiplexInputStream::GetStream(uint32_t aIndex, nsIInputStream** aResult)
{
MutexAutoLock lock(mLock);
*aResult = mStreams.SafeElementAt(aIndex, nullptr);
if (NS_WARN_IF(!*aResult)) {
if (aIndex >= mStreams.Length()) {
return NS_ERROR_NOT_AVAILABLE;
}
NS_ADDREF(*aResult);
StreamData& streamData = mStreams.ElementAt(aIndex);
nsCOMPtr<nsIInputStream> stream = streamData.mStream;
stream.forget(aResult);
return NS_OK;
}
@ -248,7 +288,7 @@ nsMultiplexInputStream::Close()
uint32_t len = mStreams.Length();
for (uint32_t i = 0; i < len; ++i) {
nsresult rv2 = mStreams[i]->Close();
nsresult rv2 = mStreams[i].mStream->Close();
// We still want to close all streams, but we should return an error
if (NS_FAILED(rv2)) {
rv = rv2;
@ -261,24 +301,57 @@ nsMultiplexInputStream::Close()
NS_IMETHODIMP
nsMultiplexInputStream::Available(uint64_t* aResult)
{
*aResult = 0;
MutexAutoLock lock(mLock);
if (NS_FAILED(mStatus)) {
return mStatus;
}
uint64_t avail = 0;
nsresult rv = NS_BASE_STREAM_CLOSED;
uint32_t len = mStreams.Length();
for (uint32_t i = mCurrentStream; i < len; i++) {
uint64_t streamAvail;
mStatus = AvailableMaybeSeek(mStreams[i], &streamAvail);
if (NS_WARN_IF(NS_FAILED(mStatus))) {
rv = AvailableMaybeSeek(mStreams[i], &streamAvail);
if (rv == NS_BASE_STREAM_CLOSED) {
// If a stream is closed, we continue with the next one.
// If this is the last stream, we want to return this error code.
continue;
}
if (NS_WARN_IF(NS_FAILED(rv))) {
mStatus = rv;
return mStatus;
}
// If the current stream is async, we have to return what we have so far
// without processing the following streams. This is needed because
// ::Available should return only what is currently available. In case of an
// nsIAsyncInputStream, we have to call AsyncWait() in order to read more.
if (mStreams[i].mAsyncStream) {
avail += streamAvail;
break;
}
if (streamAvail == 0) {
// Nothing to read for this stream. Let's move to the next one.
continue;
}
avail += streamAvail;
}
*aResult = avail;
return NS_OK;
// We still have something to read. We don't want to return an error code yet.
if (avail) {
*aResult = avail;
return NS_OK;
}
// Let's propagate the last error message.
mStatus = rv;
return rv;
}
NS_IMETHODIMP
@ -303,7 +376,7 @@ nsMultiplexInputStream::Read(char* aBuf, uint32_t aCount, uint32_t* aResult)
uint32_t len = mStreams.Length();
while (mCurrentStream < len && aCount) {
uint32_t read;
rv = mStreams[mCurrentStream]->Read(aBuf, aCount, &read);
rv = mStreams[mCurrentStream].mStream->Read(aBuf, aCount, &read);
// XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
// (This is a bug in those stream implementations)
@ -356,7 +429,8 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t len = mStreams.Length();
while (mCurrentStream < len && aCount) {
uint32_t read;
rv = mStreams[mCurrentStream]->ReadSegments(ReadSegCb, &state, aCount, &read);
rv = mStreams[mCurrentStream].mStream->ReadSegments(ReadSegCb, &state,
aCount, &read);
// XXX some streams return NS_BASE_STREAM_CLOSED to indicate EOF.
// (This is a bug in those stream implementations)
@ -416,14 +490,11 @@ nsMultiplexInputStream::IsNonBlocking(bool* aNonBlocking)
uint32_t len = mStreams.Length();
if (len == 0) {
// Claim to be non-blocking, since we won't block the caller.
// On the other hand we'll never return NS_BASE_STREAM_WOULD_BLOCK,
// so maybe we should claim to be blocking? It probably doesn't
// matter in practice.
*aNonBlocking = true;
return NS_OK;
}
for (uint32_t i = 0; i < len; ++i) {
nsresult rv = mStreams[i]->IsNonBlocking(aNonBlocking);
nsresult rv = mStreams[i].mStream->IsNonBlocking(aNonBlocking);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -457,8 +528,7 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
mCurrentStream = 0;
}
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
nsCOMPtr<nsISeekableStream> stream = mStreams[i].mSeekableStream;
if (!stream) {
return NS_ERROR_FAILURE;
}
@ -540,9 +610,6 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
if (aWhence == NS_SEEK_CUR && aOffset > 0) {
int64_t remaining = aOffset;
for (uint32_t i = mCurrentStream; remaining && i < mStreams.Length(); ++i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
uint64_t avail;
rv = AvailableMaybeSeek(mStreams[i], &avail);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -551,7 +618,7 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
int64_t seek = XPCOM_MIN((int64_t)avail, remaining);
rv = stream->Seek(NS_SEEK_CUR, seek);
rv = mStreams[i].mSeekableStream->Seek(NS_SEEK_CUR, seek);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -568,18 +635,15 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
if (aWhence == NS_SEEK_CUR && aOffset < 0) {
int64_t remaining = -aOffset;
for (uint32_t i = mCurrentStream; remaining && i != (uint32_t)-1; --i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
int64_t pos;
rv = TellMaybeSeek(stream, &pos);
rv = TellMaybeSeek(mStreams[i].mSeekableStream, &pos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
int64_t seek = XPCOM_MIN(pos, remaining);
rv = stream->Seek(NS_SEEK_CUR, -seek);
rv = mStreams[i].mSeekableStream->Seek(NS_SEEK_CUR, -seek);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -605,8 +669,7 @@ nsMultiplexInputStream::Seek(int32_t aWhence, int64_t aOffset)
}
int64_t remaining = aOffset;
for (uint32_t i = mStreams.Length() - 1; i != (uint32_t)-1; --i) {
nsCOMPtr<nsISeekableStream> stream =
do_QueryInterface(mStreams[i]);
nsCOMPtr<nsISeekableStream> stream = mStreams[i].mSeekableStream;
// See if all remaining streams should be seeked to end
if (remaining == 0) {
@ -696,13 +759,12 @@ nsMultiplexInputStream::Tell(int64_t* aResult)
uint32_t i, last;
last = mStartedReadingCurrent ? mCurrentStream + 1 : mCurrentStream;
for (i = 0; i < last; ++i) {
nsCOMPtr<nsISeekableStream> stream = do_QueryInterface(mStreams[i]);
if (NS_WARN_IF(!stream)) {
if (NS_WARN_IF(!mStreams[i].mSeekableStream)) {
return NS_ERROR_NO_INTERFACE;
}
int64_t pos;
rv = TellMaybeSeek(stream, &pos);
rv = TellMaybeSeek(mStreams[i].mSeekableStream, &pos);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -864,10 +926,8 @@ nsMultiplexInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
nsTArray<nsCOMPtr<nsIAsyncInputStream>> asyncStreams;
for (uint32_t i = mCurrentStream; i < mStreams.Length(); ++i) {
nsCOMPtr<nsIAsyncInputStream> asyncStream =
do_QueryInterface(mStreams.SafeElementAt(i, nullptr));
if (asyncStream) {
asyncStreams.AppendElement(asyncStream);
if (mStreams[i].mAsyncStream) {
asyncStreams.AppendElement(mStreams[i].mAsyncStream);
}
}
@ -935,7 +995,7 @@ nsMultiplexInputStream::Serialize(InputStreamParams& aParams,
streams.SetCapacity(streamCount);
for (uint32_t index = 0; index < streamCount; index++) {
InputStreamParams childStreamParams;
InputStreamHelper::SerializeInputStream(mStreams[index],
InputStreamHelper::SerializeInputStream(mStreams[index].mStream,
childStreamParams,
aFileDescriptors);
@ -997,7 +1057,8 @@ nsMultiplexInputStream::ExpectedSerializedLength()
uint64_t expectedLength = 0;
uint32_t streamCount = mStreams.Length();
for (uint32_t index = 0; index < streamCount; index++) {
nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStreams[index]);
nsCOMPtr<nsIIPCSerializableInputStream> stream =
do_QueryInterface(mStreams[index].mStream);
if (!stream) {
continue;
}
@ -1024,7 +1085,8 @@ nsMultiplexInputStream::GetCloneable(bool* aCloneable)
uint32_t len = mStreams.Length();
for (uint32_t i = 0; i < len; ++i) {
nsCOMPtr<nsICloneableInputStream> cis = do_QueryInterface(mStreams[i]);
nsCOMPtr<nsICloneableInputStream> cis =
do_QueryInterface(mStreams[i].mStream);
if (!cis || !cis->GetCloneable()) {
*aCloneable = false;
return NS_OK;
@ -1051,7 +1113,8 @@ nsMultiplexInputStream::Clone(nsIInputStream** aClone)
nsresult rv;
uint32_t len = mStreams.Length();
for (uint32_t i = 0; i < len; ++i) {
nsCOMPtr<nsICloneableInputStream> substream = do_QueryInterface(mStreams[i]);
nsCOMPtr<nsICloneableInputStream> substream =
do_QueryInterface(mStreams[i].mStream);
if (NS_WARN_IF(!substream)) {
return NS_ERROR_FAILURE;
}
@ -1072,30 +1135,32 @@ nsMultiplexInputStream::Clone(nsIInputStream** aClone)
return NS_OK;
}
#define MAYBE_UPDATE_VALUE(x, y) \
{ \
nsCOMPtr<y> substream = do_QueryInterface(aStream); \
if (substream) { \
if (aCount == 1) { \
++x; \
} else if (x > 0) { \
--x; \
} else { \
MOZ_CRASH("A nsIInputStream changed QI map when stored in a nsMultiplexInputStream!"); \
} \
} \
#define MAYBE_UPDATE_VALUE_REAL(x, y) \
if (y) { \
if (aCount == 1) { \
++x; \
} else if (x > 0) { \
--x; \
} else { \
MOZ_CRASH("A nsIInputStream changed QI map when stored in a nsMultiplexInputStream!"); \
} \
}
#define MAYBE_UPDATE_VALUE(x, y) \
{ \
nsCOMPtr<y> substream = do_QueryInterface(aStream.mStream); \
MAYBE_UPDATE_VALUE_REAL(x, substream) \
}
void
nsMultiplexInputStream::UpdateQIMap(nsIInputStream* aStream, int32_t aCount)
nsMultiplexInputStream::UpdateQIMap(StreamData& aStream, int32_t aCount)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(aCount == -1 || aCount == 1);
MAYBE_UPDATE_VALUE(mSeekableStreams, nsISeekableStream);
MAYBE_UPDATE_VALUE(mIPCSerializableStreams, nsIIPCSerializableInputStream);
MAYBE_UPDATE_VALUE(mCloneableStreams, nsICloneableInputStream);
MAYBE_UPDATE_VALUE(mAsyncInputStreams, nsIAsyncInputStream);
MAYBE_UPDATE_VALUE_REAL(mSeekableStreams, aStream.mSeekableStream)
MAYBE_UPDATE_VALUE(mIPCSerializableStreams, nsIIPCSerializableInputStream)
MAYBE_UPDATE_VALUE(mCloneableStreams, nsICloneableInputStream)
MAYBE_UPDATE_VALUE_REAL(mAsyncInputStreams, aStream.mAsyncStream)
}
#undef MAYBE_UPDATE_VALUE

Просмотреть файл

@ -213,3 +213,184 @@ TEST(TestMultiplexInputStream, AsyncWait_withEventTarget_closureOnly) {
MOZ_ALWAYS_TRUE(mozilla::SpinEventLoopUntil([&]() { return cb->Called(); }));
ASSERT_TRUE(cb->Called());
}
class ClosedStream final : public nsIInputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
ClosedStream() {}
NS_IMETHOD
Available(uint64_t* aLength) override
{
return NS_BASE_STREAM_CLOSED;
}
NS_IMETHOD
Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
{
MOZ_CRASH("This should not be called!");
return NS_OK;
}
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t *aResult) override
{
MOZ_CRASH("This should not be called!");
return NS_OK;
}
NS_IMETHOD
Close() override { return NS_OK; }
NS_IMETHOD
IsNonBlocking(bool* aNonBlocking) override
{
*aNonBlocking = true;
return NS_OK;
}
private:
~ClosedStream() = default;
};
NS_IMPL_ISUPPORTS(ClosedStream, nsIInputStream)
class AsyncStream final : public nsIAsyncInputStream
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit AsyncStream(int64_t aSize) : mState(eBlocked), mSize(aSize) {}
void
Unblock()
{
mState = eUnblocked;
}
NS_IMETHOD
Available(uint64_t* aLength) override
{
*aLength = mState == eBlocked ? 0 : mSize;
return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_OK;
}
NS_IMETHOD
Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount) override
{
MOZ_CRASH("This should not be called!");
return NS_OK;
}
NS_IMETHOD
ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
uint32_t aCount, uint32_t *aResult) override
{
MOZ_CRASH("This should not be called!");
return NS_OK;
}
NS_IMETHOD
Close() override
{
mState = eClosed;
return NS_OK;
}
NS_IMETHOD
IsNonBlocking(bool* aNonBlocking) override
{
*aNonBlocking = true;
return NS_OK;
}
NS_IMETHOD
AsyncWait(nsIInputStreamCallback* aCallback,
uint32_t aFlags, uint32_t aRequestedCount,
nsIEventTarget* aEventTarget) override
{
MOZ_CRASH("This should not be called!");
return NS_OK;
}
NS_IMETHOD
CloseWithStatus(nsresult aStatus) override
{
return NS_OK;
}
private:
~AsyncStream() = default;
enum {
eBlocked,
eUnblocked,
eClosed
} mState;
uint64_t mSize;
};
NS_IMPL_ISUPPORTS(AsyncStream, nsIInputStream, nsIAsyncInputStream)
TEST(TestMultiplexInputStream, Available) {
nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
nsCOMPtr<nsIInputStream> s = do_QueryInterface(multiplexStream);
ASSERT_TRUE(!!s);
nsCOMPtr<nsIAsyncInputStream> as = do_QueryInterface(multiplexStream);
ASSERT_TRUE(!as);
uint64_t length;
// The stream returns NS_BASE_STREAM_CLOSED if there are no substreams.
nsresult rv = s->Available(&length);
ASSERT_EQ(NS_BASE_STREAM_CLOSED, rv);
rv = multiplexStream->AppendStream(new ClosedStream());
ASSERT_EQ(NS_OK, rv);
uint64_t asyncSize = 2;
RefPtr<AsyncStream> asyncStream = new AsyncStream(2);
rv = multiplexStream->AppendStream(asyncStream);
ASSERT_EQ(NS_OK, rv);
nsCString buffer;
buffer.Assign("World!!!");
nsCOMPtr<nsIInputStream> stringStream;
rv = NS_NewCStringInputStream(getter_AddRefs(stringStream), buffer);
ASSERT_EQ(NS_OK, rv);
rv = multiplexStream->AppendStream(stringStream);
ASSERT_EQ(NS_OK, rv);
// Now we are async.
as = do_QueryInterface(multiplexStream);
ASSERT_TRUE(!!as);
// Available should skip the closed stream and return 0 because the
// asyncStream returns 0 and it's async.
rv = s->Available(&length);
ASSERT_EQ(NS_OK, rv);
ASSERT_EQ((uint64_t)0, length);
asyncStream->Unblock();
// Now we should return only the size of the async stream because we don't
// know when this is completed.
rv = s->Available(&length);
ASSERT_EQ(NS_OK, rv);
ASSERT_EQ(asyncSize, length);
asyncStream->Close();
rv = s->Available(&length);
ASSERT_EQ(NS_OK, rv);
ASSERT_EQ(buffer.Length(), length);
}