merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-12-18 15:23:39 +01:00
Родитель d402cbdb96 0e9f21c88c
Коммит 71603d10db
202 изменённых файлов: 5120 добавлений и 2680 удалений

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

@ -15,8 +15,8 @@
"unpack": true
},
{
"size": 73029932,
"digest": "ef1818acf065838dcb72554e521f9fd7098f0a3690cb6a3106d7bf18f46c342bfdd5a2b7d86e92ee3ddb9e478380343e58ecf8fd242807b8881a2d53fbec5ab3",
"size": 193213220,
"digest": "58b8ebd8de923117831dcbba71172a53e26c25bd16c2b2bb363a1254f2cd4e87f95e2c5f41e2dce10e18e43a17e0a395637c9ddcbf1e27673582792f9095c62e",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true

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

@ -14,17 +14,17 @@
"unpack": true
},
{
"size": 193213220,
"digest": "58b8ebd8de923117831dcbba71172a53e26c25bd16c2b2bb363a1254f2cd4e87f95e2c5f41e2dce10e18e43a17e0a395637c9ddcbf1e27673582792f9095c62e",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true
},
{
"size": 167175,
"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
"algorithm": "sha512",
"filename": "sccache.tar.bz2",
"unpack": true
},
{
"size": 73029932,
"digest": "ef1818acf065838dcb72554e521f9fd7098f0a3690cb6a3106d7bf18f46c342bfdd5a2b7d86e92ee3ddb9e478380343e58ecf8fd242807b8881a2d53fbec5ab3",
"algorithm": "sha512",
"filename": "rustc.tar.xz",
"unpack": true
}
]

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

@ -10,8 +10,8 @@
"unpack": true
},
{
"size": 128301120,
"digest": "d2d71103a6cec84b150b8f08bfef2682aa713c2d6eadce584f79836ef27ba4380ffb444165d999b79605f18ad165641a7a8cc0e04a201675ad5f655a59adbda9",
"size": 215952362,
"digest": "5e9825dbe83b2a157879076da70fc5c989a1638e30d3b14a9901b166db09013c356a9dc4eaf6c16209a1832d9cb1c67ca869e9b9003cab8355a7f03b3dc08775",
"algorithm": "sha512",
"filename": "rustc.tar.bz2",
"unpack": true

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

@ -72,7 +72,7 @@ SEARCHPLUGINS_NAMES = $(shell cat $(call MERGE_FILE,/searchplugins/list.txt)) dd
SEARCHPLUGINS_FILENAMES = $(subst :hidden,,$(SEARCHPLUGINS_NAMES))
SEARCHPLUGINS_PATH := .deps/generated_$(AB_CD)
SEARCHPLUGINS_TARGET := libs searchplugins
SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(info Missing searchplugin: $(plugin))))
SEARCHPLUGINS := $(foreach plugin,$(addsuffix .xml,$(SEARCHPLUGINS_FILENAMES)),$(or $(wildcard $(call EN_US_OR_L10N_FILE,searchplugins/$(plugin))),$(warning Missing searchplugin: $(plugin))))
# Some locale-specific search plugins may have preprocessor directives, but the
# default en-US ones do not.
SEARCHPLUGINS_FLAGS := --silence-missing-directive-warnings

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

@ -25,10 +25,10 @@ AC_DEFUN([MOZ_RUST_SUPPORT], [
fi
if test -n "$MOZ_RUST" && test -z "$_RUSTC_MAJOR_VERSION" -o \
"$_RUSTC_MAJOR_VERSION" -lt 1 -o \
\( "$_RUSTC_MAJOR_VERSION" -eq 1 -a "$_RUSTC_MINOR_VERSION" -lt 4 \); then
\( "$_RUSTC_MAJOR_VERSION" -eq 1 -a "$_RUSTC_MINOR_VERSION" -lt 5 \); then
AC_MSG_ERROR([Rust compiler ${RUSTC_VERSION} is too old.
To compile Rust language sources please install at least
version 1.4 of the 'rustc' toolchain and make sure it is
version 1.5 of the 'rustc' toolchain and make sure it is
first in your path.
You can verify this by typing 'rustc --version'.])
fi

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

@ -945,7 +945,7 @@ ifdef MOZ_RUST
# in the target's LIBS.
$(RSOBJS):
$(REPORT_BUILD)
$(RUSTC) $(RUSTFLAGS) --crate-type staticlib -o $(call mk_libname,$<) $(_VPATH_SRCS)
$(RUSTC) $(RUSTFLAGS) --crate-type staticlib --emit dep-info=$(MDDEPDIR)/$(call mk_libname,$<).pp,link=$(call mk_libname,$<) $(_VPATH_SRCS)
endif
$(SOBJS):

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

@ -6405,14 +6405,6 @@ AC_SUBST(TAR)
AC_CHECK_PROGS(WGET, wget, "")
AC_SUBST(WGET)
dnl ========================================================
dnl Signing
dnl ========================================================
if test -n "$MOZ_SIGN_CMD"; then
AC_DEFINE(MOZ_SIGNING)
fi
dnl ========================================================
dnl Maintenance Service
dnl ========================================================

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

@ -4,25 +4,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/devtools/DominatorTree.h"
#include "js/Debug.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/dom/DominatorTreeBinding.h"
namespace mozilla {
namespace devtools {
static MallocSizeOf
getCurrentThreadDebuggerMallocSizeOf()
{
auto ccrt = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(ccrt);
auto rt = ccrt->Runtime();
MOZ_ASSERT(rt);
auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
MOZ_ASSERT(mallocSizeOf);
return mallocSizeOf;
}
dom::Nullable<uint64_t>
DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv)
{
@ -31,7 +17,7 @@ DominatorTree::GetRetainedSize(uint64_t aNodeId, ErrorResult& aRv)
if (node.isNothing())
return dom::Nullable<uint64_t>();
auto mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf();
auto mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
JS::ubi::Node::Size size = 0;
if (!mDominatorTree.getRetainedSize(*node, mallocSizeOf, size)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
@ -83,7 +69,7 @@ DominatorTree::GetImmediatelyDominated(uint64_t aNodeId,
return;
// Get all immediately dominated nodes and their retained sizes.
MallocSizeOf mallocSizeOf = getCurrentThreadDebuggerMallocSizeOf();
MallocSizeOf mallocSizeOf = GetCurrentThreadDebuggerMallocSizeOf();
Maybe<JS::ubi::DominatorTree::DominatedSetRange> range = mDominatorTree.getDominatedSet(*node);
MOZ_ASSERT(range.isSome(), "The node should be known, since we got it from the heap snapshot.");
size_t length = range->length();

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

@ -55,6 +55,18 @@ using ::google::protobuf::io::ZeroCopyInputStream;
using JS::ubi::AtomOrTwoByteChars;
MallocSizeOf
GetCurrentThreadDebuggerMallocSizeOf()
{
auto ccrt = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(ccrt);
auto rt = ccrt->Runtime();
MOZ_ASSERT(rt);
auto mallocSizeOf = JS::dbg::GetDebuggerMallocSizeOf(rt);
MOZ_ASSERT(mallocSizeOf);
return mallocSizeOf;
}
/*** Cycle Collection Boilerplate *****************************************************************/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HeapSnapshot, mParent)
@ -482,7 +494,7 @@ HeapSnapshot::TakeCensus(JSContext* cx, JS::HandleObject options,
return;
}
JS::ubi::CensusHandler handler(census, rootCount);
JS::ubi::CensusHandler handler(census, rootCount, GetCurrentThreadDebuggerMallocSizeOf());
{
JS::AutoCheckCannotGC nogc;
@ -504,7 +516,7 @@ HeapSnapshot::TakeCensus(JSContext* cx, JS::HandleObject options,
}
}
if (NS_WARN_IF(!handler.report(rval))) {
if (NS_WARN_IF(!handler.report(cx, rval))) {
rv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}

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

@ -221,6 +221,9 @@ WriteHeapGraph(JSContext* cx,
ignoreNodeCount, ignoreEdgeCount);
}
// Get the mozilla::MallocSizeOf for the current thread's JSRuntime.
MallocSizeOf GetCurrentThreadDebuggerMallocSizeOf();
} // namespace devtools
} // namespace mozilla

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

@ -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"
@ -56,7 +56,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileReader,
DOMEventTargetHelper)
tmp->mResultArrayBuffer = nullptr;
tmp->Shutdown();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mBlob)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
@ -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,14 +110,18 @@ 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);
}
FileReader::~FileReader()
{
FreeFileData();
mResultArrayBuffer = nullptr;
Shutdown();
DropJSObjects(this);
}
@ -111,9 +130,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 +242,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 +293,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,7 +328,7 @@ 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,
mAsyncStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
&bytesRead);
NS_ASSERTION(bytesRead == aCount, "failed to read data");
}
@ -287,7 +344,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 +418,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 +521,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 +605,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 +700,56 @@ 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) {
Shutdown();
}
return true;
}
void
FileReader::Shutdown()
{
FreeFileData();
mResultArrayBuffer = nullptr;
if (mAsyncStream) {
mAsyncStream->Close();
mAsyncStream = nullptr;
}
if (mWorkerPrivate && mBusyCount != 0) {
mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
mWorkerPrivate = nullptr;
mBusyCount = 0;
}
}
} // 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,11 @@ private:
mDataLen = 0;
}
nsresult IncreaseBusyCounter();
void DecreaseBusyCounter();
void Shutdown();
char *mFileData;
RefPtr<Blob> mBlob;
nsCString mCharset;
@ -166,6 +185,13 @@ private:
uint64_t mTotal;
uint64_t mTransferred;
nsCOMPtr<nsIEventTarget> mTarget;
uint64_t mBusyCount;
// Kept alive with a WorkerFeature.
workers::WorkerPrivate* mWorkerPrivate;
};
} // dom namespace

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

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ImageEncoder.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/DataSurfaceHelpers.h"
@ -13,7 +14,10 @@
#include "mozilla/SyncRunnable.h"
#include "mozilla/unused.h"
#include "gfxUtils.h"
#include "nsIThreadPool.h"
#include "nsNetUtil.h"
#include "nsXPCOMCIDInternal.h"
#include "WorkerPrivate.h"
using namespace mozilla::gfx;
@ -69,27 +73,28 @@ GetBRGADataSourceSurfaceSync(already_AddRefed<layers::Image> aImage)
return helper->GetDataSurfaceSafe();
}
class EncodingCompleteEvent : public nsRunnable
class EncodingCompleteEvent : public nsCancelableRunnable
{
virtual ~EncodingCompleteEvent() {}
public:
NS_DECL_ISUPPORTS_INHERITED
EncodingCompleteEvent(nsIThread* aEncoderThread,
EncodeCompleteCallback* aEncodeCompleteCallback)
explicit EncodingCompleteEvent(EncodeCompleteCallback* aEncodeCompleteCallback)
: mImgSize(0)
, mType()
, mImgData(nullptr)
, mEncoderThread(aEncoderThread)
, mEncodeCompleteCallback(aEncodeCompleteCallback)
, mFailed(false)
{}
{
if (!NS_IsMainThread() && workers::GetCurrentThreadWorkerPrivate()) {
mCreationThread = NS_GetCurrentThread();
} else {
NS_GetMainThread(getter_AddRefs(mCreationThread));
}
}
NS_IMETHOD Run() override
{
nsresult rv = NS_OK;
MOZ_ASSERT(NS_IsMainThread());
if (!mFailed) {
// The correct parentObject has to be set by the mEncodeCompleteCallback.
@ -102,7 +107,6 @@ public:
mEncodeCompleteCallback = nullptr;
mEncoderThread->Shutdown();
return rv;
}
@ -118,17 +122,20 @@ public:
mFailed = true;
}
nsIThread* GetCreationThread()
{
return mCreationThread;
}
private:
uint64_t mImgSize;
nsAutoString mType;
void* mImgData;
nsCOMPtr<nsIThread> mEncoderThread;
nsCOMPtr<nsIThread> mCreationThread;
RefPtr<EncodeCompleteCallback> mEncodeCompleteCallback;
bool mFailed;
};
NS_IMPL_ISUPPORTS_INHERITED0(EncodingCompleteEvent, nsRunnable);
class EncodingRunnable : public nsRunnable
{
virtual ~EncodingRunnable() {}
@ -207,7 +214,8 @@ public:
} else {
mEncodingCompleteEvent->SetMembers(imgData, imgSize, mType);
}
rv = NS_DispatchToMainThread(mEncodingCompleteEvent);
rv = mEncodingCompleteEvent->GetCreationThread()->
Dispatch(mEncodingCompleteEvent, nsIThread::DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
// Better to leak than to crash.
Unused << mEncodingCompleteEvent.forget();
@ -231,6 +239,8 @@ private:
NS_IMPL_ISUPPORTS_INHERITED0(EncodingRunnable, nsRunnable);
StaticRefPtr<nsIThreadPool> ImageEncoder::sThreadPool;
/* static */
nsresult
ImageEncoder::ExtractData(nsAString& aType,
@ -262,12 +272,13 @@ ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
nsCOMPtr<nsIThread> encoderThread;
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = EnsureThreadPool();
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<EncodingCompleteEvent> completeEvent =
new EncodingCompleteEvent(encoderThread, aEncodeCallback);
new EncodingCompleteEvent(aEncodeCallback);
nsIntSize size(aImage->GetSize().width, aImage->GetSize().height);
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
@ -279,7 +290,7 @@ ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
imgIEncoder::INPUT_FORMAT_HOSTARGB,
size,
aUsingCustomOptions);
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
}
/* static */
@ -297,12 +308,13 @@ ImageEncoder::ExtractDataAsync(nsAString& aType,
return NS_IMAGELIB_ERROR_NO_ENCODER;
}
nsCOMPtr<nsIThread> encoderThread;
nsresult rv = NS_NewThread(getter_AddRefs(encoderThread), nullptr);
NS_ENSURE_SUCCESS(rv, rv);
nsresult rv = EnsureThreadPool();
if (NS_FAILED(rv)) {
return rv;
}
RefPtr<EncodingCompleteEvent> completeEvent =
new EncodingCompleteEvent(encoderThread, aEncodeCallback);
new EncodingCompleteEvent(aEncodeCallback);
nsCOMPtr<nsIRunnable> event = new EncodingRunnable(aType,
aOptions,
@ -313,7 +325,7 @@ ImageEncoder::ExtractDataAsync(nsAString& aType,
aFormat,
aSize,
aUsingCustomOptions);
return encoderThread->Dispatch(event, NS_DISPATCH_NORMAL);
return sThreadPool->Dispatch(event, NS_DISPATCH_NORMAL);
}
/*static*/ nsresult
@ -479,5 +491,48 @@ ImageEncoder::GetImageEncoder(nsAString& aType)
return encoder.forget();
}
/* static */
nsresult
ImageEncoder::EnsureThreadPool()
{
if (!sThreadPool) {
nsCOMPtr<nsIThreadPool> threadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
sThreadPool = threadPool;
if (!NS_IsMainThread()) {
NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void {
ClearOnShutdown(&sThreadPool);
}));
} else {
ClearOnShutdown(&sThreadPool);
}
const uint32_t kThreadLimit = 2;
const uint32_t kIdleThreadLimit = 1;
const uint32_t kIdleThreadTimeoutMs = 30000;
nsresult rv = sThreadPool->SetName(NS_LITERAL_CSTRING("EncodingRunnable"));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = sThreadPool->SetThreadLimit(kThreadLimit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = sThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = sThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

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

@ -16,6 +16,7 @@
#include "nsSize.h"
class nsICanvasRenderingContextInternal;
class nsIThreadPool;
namespace mozilla {
@ -107,6 +108,11 @@ private:
// undefined in this case.
static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType);
static nsresult EnsureThreadPool();
// Thread pool for dispatching EncodingRunnable.
static StaticRefPtr<nsIThreadPool> sThreadPool;
friend class EncodingRunnable;
};

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

@ -28,6 +28,7 @@
#include "mozilla/dom/SubtleCryptoBinding.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/dom/WebCryptoCommon.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
@ -306,7 +307,7 @@ StructuredCloneHolder::Read(nsISupports* aParent,
// If we are tranferring something, we cannot call 'Read()' more than once.
if (mSupportsTransferring) {
mBlobImplArray.Clear();
mClonedImages.Clear();
mClonedSurfaces.Clear();
Clear();
}
}
@ -976,7 +977,7 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx,
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
// aIndex is the index of the cloned image.
return ImageBitmap::ReadStructuredClone(aCx, aReader,
parent, GetImages(), aIndex);
parent, GetSurfaces(), aIndex);
}
return ReadFullySerializableObjects(aCx, aReader, aTag);
@ -1021,7 +1022,7 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
ImageBitmap* imageBitmap = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(ImageBitmap, aObj, imageBitmap))) {
return ImageBitmap::WriteStructuredClone(aWriter,
GetImages(),
GetSurfaces(),
imageBitmap);
}
}
@ -1072,7 +1073,8 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
MOZ_ASSERT(aContent);
OffscreenCanvasCloneData* data =
static_cast<OffscreenCanvasCloneData*>(aContent);
RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(data);
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
RefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(parent, data);
delete data;
JS::Rooted<JS::Value> value(aCx);
@ -1085,6 +1087,26 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
return true;
}
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
mSupportedContext == SameProcessDifferentThread);
MOZ_ASSERT(aContent);
ImageBitmapCloneData* data =
static_cast<ImageBitmapCloneData*>(aContent);
nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent);
RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data);
delete data;
JS::Rooted<JS::Value> value(aCx);
if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) {
JS_ClearPendingException(aCx);
return false;
}
aReturnObject.set(&value.toObject());
return true;
}
return false;
}
@ -1133,6 +1155,21 @@ StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
return true;
}
ImageBitmap* bitmap = nullptr;
rv = UNWRAP_OBJECT(ImageBitmap, aObj, bitmap);
if (NS_SUCCEEDED(rv)) {
MOZ_ASSERT(bitmap);
*aExtraData = 0;
*aTag = SCTAG_DOM_IMAGEBITMAP;
*aOwnership = JS::SCTAG_TMO_CUSTOM;
*aContent = bitmap->ToCloneData();
MOZ_ASSERT(*aContent);
bitmap->Close();
return true;
}
}
}
@ -1163,6 +1200,16 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
delete data;
return;
}
if (aTag == SCTAG_DOM_IMAGEBITMAP) {
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
mSupportedContext == SameProcessDifferentThread);
MOZ_ASSERT(aContent);
ImageBitmapCloneData* data =
static_cast<ImageBitmapCloneData*>(aContent);
delete data;
return;
}
}
} // dom namespace

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

@ -22,6 +22,10 @@ namespace layers {
class Image;
}
namespace gfx {
class DataSourceSurface;
}
namespace dom {
class StructuredCloneHolderBase
@ -181,7 +185,7 @@ public:
bool HasClonedDOMObjects() const
{
return !mBlobImplArray.IsEmpty() ||
!mClonedImages.IsEmpty();
!mClonedSurfaces.IsEmpty();
}
nsTArray<RefPtr<BlobImpl>>& BlobImpls()
@ -212,9 +216,9 @@ public:
return mPortIdentifiers;
}
nsTArray<RefPtr<layers::Image>>& GetImages()
nsTArray<RefPtr<gfx::DataSourceSurface>>& GetSurfaces()
{
return mClonedImages;
return mClonedSurfaces;
}
// Implementations of the virtual methods to allow cloning of objects which
@ -291,10 +295,10 @@ protected:
nsTArray<RefPtr<BlobImpl>> mBlobImplArray;
// This is used for sharing the backend of ImageBitmaps.
// The layers::Image object must be thread-safely reference-counted.
// The layers::Image object will not be written ever via any ImageBitmap
// The DataSourceSurface object must be thread-safely reference-counted.
// The DataSourceSurface object will not be written ever via any ImageBitmap
// instance, so no race condition will occur.
nsTArray<RefPtr<layers::Image>> mClonedImages;
nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces;
// This raw pointer is only set within ::Read() and is unset by the end.
nsISupports* MOZ_NON_OWNING_REF mParent;

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

@ -6775,66 +6775,6 @@ nsContentUtils::HaveEqualPrincipals(nsIDocument* aDoc1, nsIDocument* aDoc2)
return principalsEqual;
}
static void
CheckForWindowedPlugins(nsISupports* aSupports, void* aResult)
{
nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
if (!content || !content->IsInDoc()) {
return;
}
nsCOMPtr<nsIObjectLoadingContent> olc(do_QueryInterface(content));
if (!olc) {
return;
}
RefPtr<nsNPAPIPluginInstance> plugin;
olc->GetPluginInstance(getter_AddRefs(plugin));
if (!plugin) {
return;
}
bool isWindowless = false;
nsresult res = plugin->IsWindowless(&isWindowless);
if (NS_SUCCEEDED(res) && !isWindowless) {
*static_cast<bool*>(aResult) = true;
}
}
static bool
DocTreeContainsWindowedPlugins(nsIDocument* aDoc, void* aResult)
{
if (!nsContentUtils::IsChromeDoc(aDoc)) {
aDoc->EnumerateActivityObservers(CheckForWindowedPlugins, aResult);
}
if (*static_cast<bool*>(aResult)) {
// Return false to stop iteration, we found a windowed plugin.
return false;
}
aDoc->EnumerateSubDocuments(DocTreeContainsWindowedPlugins, aResult);
// Return false to stop iteration if we found a windowed plugin in
// the sub documents.
return !*static_cast<bool*>(aResult);
}
/* static */
bool
nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc)
{
#ifdef XP_MACOSX
// We control dispatch to all mac plugins.
return false;
#endif
bool result = false;
// Find the top of the document's branch, the child of the chrome document.
nsIDocument* doc = aDoc;
nsIDocument* parent = nullptr;
while (doc && (parent = doc->GetParentDocument()) && !IsChromeDoc(parent)) {
doc = parent;
}
DocTreeContainsWindowedPlugins(doc, &result);
return result;
}
/* static */
bool
nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
@ -6842,10 +6782,30 @@ nsContentUtils::HasPluginWithUncontrolledEventDispatch(nsIContent* aContent)
#ifdef XP_MACOSX
// We control dispatch to all mac plugins.
return false;
#else
if (!aContent || !aContent->IsInDoc()) {
return false;
}
nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(aContent);
if (!olc) {
return false;
}
RefPtr<nsNPAPIPluginInstance> plugin;
olc->GetPluginInstance(getter_AddRefs(plugin));
if (!plugin) {
return false;
}
bool isWindowless = false;
nsresult res = plugin->IsWindowless(&isWindowless);
if (NS_FAILED(res)) {
return false;
}
return !isWindowless;
#endif
bool result = false;
CheckForWindowedPlugins(aContent, &result);
return result;
}
/* static */

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

@ -2059,14 +2059,6 @@ public:
return sPrivacyResistFingerprinting;
}
/**
* Returns true if the doc tree branch which contains aDoc contains any
* plugins which we don't control event dispatch for, i.e. do any plugins
* in the same tab as this document receive key events outside of our
* control? This always returns false on MacOSX.
*/
static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
/**
* Return true if this doc is controlled by a ServiceWorker.
*/

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

@ -7668,7 +7668,6 @@ nsGlobalWindow::Open(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
getter_AddRefs(window));
if (NS_SUCCEEDED(rv) && window) {
@ -7689,7 +7688,6 @@ nsGlobalWindow::OpenJS(const nsAString& aUrl, const nsAString& aName,
true, // aDoJSFixups
true, // aNavigate
nullptr, nullptr, // No args
GetPrincipal(), // aCalleePrincipal
nsContentUtils::GetCurrentJSContext(), // aJSCallerContext
_retval);
}
@ -7709,7 +7707,6 @@ nsGlobalWindow::OpenDialog(const nsAString& aUrl, const nsAString& aName,
false, // aDoJSFixups
true, // aNavigate
nullptr, aExtraArgument, // Arguments
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
_retval);
}
@ -7729,7 +7726,6 @@ nsGlobalWindow::OpenNoNavigate(const nsAString& aUrl,
false, // aDoJSFixups
false, // aNavigate
nullptr, nullptr, // No args
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
_retval);
@ -7759,7 +7755,6 @@ nsGlobalWindow::OpenDialogOuter(JSContext* aCx, const nsAString& aUrl,
false, // aDoJSFixups
true, // aNavigate
argvArray, nullptr, // Arguments
GetPrincipal(), // aCalleePrincipal
aCx, // aJSCallerContext
getter_AddRefs(dialog));
return dialog.forget();
@ -8839,7 +8834,6 @@ nsGlobalWindow::ShowModalDialogOuter(const nsAString& aUrl, nsIVariant* aArgumen
true, // aDoJSFixups
true, // aNavigate
nullptr, argHolder, // args
GetPrincipal(), // aCalleePrincipal
nullptr, // aJSCallerContext
getter_AddRefs(dlgWin));
nsContentUtils::SetMicroTaskLevel(oldMicroTaskLevel);
@ -11270,7 +11264,6 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName,
bool aDoJSFixups, bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsIPrincipal *aCalleePrincipal,
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn)
{

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

@ -1392,7 +1392,6 @@ private:
bool aNavigate,
nsIArray *argv,
nsISupports *aExtraArgument,
nsIPrincipal *aCalleePrincipal,
JSContext *aJSCallerContext,
nsIDOMWindow **aReturn);

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

@ -5219,19 +5219,17 @@ CanvasRenderingContext2D::GetImageDataArray(JSContext* aCx,
IntRect dstWriteRect = srcReadRect;
dstWriteRect.MoveBy(-aX, -aY);
uint8_t* src;
uint32_t srcStride;
if (readback) {
srcStride = rawData.mStride;
src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
}
JS::AutoCheckCannotGC nogc;
bool isShared;
uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
MOZ_ASSERT(!isShared); // Should not happen, data was created above
if (!readback) {
uint8_t* src;
uint32_t srcStride;
if (readback) {
srcStride = rawData.mStride;
src = rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
} else {
src = data;
srcStride = aWidth * 4;
}
@ -5579,9 +5577,9 @@ CanvasRenderingContext2D::GetBufferProvider(LayerManager* aManager)
return mBufferProvider;
}
already_AddRefed<CanvasLayer>
already_AddRefed<Layer>
CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
Layer *aOldLayer,
LayerManager *aManager)
{
if (mOpaque || mIsSkiaGL) {
@ -5624,8 +5622,10 @@ CanvasRenderingContext2D::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
data.mBufferProvider = provider;
}
if (userData && userData->IsForContext(this) && aOldLayer->IsDataValid(data)) {
RefPtr<CanvasLayer> ret = aOldLayer;
if (userData &&
userData->IsForContext(this) &&
static_cast<CanvasLayer*>(aOldLayer)->IsDataValid(data)) {
RefPtr<Layer> ret = aOldLayer;
return ret.forget();
}
}

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

@ -459,8 +459,8 @@ public:
bool GetIsOpaque() override { return mOpaque; }
NS_IMETHOD Reset() override;
mozilla::layers::PersistentBufferProvider* GetBufferProvider(mozilla::layers::LayerManager* aManager);
already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager) override;
virtual bool ShouldForceInactiveLayer(LayerManager *aManager) override;
void MarkContextClean() override;

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

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CanvasRenderingContextHelper.h"
#include "ImageBitmapRenderingContext.h"
#include "ImageEncoder.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/Telemetry.h"
@ -26,36 +27,6 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv)
{
nsAutoString type;
nsContentUtils::ASCIIToLower(aType, type);
nsAutoString params;
bool usingCustomParseOptions;
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
if (aRv.Failed()) {
return;
}
if (mCurrentContext) {
// We disallow canvases of width or height zero, and set them to 1, so
// we will have a discrepancy with the sizes of the canvas and the context.
// That discrepancy is OK, the rest are not.
nsIntSize elementSize = GetWidthHeight();
if ((elementSize.width != mCurrentContext->GetWidth() &&
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
(elementSize.height != mCurrentContext->GetHeight() &&
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
UniquePtr<uint8_t[]> imageBuffer;
int32_t format = 0;
if (mCurrentContext) {
imageBuffer = mCurrentContext->GetImageBuffer(&format);
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
@ -97,6 +68,49 @@ CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
RefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(aGlobal, &aCallback);
ToBlob(aCx, aGlobal, callback, aType, aParams, aRv);
}
void
CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
nsIGlobalObject* aGlobal,
EncodeCompleteCallback* aCallback,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv)
{
nsAutoString type;
nsContentUtils::ASCIIToLower(aType, type);
nsAutoString params;
bool usingCustomParseOptions;
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
if (aRv.Failed()) {
return;
}
if (mCurrentContext) {
// We disallow canvases of width or height zero, and set them to 1, so
// we will have a discrepancy with the sizes of the canvas and the context.
// That discrepancy is OK, the rest are not.
nsIntSize elementSize = GetWidthHeight();
if ((elementSize.width != mCurrentContext->GetWidth() &&
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
(elementSize.height != mCurrentContext->GetHeight() &&
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
UniquePtr<uint8_t[]> imageBuffer;
int32_t format = 0;
if (mCurrentContext) {
imageBuffer = mCurrentContext->GetImageBuffer(&format);
}
RefPtr<EncodeCompleteCallback> callback = aCallback;
aRv = ImageEncoder::ExtractDataAsync(type,
params,
usingCustomParseOptions,
@ -138,6 +152,11 @@ CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
return nullptr;
break;
case CanvasContextType::ImageBitmap:
ret = new ImageBitmapRenderingContext();
break;
}
MOZ_ASSERT(ret);

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

@ -18,13 +18,15 @@ class ErrorResult;
namespace dom {
class EncodeCompleteCallback;
class FileCallback;
enum class CanvasContextType : uint8_t {
NoContext,
Canvas2D,
WebGL1,
WebGL2
WebGL2,
ImageBitmap
};
/**
@ -57,6 +59,10 @@ protected:
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
void ToBlob(JSContext* aCx, nsIGlobalObject* aGlobal, EncodeCompleteCallback* aCallback,
const nsAString& aType, JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
virtual already_AddRefed<nsICanvasRenderingContextInternal>
CreateContext(CanvasContextType aContextType);

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

@ -61,6 +61,11 @@ GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_typ
}
}
if (str.EqualsLiteral("bitmaprenderer")) {
*out_type = dom::CanvasContextType::ImageBitmap;
return true;
}
return false;
}

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

@ -5,6 +5,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ImageBitmap.h"
#include "mozilla/dom/ImageBitmapBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/StructuredCloneTags.h"
@ -145,15 +146,14 @@ CropAndCopyDataSourceSurface(DataSourceSurface* aSurface, const IntRect& aCropRe
}
/*
* Encapsulate the given _aSurface_ into a layers::CairoImage.
* Encapsulate the given _aSurface_ into a layers::SourceSurfaceImage.
*/
static already_AddRefed<layers::Image>
CreateImageFromSurface(SourceSurface* aSurface)
{
MOZ_ASSERT(aSurface);
RefPtr<layers::CairoImage> image =
new layers::CairoImage(aSurface->GetSize(), aSurface);
RefPtr<layers::SourceSurfaceImage> image =
new layers::SourceSurfaceImage(aSurface->GetSize(), aSurface);
return image.forget();
}
@ -250,13 +250,13 @@ CreateImageFromRawData(const gfx::IntSize& aSize,
/*
* This is a synchronous task.
* This class is used to create a layers::CairoImage from raw data in the main
* This class is used to create a layers::SourceSurfaceImage from raw data in the main
* thread. While creating an ImageBitmap from an ImageData, we need to create
* a SouceSurface from the ImageData's raw data and then set the SourceSurface
* into a layers::CairoImage. However, the layers::CairoImage asserts the
* into a layers::SourceSurfaceImage. However, the layers::SourceSurfaceImage asserts the
* setting operation in the main thread, so if we are going to create an
* ImageBitmap from an ImageData off the main thread, we post an event to the
* main thread to create a layers::CairoImage from an ImageData's raw data.
* main thread to create a layers::SourceSurfaceImage from an ImageData's raw data.
*/
class CreateImageFromRawDataInMainThreadSyncTask final :
public WorkerMainThreadRunnable
@ -408,6 +408,14 @@ ImageBitmap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return ImageBitmapBinding::Wrap(aCx, this, aGivenProto);
}
void
ImageBitmap::Close()
{
mData = nullptr;
mSurface = nullptr;
mPictureRect.SetEmpty();
}
void
ImageBitmap::SetPictureRect(const IntRect& aRect, ErrorResult& aRv)
{
@ -419,6 +427,10 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
{
MOZ_ASSERT(aTarget);
if (!mData) {
return nullptr;
}
if (!mSurface) {
mSurface = mData->GetAsSourceSurface();
}
@ -493,6 +505,68 @@ ImageBitmap::PrepareForDrawTarget(gfx::DrawTarget* aTarget)
return surface.forget();
}
already_AddRefed<layers::Image>
ImageBitmap::TransferAsImage()
{
RefPtr<layers::Image> image = mData;
Close();
return image.forget();
}
ImageBitmapCloneData*
ImageBitmap::ToCloneData()
{
ImageBitmapCloneData* result = new ImageBitmapCloneData();
result->mPictureRect = mPictureRect;
RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
result->mSurface = surface->GetDataSurface();
MOZ_ASSERT(result->mSurface);
return result;
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
ImageBitmapCloneData* aData)
{
RefPtr<layers::Image> data =
CreateImageFromSurface(aData->mSurface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
ErrorResult rv;
ret->SetPictureRect(aData->mPictureRect, rv);
return ret.forget();
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
OffscreenCanvas& aOffscreenCanvas,
ErrorResult& aRv)
{
// Check origin-clean.
if (aOffscreenCanvas.IsWriteOnly()) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsLayoutUtils::SurfaceFromElementResult res =
nsLayoutUtils::SurfaceFromOffscreenCanvas(&aOffscreenCanvas,
nsLayoutUtils::SFE_WANT_FIRST_FRAME);
RefPtr<SourceSurface> surface = res.GetSourceSurface();
if (NS_WARN_IF(!surface)) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
RefPtr<layers::Image> data =
CreateImageFromSurface(surface);
RefPtr<ImageBitmap> ret = new ImageBitmap(aGlobal, data);
return ret.forget();
}
/* static */ already_AddRefed<ImageBitmap>
ImageBitmap::CreateInternal(nsIGlobalObject* aGlobal, HTMLImageElement& aImageEl,
const Maybe<IntRect>& aCropRect, ErrorResult& aRv)
@ -1154,7 +1228,7 @@ ImageBitmap::Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
ImageBitmap::ReadStructuredClone(JSContext* aCx,
JSStructuredCloneReader* aReader,
nsIGlobalObject* aParent,
const nsTArray<RefPtr<layers::Image>>& aClonedImages,
const nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
uint32_t aIndex)
{
MOZ_ASSERT(aCx);
@ -1177,8 +1251,8 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
int32_t picRectHeight = BitwiseCast<int32_t>(picRectHeight_);
// Create a new ImageBitmap.
MOZ_ASSERT(!aClonedImages.IsEmpty());
MOZ_ASSERT(aIndex < aClonedImages.Length());
MOZ_ASSERT(!aClonedSurfaces.IsEmpty());
MOZ_ASSERT(aIndex < aClonedSurfaces.Length());
// RefPtr<ImageBitmap> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
@ -1187,8 +1261,9 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
// while destructors are running.
JS::Rooted<JS::Value> value(aCx);
{
RefPtr<layers::Image> img = CreateImageFromSurface(aClonedSurfaces[aIndex]);
RefPtr<ImageBitmap> imageBitmap =
new ImageBitmap(aParent, aClonedImages[aIndex]);
new ImageBitmap(aParent, img);
ErrorResult error;
imageBitmap->SetPictureRect(IntRect(picRectX, picRectY,
@ -1208,7 +1283,7 @@ ImageBitmap::ReadStructuredClone(JSContext* aCx,
/*static*/ bool
ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
nsTArray<RefPtr<layers::Image>>& aClonedImages,
nsTArray<RefPtr<DataSourceSurface>>& aClonedSurfaces,
ImageBitmap* aImageBitmap)
{
MOZ_ASSERT(aWriter);
@ -1219,8 +1294,8 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
const uint32_t picRectWidth = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.width);
const uint32_t picRectHeight = BitwiseCast<uint32_t>(aImageBitmap->mPictureRect.height);
// Indexing the cloned images and send the index to the receiver.
uint32_t index = aClonedImages.Length();
// Indexing the cloned surfaces and send the index to the receiver.
uint32_t index = aClonedSurfaces.Length();
if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEBITMAP, index)) ||
NS_WARN_IF(!JS_WriteUint32Pair(aWriter, picRectX, picRectY)) ||
@ -1228,8 +1303,24 @@ ImageBitmap::WriteStructuredClone(JSStructuredCloneWriter* aWriter,
return false;
}
aClonedImages.AppendElement(aImageBitmap->mData);
RefPtr<SourceSurface> surface =
aImageBitmap->mData->GetAsSourceSurface();
RefPtr<DataSourceSurface> snapshot = surface->GetDataSurface();
RefPtr<DataSourceSurface> dstDataSurface;
{
// DataSourceSurfaceD2D1::GetStride() will call EnsureMapped implicitly and
// won't Unmap after exiting function. So instead calling GetStride()
// directly, using ScopedMap to get stride.
DataSourceSurface::ScopedMap map(snapshot, DataSourceSurface::READ);
dstDataSurface =
Factory::CreateDataSourceSurfaceWithStride(snapshot->GetSize(),
snapshot->GetFormat(),
map.GetStride(),
true);
}
MOZ_ASSERT(dstDataSurface);
Factory::CopyDataSourceSurface(snapshot, dstDataSurface);
aClonedSurfaces.AppendElement(dstDataSurface);
return true;
}

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

@ -25,6 +25,7 @@ namespace mozilla {
class ErrorResult;
namespace gfx {
class DataSourceSurface;
class SourceSurface;
}
@ -33,6 +34,7 @@ class Image;
}
namespace dom {
class OffscreenCanvas;
namespace workers {
class WorkerStructuredCloneClosure;
@ -50,6 +52,12 @@ class CreateImageBitmapFromBlob;
class CreateImageBitmapFromBlobTask;
class CreateImageBitmapFromBlobWorkerTask;
struct ImageBitmapCloneData final
{
RefPtr<gfx::DataSourceSurface> mSurface;
gfx::IntRect mPictureRect;
};
/*
* ImageBitmap is an opaque handler to several kinds of image-like objects from
* HTMLImageElement, HTMLVideoElement, HTMLCanvasElement, ImageData to
@ -83,6 +91,8 @@ public:
return mPictureRect.Height();
}
void Close();
/*
* The PrepareForDrawTarget() might return null if the mPictureRect does not
* intersect with the size of mData.
@ -90,6 +100,24 @@ public:
already_AddRefed<gfx::SourceSurface>
PrepareForDrawTarget(gfx::DrawTarget* aTarget);
/*
* Transfer ownership of buffer to caller. So this function call
* Close() implicitly.
*/
already_AddRefed<layers::Image>
TransferAsImage();
ImageBitmapCloneData*
ToCloneData();
static already_AddRefed<ImageBitmap>
CreateFromCloneData(nsIGlobalObject* aGlobal, ImageBitmapCloneData* aData);
static already_AddRefed<ImageBitmap>
CreateFromOffscreenCanvas(nsIGlobalObject* aGlobal,
OffscreenCanvas& aOffscreenCanvas,
ErrorResult& aRv);
static already_AddRefed<Promise>
Create(nsIGlobalObject* aGlobal, const ImageBitmapSource& aSrc,
const Maybe<gfx::IntRect>& aCropRect, ErrorResult& aRv);
@ -98,12 +126,12 @@ public:
ReadStructuredClone(JSContext* aCx,
JSStructuredCloneReader* aReader,
nsIGlobalObject* aParent,
const nsTArray<RefPtr<layers::Image>>& aClonedImages,
const nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
uint32_t aIndex);
static bool
WriteStructuredClone(JSStructuredCloneWriter* aWriter,
nsTArray<RefPtr<layers::Image>>& aClonedImages,
nsTArray<RefPtr<gfx::DataSourceSurface>>& aClonedSurfaces,
ImageBitmap* aImageBitmap);
friend CreateImageBitmapFromBlob;

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

@ -0,0 +1,303 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* 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 "ImageBitmapRenderingContext.h"
#include "mozilla/dom/ImageBitmapRenderingContextBinding.h"
#include "ImageContainer.h"
#include "ImageLayers.h"
namespace mozilla {
namespace dom {
ImageBitmapRenderingContext::ImageBitmapRenderingContext()
: mWidth(0)
, mHeight(0)
{
}
ImageBitmapRenderingContext::~ImageBitmapRenderingContext()
{
RemovePostRefreshObserver();
}
JSObject*
ImageBitmapRenderingContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return ImageBitmapRenderingContextBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<layers::Image>
ImageBitmapRenderingContext::ClipToIntrinsicSize()
{
if (!mImage) {
return nullptr;
}
// If image is larger than canvas intrinsic size, clip it to the intrinsic size.
RefPtr<gfx::SourceSurface> surface;
RefPtr<layers::Image> result;
if (mWidth < mImage->GetSize().width ||
mHeight < mImage->GetSize().height) {
surface = MatchWithIntrinsicSize();
} else {
surface = mImage->GetAsSourceSurface();
}
result = new layers::SourceSurfaceImage(gfx::IntSize(mWidth, mHeight), surface);
return result.forget();
}
void
ImageBitmapRenderingContext::TransferImageBitmap(ImageBitmap& aImageBitmap)
{
Reset();
mImage = aImageBitmap.TransferAsImage();
if (!mImage) {
return;
}
Redraw(gfxRect(0, 0, mWidth, mHeight));
}
int32_t
ImageBitmapRenderingContext::GetWidth() const
{
return mWidth;
}
int32_t
ImageBitmapRenderingContext::GetHeight() const
{
return mHeight;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
{
mWidth = aWidth;
mHeight = aHeight;
return NS_OK;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::InitializeWithSurface(nsIDocShell* aDocShell,
gfxASurface* aSurface,
int32_t aWidth,
int32_t aHeight)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
already_AddRefed<DataSourceSurface>
ImageBitmapRenderingContext::MatchWithIntrinsicSize()
{
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
RefPtr<DataSourceSurface> temp =
Factory::CreateDataSourceSurface(IntSize(mWidth, mHeight), surface->GetFormat());
if (!temp) {
return nullptr;
}
DataSourceSurface::ScopedMap map(temp, DataSourceSurface::READ_WRITE);
if (!map.IsMapped()) {
return nullptr;
}
RefPtr<DrawTarget> dt =
Factory::CreateDrawTargetForData(BackendType::CAIRO,
map.GetData(),
temp->GetSize(),
map.GetStride(),
temp->GetFormat());
if (!dt) {
return nullptr;
}
dt->ClearRect(Rect(0, 0, mWidth, mHeight));
dt->CopySurface(surface,
IntRect(0, 0, surface->GetSize().width,
surface->GetSize().height),
IntPoint(0, 0));
return temp.forget();
}
mozilla::UniquePtr<uint8_t[]>
ImageBitmapRenderingContext::GetImageBuffer(int32_t* aFormat)
{
*aFormat = 0;
if (!mImage) {
return nullptr;
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
RefPtr<DataSourceSurface> data = surface->GetDataSurface();
if (!data) {
return nullptr;
}
if (data->GetSize() != IntSize(mWidth, mHeight)) {
data = MatchWithIntrinsicSize();
}
*aFormat = imgIEncoder::INPUT_FORMAT_HOSTARGB;
return SurfaceToPackedBGRA(data);
}
NS_IMETHODIMP
ImageBitmapRenderingContext::GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream)
{
nsCString enccid("@mozilla.org/image/encoder;2?type=");
enccid += aMimeType;
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
if (!encoder) {
return NS_ERROR_FAILURE;
}
int32_t format = 0;
UniquePtr<uint8_t[]> imageBuffer = GetImageBuffer(&format);
if (!imageBuffer) {
return NS_ERROR_FAILURE;
}
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer.get(), format,
encoder, aEncoderOptions, aStream);
}
already_AddRefed<mozilla::gfx::SourceSurface>
ImageBitmapRenderingContext::GetSurfaceSnapshot(bool* aPremultAlpha)
{
if (!mImage) {
return nullptr;
}
if (aPremultAlpha) {
*aPremultAlpha = true;
}
RefPtr<SourceSurface> surface = mImage->GetAsSourceSurface();
if (surface->GetSize() != IntSize(mWidth, mHeight)) {
return MatchWithIntrinsicSize();
}
return surface.forget();
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetIsOpaque(bool aIsOpaque)
{
return NS_OK;
}
bool
ImageBitmapRenderingContext::GetIsOpaque()
{
return false;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::Reset()
{
if (mCanvasElement) {
mCanvasElement->InvalidateCanvas();
}
mImage = nullptr;
return NS_OK;
}
already_AddRefed<Layer>
ImageBitmapRenderingContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager)
{
if (!mImage) {
// No DidTransactionCallback will be received, so mark the context clean
// now so future invalidations will be dispatched.
MarkContextClean();
return nullptr;
}
RefPtr<ImageLayer> imageLayer;
if (aOldLayer) {
imageLayer = static_cast<ImageLayer*>(aOldLayer);
} else {
imageLayer = aManager->CreateImageLayer();
}
RefPtr<ImageContainer> imageContainer = imageLayer->GetContainer();
if (!imageContainer) {
imageContainer = aManager->CreateImageContainer();
imageLayer->SetContainer(imageContainer);
}
nsAutoTArray<ImageContainer::NonOwningImage, 1> imageList;
RefPtr<layers::Image> image = ClipToIntrinsicSize();
imageList.AppendElement(ImageContainer::NonOwningImage(image));
imageContainer->SetCurrentImages(imageList);
return imageLayer.forget();
}
void
ImageBitmapRenderingContext::MarkContextClean()
{
}
NS_IMETHODIMP
ImageBitmapRenderingContext::Redraw(const gfxRect& aDirty)
{
if (!mCanvasElement) {
return NS_OK;
}
mozilla::gfx::Rect rect = ToRect(aDirty);
mCanvasElement->InvalidateCanvasContent(&rect);
return NS_OK;
}
NS_IMETHODIMP
ImageBitmapRenderingContext::SetIsIPC(bool aIsIPC)
{
return NS_OK;
}
void
ImageBitmapRenderingContext::DidRefresh()
{
}
void
ImageBitmapRenderingContext::MarkContextCleanForFrameCapture()
{
}
bool
ImageBitmapRenderingContext::IsContextCleanForFrameCapture()
{
return true;
}
NS_IMPL_CYCLE_COLLECTING_ADDREF(ImageBitmapRenderingContext)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ImageBitmapRenderingContext)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ImageBitmapRenderingContext,
mCanvasElement,
mOffscreenCanvas)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImageBitmapRenderingContext)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
}
}

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

@ -0,0 +1,97 @@
/* 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/. */
#ifndef ImageBitmapRenderingContext_h
#define ImageBitmapRenderingContext_h
#include "nsICanvasRenderingContextInternal.h"
#include "nsWrapperCache.h"
namespace mozilla {
namespace gfx {
class DataSourceSurface;
class SourceSurface;
}
namespace layers {
class Image;
class ImageContainer;
}
namespace dom {
/**
* The purpose of ImageBitmapRenderingContext is to provide a faster and efficient
* way to display ImageBitmap. Simply call TransferImageBitmap() then we'll transfer
* the surface of ImageBitmap to this context and then to use it to display.
*
* See more details in spec: https://wiki.whatwg.org/wiki/OffscreenCanvas
*/
class ImageBitmapRenderingContext final :
public nsICanvasRenderingContextInternal,
public nsWrapperCache
{
virtual ~ImageBitmapRenderingContext();
public:
ImageBitmapRenderingContext();
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
// nsISupports interface + CC
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ImageBitmapRenderingContext)
void TransferImageBitmap(ImageBitmap& aImageBitmap);
// nsICanvasRenderingContextInternal
virtual int32_t GetWidth() const override;
virtual int32_t GetHeight() const override;
NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
NS_IMETHOD InitializeWithSurface(nsIDocShell* aDocShell,
gfxASurface* aSurface,
int32_t aWidth,
int32_t aHeight) override;
virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
NS_IMETHOD GetInputStream(const char* aMimeType,
const char16_t* aEncoderOptions,
nsIInputStream** aStream) override;
virtual already_AddRefed<mozilla::gfx::SourceSurface>
GetSurfaceSnapshot(bool* aPremultAlpha = nullptr) override;
NS_IMETHOD SetIsOpaque(bool aIsOpaque) override;
virtual bool GetIsOpaque() override;
NS_IMETHOD Reset() override;
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer* aOldLayer,
LayerManager* aManager) override;
virtual void MarkContextClean() override;
NS_IMETHOD Redraw(const gfxRect& aDirty) override;
NS_IMETHOD SetIsIPC(bool aIsIPC) override;
virtual void DidRefresh() override;
virtual void MarkContextCleanForFrameCapture() override;
virtual bool IsContextCleanForFrameCapture() override;
protected:
already_AddRefed<gfx::DataSourceSurface> MatchWithIntrinsicSize();
already_AddRefed<layers::Image> ClipToIntrinsicSize();
int32_t mWidth;
int32_t mHeight;
RefPtr<layers::Image> mImage;
};
}
}
#endif /* ImageBitmapRenderingContext_h */

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

@ -8,6 +8,7 @@
#include "mozilla/dom/OffscreenCanvasBinding.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/layers/AsyncCanvasRenderer.h"
#include "mozilla/layers/CanvasClient.h"
#include "mozilla/layers/ImageBridgeChild.h"
@ -24,12 +25,13 @@ namespace dom {
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
uint32_t aWidth, uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
bool aNeutered)
bool aNeutered, bool aIsWriteOnly)
: mRenderer(aRenderer)
, mWidth(aWidth)
, mHeight(aHeight)
, mCompositorBackendType(aCompositorBackend)
, mNeutered(aNeutered)
, mIsWriteOnly(aIsWriteOnly)
{
}
@ -37,12 +39,15 @@ OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
{
}
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal,
uint32_t aWidth,
uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
layers::AsyncCanvasRenderer* aRenderer)
: mAttrDirty(false)
: DOMEventTargetHelper(aGlobal)
, mAttrDirty(false)
, mNeutered(false)
, mIsWriteOnly(false)
, mWidth(aWidth)
, mHeight(aHeight)
, mCompositorBackendType(aCompositorBackend)
@ -55,12 +60,6 @@ OffscreenCanvas::~OffscreenCanvas()
ClearResources();
}
OffscreenCanvas*
OffscreenCanvas::GetParentObject() const
{
return nullptr;
}
JSObject*
OffscreenCanvas::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
@ -68,6 +67,19 @@ OffscreenCanvas::WrapObject(JSContext* aCx,
return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
}
/* static */ already_AddRefed<OffscreenCanvas>
OffscreenCanvas::Constructor(const GlobalObject& aGlobal,
uint32_t aWidth,
uint32_t aHeight,
ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
RefPtr<OffscreenCanvas> offscreenCanvas =
new OffscreenCanvas(global, aWidth, aHeight,
layers::LayersBackend::LAYERS_NONE, nullptr);
return offscreenCanvas.forget();
}
void
OffscreenCanvas::ClearResources()
{
@ -79,7 +91,9 @@ OffscreenCanvas::ClearResources()
if (mCanvasRenderer) {
nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
MOZ_RELEASE_ASSERT(activeThread);
MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
bool current;
activeThread->IsOnCurrentThread(&current);
MOZ_RELEASE_ASSERT(current);
mCanvasRenderer->SetCanvasClient(nullptr);
mCanvasRenderer->mContext = nullptr;
mCanvasRenderer->mGLContext = nullptr;
@ -107,7 +121,8 @@ OffscreenCanvas::GetContext(JSContext* aCx,
}
if (!(contextType == CanvasContextType::WebGL1 ||
contextType == CanvasContextType::WebGL2))
contextType == CanvasContextType::WebGL2 ||
contextType == CanvasContextType::ImageBitmap))
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return nullptr;
@ -124,6 +139,8 @@ OffscreenCanvas::GetContext(JSContext* aCx,
}
if (mCanvasRenderer) {
if (contextType == CanvasContextType::WebGL1 ||
contextType == CanvasContextType::WebGL2) {
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
gl::GLContext* gl = webGL->GL();
mCanvasRenderer->mContext = mCurrentContext;
@ -148,6 +165,7 @@ OffscreenCanvas::GetContext(JSContext* aCx,
screen->Morph(Move(factory));
}
}
}
return result;
}
@ -165,6 +183,12 @@ OffscreenCanvas::CreateContext(CanvasContextType aContextType)
void
OffscreenCanvas::CommitFrameToCompositor()
{
if (!mCanvasRenderer) {
// This offscreen canvas doesn't associate to any HTML canvas element.
// So, just bail out.
return;
}
// The attributes has changed, we have to notify main
// thread to change canvas size.
if (mAttrDirty) {
@ -191,15 +215,121 @@ OffscreenCanvasCloneData*
OffscreenCanvas::ToCloneData()
{
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
mCompositorBackendType, mNeutered);
mCompositorBackendType, mNeutered, mIsWriteOnly);
}
already_AddRefed<ImageBitmap>
OffscreenCanvas::TransferToImageBitmap()
{
ErrorResult rv;
RefPtr<ImageBitmap> result = ImageBitmap::CreateFromOffscreenCanvas(GetGlobalObject(), *this, rv);
// Clear the content.
if ((mCurrentContextType == CanvasContextType::WebGL1 ||
mCurrentContextType == CanvasContextType::WebGL2))
{
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
webGL->ClearScreen();
}
return result.forget();
}
already_AddRefed<Promise>
OffscreenCanvas::ToBlob(JSContext* aCx,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv)
{
// do a trust check if this is a write-only canvas
if (mIsWriteOnly) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = GetGlobalObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
// Encoder callback when encoding is complete.
class EncodeCallback : public EncodeCompleteCallback
{
public:
EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise)
: mGlobal(aGlobal)
, mPromise(aPromise) {}
// This is called on main thread.
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
{
RefPtr<Blob> blob = aBlob;
ErrorResult rv;
uint64_t size = blob->GetSize(rv);
if (rv.Failed()) {
rv.SuppressException();
} else {
AutoJSAPI jsapi;
if (jsapi.Init(mGlobal)) {
JS_updateMallocCounter(jsapi.cx(), size);
}
}
if (mPromise) {
RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
mPromise->MaybeResolve(newBlob);
}
mGlobal = nullptr;
mPromise = nullptr;
return rv.StealNSResult();
}
nsCOMPtr<nsIGlobalObject> mGlobal;
RefPtr<Promise> mPromise;
};
RefPtr<EncodeCompleteCallback> callback =
new EncodeCallback(global, promise);
CanvasRenderingContextHelper::ToBlob(aCx, global,
callback, aType, aParams, aRv);
return promise.forget();
}
already_AddRefed<gfx::SourceSurface>
OffscreenCanvas::GetSurfaceSnapshot(bool* aPremultAlpha)
{
if (!mCurrentContext) {
return nullptr;
}
return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
}
nsCOMPtr<nsIGlobalObject>
OffscreenCanvas::GetGlobalObject()
{
if (NS_IsMainThread()) {
return GetParentObject();
}
dom::workers::WorkerPrivate* workerPrivate =
dom::workers::GetCurrentThreadWorkerPrivate();
return workerPrivate->GlobalScope();
}
/* static */ already_AddRefed<OffscreenCanvas>
OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
OffscreenCanvas::CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData)
{
MOZ_ASSERT(aData);
RefPtr<OffscreenCanvas> wc =
new OffscreenCanvas(aData->mWidth, aData->mHeight,
new OffscreenCanvas(aGlobal, aData->mWidth, aData->mHeight,
aData->mCompositorBackendType, aData->mRenderer);
if (aData->mNeutered) {
wc->SetNeutered();

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

@ -25,6 +25,8 @@ class CanvasClient;
} // namespace layers
namespace dom {
class Blob;
class ImageBitmap;
// This is helper class for transferring OffscreenCanvas to worker thread.
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
@ -35,7 +37,7 @@ struct OffscreenCanvasCloneData final
OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
uint32_t aWidth, uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
bool aNeutered);
bool aNeutered, bool aIsWriteOnly);
~OffscreenCanvasCloneData();
RefPtr<layers::AsyncCanvasRenderer> mRenderer;
@ -43,6 +45,7 @@ struct OffscreenCanvasCloneData final
uint32_t mHeight;
layers::LayersBackend mCompositorBackendType;
bool mNeutered;
bool mIsWriteOnly;
};
class OffscreenCanvas final : public DOMEventTargetHelper
@ -52,16 +55,23 @@ public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
OffscreenCanvas(uint32_t aWidth,
OffscreenCanvas(nsIGlobalObject* aGlobal,
uint32_t aWidth,
uint32_t aHeight,
layers::LayersBackend aCompositorBackend,
layers::AsyncCanvasRenderer* aRenderer);
OffscreenCanvas* GetParentObject() const;
nsCOMPtr<nsIGlobalObject> GetParentObject() const { return GetOwnerGlobal(); }
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
static already_AddRefed<OffscreenCanvas>
Constructor(const GlobalObject& aGlobal,
uint32_t aWidth,
uint32_t aHeight,
ErrorResult& aRv);
void ClearResources();
uint32_t Width() const
@ -100,13 +110,24 @@ public:
}
}
already_AddRefed<ImageBitmap>
TransferToImageBitmap();
already_AddRefed<Promise>
ToBlob(JSContext* aCx,
const nsAString& aType,
JS::Handle<JS::Value> aParams,
ErrorResult& aRv);
nsICanvasRenderingContextInternal* GetContext() const
{
return mCurrentContext;
}
already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
static already_AddRefed<OffscreenCanvas>
CreateFromCloneData(OffscreenCanvasCloneData* aData);
CreateFromCloneData(nsIGlobalObject* aGlobal, OffscreenCanvasCloneData* aData);
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
@ -147,6 +168,16 @@ public:
return mNeutered;
}
void SetWriteOnly()
{
mIsWriteOnly = true;
}
bool IsWriteOnly() const
{
return mIsWriteOnly;
}
layers::LayersBackend GetCompositorBackendType() const
{
return mCompositorBackendType;
@ -155,6 +186,8 @@ public:
private:
~OffscreenCanvas();
nsCOMPtr<nsIGlobalObject> GetGlobalObject();
void CanvasAttrChanged()
{
mAttrDirty = true;
@ -164,6 +197,7 @@ private:
bool mAttrDirty;
bool mNeutered;
bool mIsWriteOnly;
uint32_t mWidth;
uint32_t mHeight;

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

@ -5,10 +5,10 @@
#include "WebGL2Context.h"
#include "gfxPrefs.h"
#include "GLContext.h"
#include "mozilla/dom/WebGL2RenderingContextBinding.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "WebGLBuffer.h"
#include "WebGLFormats.h"
@ -37,7 +37,7 @@ WebGL2Context::CreateFormatUsage(gl::GLContext* gl) const
/*static*/ bool
WebGL2Context::IsSupported()
{
return Preferences::GetBool("webgl.enable-prototype-webgl2", false);
return gfxPrefs::WebGL2Enabled();
}
/*static*/ WebGL2Context*

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

@ -1131,9 +1131,9 @@ private:
RefPtr<HTMLCanvasElement> mCanvas;
};
already_AddRefed<layers::CanvasLayer>
already_AddRefed<layers::Layer>
WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
CanvasLayer* oldLayer,
Layer* oldLayer,
LayerManager* manager)
{
if (IsContextLost())
@ -1141,7 +1141,7 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
if (!mResetLayer && oldLayer &&
oldLayer->HasUserData(&gWebGLLayerUserData)) {
RefPtr<layers::CanvasLayer> ret = oldLayer;
RefPtr<layers::Layer> ret = oldLayer;
return ret.forget();
}

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

@ -331,8 +331,8 @@ public:
return ActiveBoundTextureForTarget(texTarget);
}
already_AddRefed<CanvasLayer>
GetCanvasLayer(nsDisplayListBuilder* builder, CanvasLayer* oldLayer,
already_AddRefed<Layer>
GetCanvasLayer(nsDisplayListBuilder* builder, Layer* oldLayer,
LayerManager* manager) override;
// Note that 'clean' here refers to its invalidation state, not the

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

@ -539,11 +539,14 @@ FormatUsageAuthority::CreateForWebGL1(gl::GLContext* gl)
// RGBA8 is made renderable in WebGL 1.0, "Framebuffer Object Attachments"
// render filter
// able able
fnSet(EffectiveFormat::RGBA8 , true , true);
fnSet(EffectiveFormat::RGBA4 , true , true);
fnSet(EffectiveFormat::RGB5_A1, true , true);
fnSet(EffectiveFormat::RGB8 , false, true);
fnSet(EffectiveFormat::RGB565 , true , true);
fnSet(EffectiveFormat::RGBA8 , true, true);
fnSet(EffectiveFormat::RGBA4 , true, true);
fnSet(EffectiveFormat::RGB5_A1, true, true);
fnSet(EffectiveFormat::RGB565 , true, true);
// RGB8 is not guaranteed to be renderable, but we should allow it for web-compat.
// Min-capability mode should mark this as non-renderable.
fnSet(EffectiveFormat::RGB8, true, true);
fnSet(EffectiveFormat::Luminance8Alpha8, false, true);
fnSet(EffectiveFormat::Luminance8 , false, true);

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

@ -7,7 +7,7 @@
TEST_DIRS += ['compiledtest']
# Number changes to this file to avoid bug 1081323 (clobber after changing a manifest):
# 1
# 2
MOCHITEST_MANIFESTS += [
'test/crossorigin/mochitest.ini',
@ -34,6 +34,7 @@ EXPORTS.mozilla.dom += [
'CanvasRenderingContextHelper.h',
'CanvasUtils.h',
'ImageBitmap.h',
'ImageBitmapRenderingContext.h',
'ImageBitmapSource.h',
'ImageData.h',
'OffscreenCanvas.h',
@ -50,6 +51,7 @@ UNIFIED_SOURCES += [
'DocumentRendererChild.cpp',
'DocumentRendererParent.cpp',
'ImageBitmap.cpp',
'ImageBitmapRenderingContext.cpp',
'ImageData.cpp',
'OffscreenCanvas.cpp',
]

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

@ -26,6 +26,7 @@ class nsDisplayListBuilder;
namespace mozilla {
namespace layers {
class CanvasLayer;
class Layer;
class LayerManager;
} // namespace layers
namespace gfx {
@ -39,6 +40,7 @@ class nsICanvasRenderingContextInternal :
{
public:
typedef mozilla::layers::CanvasLayer CanvasLayer;
typedef mozilla::layers::Layer Layer;
typedef mozilla::layers::LayerManager LayerManager;
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
@ -129,8 +131,8 @@ public:
// Return the CanvasLayer for this context, creating
// one for the given layer manager if not available.
virtual already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* builder,
CanvasLayer *oldLayer,
virtual already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* builder,
Layer *oldLayer,
LayerManager *manager) = 0;
// Return true if the canvas should be forced to be "inactive" to ensure

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

@ -218,6 +218,7 @@ skip-if = toolkit != 'cocoa'
[test_2d.path.rect.zero.6.html]
disabled = bug 407107
[test_2d.strokeRect.zero.5.html]
[test_bitmaprenderer.html]
[test_bug232227.html]
[test_bug613794.html]
[test_bug753758.html]
@ -246,11 +247,13 @@ support-files = captureStream_common.js
support-files = file_drawWindow_source.html file_drawWindow_common.js
skip-if = (buildapp == 'b2g' && toolkit != 'gonk')
[test_imagebitmap.html]
[test_imagebitmap_close.html]
[test_imagebitmap_cropping.html]
[test_imagebitmap_on_worker.html]
[test_imagebitmap_structuredclone.html]
[test_imagebitmap_structuredclone_iframe.html]
[test_imagebitmap_structuredclone_window.html]
[test_imagebitmap_transfer.html]
[test_ImageData_ctor.html]
[test_isPointInStroke.html]
[test_mozDashOffset.html]
@ -267,6 +270,10 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
[test_createPattern_broken.html]
[test_setlinedash.html]
[test_filter.html]
[test_offscreencanvas_toblob.html]
tags = offscreencanvas
[test_offscreencanvas_toimagebitmap.html]
tags = offscreencanvas
[test_offscreencanvas_basic_webgl.html]
tags = offscreencanvas
[test_offscreencanvas_dynamic_fallback.html]

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

@ -1,27 +1,49 @@
/* WebWorker for test_offscreencanvas_*.html */
(function(){
var port = null;
function ok(expect, msg) {
if (port) {
port.postMessage({type: "test", result: !!expect, name: msg});
} else {
postMessage({type: "test", result: !!expect, name: msg});
function isInWorker() {
try {
return !(self instanceof Window);
} catch (e) {
return true;
}
}
function postMessageGeneral(data) {
if (isInWorker()) {
if (port) {
port.postMessage(data);
} else {
postMessage(data);
}
} else {
postMessage(data, "*");
}
}
function ok(expect, msg) {
postMessageGeneral({type: "test", result: !!expect, name: msg});
}
function finish() {
if (port) {
port.postMessage({type: "finish"});
} else {
postMessage({type: "finish"});
}
postMessageGeneral({type: "finish"});
}
function drawCount(count) {
postMessageGeneral({type: "draw", count: count});
}
function sendBlob(blob) {
postMessageGeneral({type: "blob", blob: blob});
}
function sendImageBitmap(img) {
if (port) {
port.postMessage({type: "draw", count: count});
port.postMessage({type: "imagebitmap", bitmap: img});
} else {
postMessage({type: "draw", count: count});
postMessage({type: "imagebitmap", bitmap: img});
}
}
@ -140,7 +162,7 @@ function createDrawFunc(canvas) {
// Start drawing
checkGLError('after setup');
return function(prefix) {
return function(prefix, needCommitFrame) {
if (prefix) {
prefix = "[" + prefix + "] ";
} else {
@ -152,7 +174,9 @@ function createDrawFunc(canvas) {
preDraw(prefix);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
postDraw(prefix);
if (needCommitFrame) {
gl.commit();
}
checkGLError(prefix);
};
}
@ -161,6 +185,9 @@ function createDrawFunc(canvas) {
function entryFunction(testStr, subtests, offscreenCanvas) {
var test = testStr;
var canvas = offscreenCanvas;
if (test == "webgl_imagebitmap") {
canvas = new OffscreenCanvas(64, 64);
}
if (test != "subworker") {
ok(canvas, "Canvas successfully transfered to worker");
@ -190,7 +217,7 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
finish();
return;
}
draw("loop " +count);
draw("loop " +count, true);
}, 0);
}
//------------------------------------------------------------------------
@ -205,11 +232,38 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
var count = 0;
var iid = setInterval(function() {
++count;
draw("loop " + count);
draw("loop " + count, true);
drawCount(count);
}, 0);
}
//------------------------------------------------------------------------
// Test toBlob
//------------------------------------------------------------------------
else if (test == "webgl_toblob") {
draw = createDrawFunc(canvas);
if (!draw) {
return;
}
draw("", false);
canvas.toBlob().then(function(blob) {
sendBlob(blob);
});
}
//------------------------------------------------------------------------
// Test toImageBitmap
//------------------------------------------------------------------------
else if (test == "webgl_imagebitmap") {
draw = createDrawFunc(canvas);
if (!draw) {
return;
}
draw("", false);
var imgBitmap = canvas.transferToImageBitmap();
sendImageBitmap(imgBitmap);
}
//------------------------------------------------------------------------
// Canvas Size Change from Worker
//------------------------------------------------------------------------
else if (test == "webgl_changesize") {
@ -219,22 +273,22 @@ function entryFunction(testStr, subtests, offscreenCanvas) {
return;
}
draw("64x64");
draw("64x64", true);
setTimeout(function() {
canvas.width = 128;
canvas.height = 128;
draw("Increased to 128x128");
draw("Increased to 128x128", true);
setTimeout(function() {
canvas.width = 32;
canvas.width = 32;
draw("Decreased to 32x32");
draw("Decreased to 32x32", true);
setTimeout(function() {
canvas.width = 64;
canvas.height = 64;
draw("Increased to 64x64");
draw("Increased to 64x64", true);
ok(true, "Worker is done");
finish();
@ -297,3 +351,9 @@ onconnect = function(evt) {
port.start();
};
if (!isInWorker()) {
window.entryFunction = entryFunction;
}
})();

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

@ -0,0 +1,163 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<script type="text/js-worker">
function ok(expect, msg) {
postMessage({"type": "status", status: !!expect, msg: msg});
}
onmessage = function(event) {
var bitmap = event.data.bitmap;
ok(!!bitmap, "Get the ImageBitmap from the main script.");
var offscreenCanvas = new OffscreenCanvas(64, 64);
var ctx = offscreenCanvas.getContext('bitmaprenderer');
ok(!!ctx, "Get bitmaprenderer context on worker.");
ctx.transferImageBitmap(bitmap);
var resultBitmap = offscreenCanvas.transferToImageBitmap();
postMessage({"type": "bitmap", bitmap: resultBitmap}, [resultBitmap]);
}
</script>
<script>
SimpleTest.waitForExplicitFinish();
function createCanvas(width, height) {
var htmlCanvas = document.createElement('canvas');
htmlCanvas.width = width;
htmlCanvas.height = height;
document.body.appendChild(htmlCanvas);
return htmlCanvas;
}
function runTest(canvasWidth, canvasHeight, nextTest) {
var canvas1 = createCanvas(canvasWidth, canvasHeight);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
var canvasRef = createCanvas(90, 90);
var ctx = canvasRef.getContext("2d");
// Clear with black transparent first
ctx.fillStyle = "rgba(0, 0, 0, 0)";
ctx.fillRect(0, 0, 90, 90);
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
createImageBitmap(canvas1).then(function(bmp) {
document.body.removeChild(canvas1);
var canvas2 = createCanvas(90, 90);
var ctx2 = canvas2.getContext("bitmaprenderer");
ctx2.transferImageBitmap(bmp);
ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
// Exam render result
canvasRef.style.display = "none";
canvas2.style.display = "block";
var snapshot = snapshotWindow(window);
canvasRef.style.display = "block";
canvas2.style.display = "none";
var snapshotRef = snapshotWindow(window);
var results = compareSnapshots(snapshot, snapshotRef, true);
ok(results[0], "Screenshots should be the same");
document.body.removeChild(canvasRef);
document.body.removeChild(canvas2);
nextTest();
});
}
function scaleTest() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext("2d");
ctx2.fillStyle = "#00FF00";
ctx2.fillRect(0, 0, 64, 64);
var p1 = createImageBitmap(canvas1);
var p2 = createImageBitmap(canvas2);
Promise.all([p1, p2]).then(function(bitmaps) {
document.body.removeChild(canvas1);
document.body.removeChild(canvas2);
// Create a large canvas then shrink.
var canvas3 = createCanvas(128, 128);
var ctx3 = canvas3.getContext("bitmaprenderer");
ctx3.transferImageBitmap(bitmaps[0]);
var snapshotLargeRef = snapshotWindow(window);
canvas3.width = 32;
canvas3.height = 32;
var snapshotSmall = snapshotWindow(window);
document.body.removeChild(canvas3);
// Create a small canvas then grow.
var canvas4 = createCanvas(32, 32);
var ctx4 = canvas4.getContext("bitmaprenderer");
ctx4.transferImageBitmap(bitmaps[1]);
var snapshotSmallRef = snapshotWindow(window);
canvas4.width = 128;
canvas4.height = 128;
var snapshotLarge = snapshotWindow(window);
document.body.removeChild(canvas4);
var resultsLarge = compareSnapshots(snapshotLarge, snapshotLargeRef, true);
ok(resultsLarge[0], "Screenshots should be the same");
var resultsSmall = compareSnapshots(snapshotSmall, snapshotSmallRef, true);
ok(resultsSmall[0], "Screenshots should be the same");
runTestOnWorker();
});
}
function runTestOnWorker() {
var canvas1 = createCanvas(64, 64);
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
var worker = new Worker(window.URL.createObjectURL(blob));
createImageBitmap(canvas1).then(function(bmp) {
worker.postMessage({bitmap: bmp}, [bmp]);
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "bitmap") {
var canvas2 = createCanvas(64, 64);
var ctx2 = canvas2.getContext('bitmaprenderer');
ctx2.transferImageBitmap(event.data.bitmap);
ok(canvas1.toDataURL() == canvas2.toDataURL(), 'toDataURL should be the same');
SimpleTest.finish();
}
}
});
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
]}, runTest.bind(this, 64, 64, runTest.bind(this, 128, 128, scaleTest)));
</script>
</body>
</html>

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

@ -0,0 +1,93 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<script type="text/js-worker">
function ok(expect, msg) {
postMessage({"type": "status", status: !!expect, msg: msg});
}
onmessage = function(event) {
var bitmap = event.data.bitmap;
ok(!!bitmap, "Get the ImageBitmap from the main script.");
bitmap.close();
ok(bitmap.width == 0 && bitmap.height == 0, "After close(), width and height should return 0");
postMessage({"type": "finish"});
}
</script>
<script>
SimpleTest.waitForExplicitFinish();
function createCanvas() {
var htmlCanvas = document.createElement('canvas');
htmlCanvas.width = 64;
htmlCanvas.height = 64;
document.body.appendChild(htmlCanvas);
return htmlCanvas;
}
function runTest() {
var canvas1 = createCanvas();
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var canvasRef = createCanvas();
var ctx = canvasRef.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
createImageBitmap(canvas1).then(function(bmp) {
var canvas2 = createCanvas();
var ctx2 = canvas2.getContext("2d");
ctx2.drawImage(bmp, 0, 0);
ok(canvasRef.toDataURL() == canvas2.toDataURL(), "toDataURL should return same result.");
document.body.removeChild(canvas2);
bmp.close();
ok(bmp.width == 0 && bmp.height == 0, "After close(), width and height should return 0");
var canvas2 = createCanvas();
var ctx2 = canvas2.getContext("2d");
var beforeDrawImageDataURL = canvas2.toDataURL();
ctx2.drawImage(bmp, 0, 0);
var afterDrawImageDataURL = canvas2.toDataURL();
ok(beforeDrawImageDataURL == afterDrawImageDataURL,
"Drawing operations with a closed ImageBitmap should do nothing.");
runTestOnWorker();
});
}
function runTestOnWorker() {
var canvas1 = createCanvas();
var ctx = canvas1.getContext("2d");
ctx.fillStyle = "#00FF00";
ctx.fillRect(0, 0, 64, 64);
var blob = new Blob(Array.prototype.map.call(document.querySelectorAll("script[type=\"text\/js-worker\"]"), function (oScript) { return oScript.textContent; }),{type: "text/javascript"});
var worker = new Worker(window.URL.createObjectURL(blob));
createImageBitmap(canvas1).then(function(bmp) {
worker.postMessage({bitmap: bmp}, [bmp]);
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "finish") {
SimpleTest.finish();
}
}
});
}
runTest();
</script>
</body>
</html>

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

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<title>Test ImageBitmap : Transfer</title>
<meta charset="utf-8">
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<body>
<script type="text/javascript">
var gImage1;
var gImage2;
var gImageBitmap1;
var gImageBitmap2;
function isPixel(ctx1, ctx2, x, y) {
var pixel1 = ctx1.getImageData(x, y, 1, 1);
var pixel2 = ctx2.getImageData(x, y, 1, 1);
ok(pixel1.data[0] == pixel2.data[0] &&
pixel1.data[1] == pixel2.data[1] &&
pixel1.data[2] == pixel2.data[2] &&
pixel1.data[3] == pixel2.data[3],
"Color(" + pixel1.data[0] + ", " + pixel1.data[1] + ", " + pixel1.data[2] + ", " + pixel1.data[3] + ") should qual to Color(" + pixel2.data[0] + ", " + pixel2.data[1] + ", " + pixel2.data[2] + ", " + pixel2.data[3] + ")");
}
function compareImageBitmapWithImageElement(imageBitmap, imageElement) {
var canvas1 = document.createElement('canvas');
var canvas2 = document.createElement('canvas');
canvas1.width = imageElement.naturalWidth;
canvas1.height = imageElement.naturalHeight;
canvas2.width = imageElement.naturalWidth;
canvas2.height = imageElement.naturalHeight;
var ctx1 = canvas1.getContext('2d');
var ctx2 = canvas2.getContext('2d');
ctx1.drawImage(imageElement, 0, 0);
ctx2.drawImage(imageBitmap, 0, 0);
document.body.appendChild(canvas1);
document.body.appendChild(canvas2);
for (var t = 0; t < 20; ++t) {
// check one random pixel
var randomX = Math.floor(Math.random() * imageElement.naturalWidth);
var randomY = Math.floor(Math.random() * imageElement.naturalHeight);
isPixel(ctx1, ctx2, randomX, randomY);
}
}
var worker = new Worker("imagebitmap_structuredclone.js");
worker.onmessage = function(event) {
if (event.data.type == "status") {
ok(event.data.status, event.data.msg);
} else if (event.data.type == "finish") {
SimpleTest.finish();
} else if (event.data.type == "bitmap1") {
compareImageBitmapWithImageElement(event.data.bitmap, gImage1);
} else if (event.data.type == "bitmap2") {
compareImageBitmapWithImageElement(event.data.bitmap, gImage2);
}
}
function prepareTwoImageBitmap() {
gImage1 = document.createElement('img');
gImage2 = document.createElement('img');
gImage1.src = "image_rgrg-256x256.png";
gImage2.src = "image_yellow.png";
var p1 = new Promise(function(resolve, reject) {
gImage1.onload = function() {
var promise = createImageBitmap(gImage1);
promise.then(function(bitmap) {
gImageBitmap1 = bitmap;
resolve(true);
});
}
});
var p2 = new Promise(function(resolve, reject) {
gImage2.onload = function() {
var promise = createImageBitmap(gImage2);
promise.then(function(bitmap) {
gImageBitmap2 = bitmap;
resolve(true);
});
}
});
return Promise.all([p1, p2]);
}
function runTests() {
ok(worker, "Worker created successfully.");
prepareTwoImageBitmap().then(function(){
worker.postMessage({"bitmap1":gImageBitmap1, "bitmap2":gImageBitmap2},
[gImageBitmap1, gImageBitmap2]);
ok(gImageBitmap1.width == 0 && gImageBitmap1.height == 0,
"After transfer, ImageBitmap become neutered");
ok(gImageBitmap2.width == 0 && gImageBitmap2.height == 0,
"After transfer, ImageBitmap become neutered");
});
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTests);
</script>
</body>

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

@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="offscreencanvas.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<canvas id="c" width="64" height="64"></canvas>
<canvas id="c-mt" width="64" height="64"></canvas>
<canvas id="c-ref" width="64" height="64"></canvas>
<script>
SimpleTest.waitForExplicitFinish();
function testBlob(blob, callback) {
// testing toBlob
// Fill c-ref with green color.
var c = document.getElementById("c-ref");
var ctx = c.getContext("2d");
ctx.rect(0, 0, 64, 64);
ctx.fillStyle = "#00FF00";
ctx.fill();
var reader = new FileReader();
reader.onload = function(e) {
ok(c.toDataURL() == e.target.result, "toBlob should return a 64x64 green square");
callback();
};
reader.readAsDataURL(blob);
}
function runTestOnMainThread() {
var htmlCanvas = document.getElementById("c-mt");
ok(htmlCanvas, "Should have HTML canvas element");
window.onmessage = function(evt) {
var msg = evt.data || {};
if (msg.type == "test") {
ok(msg.result, msg.name);
}
if (msg.type == "blob") {
testBlob(msg.blob, SimpleTest.finish);
}
}
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
entryFunction('webgl_toblob', '', offscreenCanvas);
}
function runTest() {
var htmlCanvas = document.getElementById("c");
var worker = new Worker("offscreencanvas.js");
ok(htmlCanvas, "Should have HTML canvas element");
ok(worker, "Web worker successfully created");
worker.onmessage = function(evt) {
var msg = evt.data || {};
if (msg.type == "test") {
ok(msg.result, msg.name);
}
if (msg.type == "blob") {
testBlob(msg.blob, function() {
worker.terminate();
runTestOnMainThread();
});
}
}
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
worker.postMessage({test: 'webgl_toblob', canvas: offscreenCanvas}, [offscreenCanvas]);
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
['webgl.force-enabled', true],
]}, runTest);
</script>
</body>
</html>

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

@ -0,0 +1,69 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebGL in OffscreenCanvas</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
</head>
<body>
<canvas id="c" width="64" height="64"></canvas>
<canvas id="c2" width="64" height="64"></canvas>
<canvas id="c-ref" width="64" height="64"></canvas>
<script>
SimpleTest.waitForExplicitFinish();
function runTest() {
var worker = new Worker("offscreencanvas.js");
ok(worker, "Web worker successfully created");
worker.onmessage = function(evt) {
var msg = evt.data || {};
if (msg.type == "test") {
ok(msg.result, msg.name);
}
if (msg.type == "imagebitmap") {
// testing toBlob
// Fill c-ref with green color.
var c = document.getElementById("c-ref");
var ctx = c.getContext("2d");
ctx.rect(0, 0, 64, 64);
ctx.fillStyle = "#00FF00";
ctx.fill();
var htmlCanvas = document.getElementById("c");
var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
bitmapRenderer.transferImageBitmap(msg.bitmap);
ok(c.toDataURL() == htmlCanvas.toDataURL(),
"imagebitmap should return a 64x64 green square");
// The ownership of msg.bitmap should be transferred to canvas "c" when
// we called transferImageBitmap. So we test if the ownership is actually
// transferred here.
var htmlCanvas = document.getElementById("c2");
var bitmapRenderer = htmlCanvas.getContext("bitmaprenderer");
bitmapRenderer.transferImageBitmap(msg.bitmap);
SimpleTest.doesThrow(
function() { c2.toDataURL(); },
"ImageBitmap has been transferred, toDataURL will throw.");
worker.terminate();
SimpleTest.finish();
}
}
worker.postMessage({test: 'webgl_imagebitmap'});
}
SpecialPowers.pushPrefEnv({'set': [
['gfx.offscreencanvas.enabled', true],
['webgl.force-enabled', true],
]}, runTest);
</script>
</body>
</html>

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

@ -13,7 +13,6 @@ fail-if = (os == 'b2g')
support-files = captureStream_common.js
[webgl-mochitest/test_cubemap_must_be_square.html]
[webgl-mochitest/test_depth_tex_lazy_clear.html]
skip-if = 1
[webgl-mochitest/test_draw.html]
[webgl-mochitest/test_fb_param.html]
[webgl-mochitest/test_fb_param_crash.html]

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

@ -374,7 +374,8 @@ HTMLCanvasElement::~HTMLCanvasElement()
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
mCurrentContext, mPrintCallback,
mPrintState, mOriginalCanvas)
mPrintState, mOriginalCanvas,
mOffscreenCanvas)
NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
@ -779,10 +780,17 @@ HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
renderer->SetWidth(sz.width);
renderer->SetHeight(sz.height);
mOffscreenCanvas = new OffscreenCanvas(sz.width,
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(OwnerDoc()->GetInnerWindow());
mOffscreenCanvas = new OffscreenCanvas(global,
sz.width,
sz.height,
GetCompositorBackendType(),
renderer);
if (mWriteOnly) {
mOffscreenCanvas->SetWriteOnly();
}
if (!mContextObserver) {
mContextObserver = new HTMLCanvasElementObserver(this);
}
@ -1038,9 +1046,9 @@ HTMLCanvasElement::GetOpaqueAttr()
return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
}
already_AddRefed<CanvasLayer>
already_AddRefed<Layer>
HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
Layer *aOldLayer,
LayerManager *aManager)
{
// The address of sOffscreenCanvasLayerUserDataDummy is used as the user
@ -1056,7 +1064,7 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
if (mOffscreenCanvas) {
if (!mResetLayer &&
aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
RefPtr<CanvasLayer> ret = aOldLayer;
RefPtr<Layer> ret = aOldLayer;
return ret.forget();
}
@ -1173,7 +1181,7 @@ void
HTMLCanvasElement::SetFrameCapture(already_AddRefed<SourceSurface> aSurface)
{
RefPtr<SourceSurface> surface = aSurface;
RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), surface);
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), surface);
// Loop backwards to allow removing elements in the loop.
for (int i = mRequestedFrameListeners.Length() - 1; i >= 0; --i) {

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

@ -31,6 +31,7 @@ namespace layers {
class AsyncCanvasRenderer;
class CanvasLayer;
class Image;
class Layer;
class LayerManager;
} // namespace layers
namespace gfx {
@ -120,6 +121,7 @@ class HTMLCanvasElement final : public nsGenericHTMLElement,
typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
typedef layers::CanvasLayer CanvasLayer;
typedef layers::Layer Layer;
typedef layers::LayerManager LayerManager;
public:
@ -308,8 +310,8 @@ public:
* Helpers called by various users of Canvas
*/
already_AddRefed<CanvasLayer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
CanvasLayer *aOldLayer,
already_AddRefed<Layer> GetCanvasLayer(nsDisplayListBuilder* aBuilder,
Layer *aOldLayer,
LayerManager *aManager);
// Should return true if the canvas layer should always be marked inactive.
// We should return true here if we can't do accelerated compositing with

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

@ -996,7 +996,7 @@ HTMLImageElement::PictureSourceSrcsetChanged(nsIContent *aSourceNode,
mResponsiveSelector->SetCandidatesFromSourceSet(aNewValue);
}
if (!mInDocResponsiveContent) {
if (!mInDocResponsiveContent && IsInComposedDoc()) {
nsIDocument* doc = GetOurOwnerDoc();
if (doc) {
doc->AddResponsiveContent(this);

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

@ -295,20 +295,25 @@ NS_IMPL_ISUPPORTS(UploadLastDir::ContentPrefCallback, nsIContentPrefCallback2)
NS_IMETHODIMP
UploadLastDir::ContentPrefCallback::HandleCompletion(uint16_t aReason)
{
nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
NS_ENSURE_STATE(localFile);
if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR ||
!mResult) {
// Default to "desktop" directory for each platform
nsCOMPtr<nsIFile> homeDir;
NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(homeDir));
localFile = do_QueryInterface(homeDir);
} else {
nsCOMPtr<nsIFile> localFile;
nsAutoString prefStr;
if (aReason == nsIContentPrefCallback2::COMPLETE_ERROR || !mResult) {
prefStr = Preferences::GetString("dom.input.fallbackUploadDir");
if (prefStr.IsEmpty()) {
// If no custom directory was set through the pref, default to
// "desktop" directory for each platform.
NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(localFile));
}
}
if (!localFile) {
if (prefStr.IsEmpty() && mResult) {
nsCOMPtr<nsIVariant> pref;
mResult->GetValue(getter_AddRefs(pref));
pref->GetAsAString(prefStr);
}
localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
localFile->InitWithPath(prefStr);
}

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

@ -600,3 +600,5 @@ skip-if = buildapp == 'b2g' # bug 1129014
[test_image_clone_load.html]
[test_bug1203668.html]
[test_bug1166138.html]
[test_filepicker_default_directory.html]
skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'

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

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1194893
-->
<head>
<title>Test for filepicker default directory</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1194893">Mozilla Bug 1194893</a>
<div id="content">
<input type="file" id="f">
</div>
<pre id="text">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
const { Cc: Cc, Ci: Ci } = SpecialPowers;
// Platform-independent directory names are #define'd in xpcom/io/nsDirectoryServiceDefs.h
var defaultUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("Desk", Ci.nsIFile);
// When we want to test an upload directory other than the default, we need to
// get a valid directory in a platform-independent way. Since NS_OS_DESKTOP_DIR
// may fallback to NS_OS_HOME_DIR, let's use NS_OS_TMP_DIR.
var customUploadDirectory = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("TmpD", Ci.nsIFile);
// Useful for debugging
//info("defaultUploadDirectory" + defaultUploadDirectory.path);
//info("customUploadDirectory" + customUploadDirectory.path);
var MockFilePicker = SpecialPowers.MockFilePicker;
MockFilePicker.init(window);
// need to show the MockFilePicker so .displayDirectory gets set
var f = document.getElementById("f");
f.focus();
var testIndex = 0;
var tests = [
["", defaultUploadDirectory.path],
[customUploadDirectory.path, customUploadDirectory.path]
]
MockFilePicker.showCallback = function(filepicker) {
info(SpecialPowers.wrap(MockFilePicker).displayDirectory.path);
is(SpecialPowers.wrap(MockFilePicker).displayDirectory.path,
tests[testIndex][1]);
if (++testIndex == tests.length) {
MockFilePicker.cleanup();
SimpleTest.finish();
} else {
launchNextTest();
}
}
function launchNextTest() {
SpecialPowers.pushPrefEnv(
{ 'set': [
['dom.input.fallbackUploadDir', tests[testIndex][0]],
]},
function () {
f.click();
});
}
launchNextTest();
</script>
</pre>
</body>
</html>

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

@ -44,7 +44,7 @@ StructuredCloneData::Copy(const StructuredCloneData& aData)
MOZ_ASSERT(BlobImpls().IsEmpty());
BlobImpls().AppendElements(aData.BlobImpls());
MOZ_ASSERT(GetImages().IsEmpty());
MOZ_ASSERT(GetSurfaces().IsEmpty());
return true;
}

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

@ -790,6 +790,7 @@ MediaDecoder::Seek(double aTime, SeekTarget::Type aSeekType)
UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
MOZ_ASSERT(!mIsDormant, "should be out of dormant by now");
MOZ_ASSERT(aTime >= 0.0, "Cannot seek to a negative value.");
int64_t timeUsecs = 0;

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

@ -782,6 +782,17 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
self->WaitRequestRef(aRejection.mType).Complete();
}));
// We are out of data to decode and will enter buffering mode soon.
// We want to play the frames we have already decoded, so we stop pre-rolling
// and ensure that loadeddata is fired as required.
if (isAudio) {
StopPrerollingAudio();
} else {
StopPrerollingVideo();
}
if (mState == DECODER_STATE_BUFFERING || mState == DECODER_STATE_DECODING) {
MaybeFinishDecodeFirstFrame();
}
return;
}
@ -1481,7 +1492,7 @@ MediaDecoderStateMachine::Seek(SeekTarget aTarget)
return MediaDecoder::SeekPromise::CreateAndReject(/* aIgnored = */ true, __func__);
}
NS_ASSERTION(mState > DECODER_STATE_DECODING_METADATA,
MOZ_ASSERT(mState > DECODER_STATE_DECODING_METADATA,
"We should have got duration already");
if (mState < DECODER_STATE_DECODING ||

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

@ -9,6 +9,7 @@
#include "mozilla/AbstractThread.h"
#include "mozilla/Atomics.h"
#include "mozilla/IndexSequence.h"
#include "mozilla/Mutex.h"
#include "mozilla/Tuple.h"
#include "mozilla/TypeTraits.h"

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

@ -546,6 +546,9 @@ MediaFormatReader::OnDemuxFailed(TrackType aTrack, DemuxerFailureReason aFailure
NotifyError(aTrack);
break;
case DemuxerFailureReason::WAITING_FOR_DATA:
if (!decoder.mWaitingForData) {
decoder.mNeedDraining = true;
}
NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::CANCELED:
@ -691,6 +694,9 @@ MediaFormatReader::NotifyWaitingForData(TrackType aTrack)
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mWaitingForData = true;
if (decoder.mTimeThreshold) {
decoder.mTimeThreshold.ref().mWaiting = true;
}
ScheduleUpdate(aTrack);
}
@ -760,17 +766,31 @@ MediaFormatReader::UpdateReceivedNewData(TrackType aTrack)
if (!decoder.mReceivedNewData) {
return false;
}
decoder.mReceivedNewData = false;
decoder.mWaitingForData = false;
bool hasLastEnd;
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
// Update our cached TimeRange.
decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered();
if (decoder.mTimeRanges.Length() &&
(!hasLastEnd || decoder.mTimeRanges.GetEnd() > lastEnd)) {
if (decoder.mDrainComplete || decoder.mDraining) {
// We do not want to clear mWaitingForData or mDemuxEOS while
// a drain is in progress in order to properly complete the operation.
return false;
}
bool hasLastEnd;
media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
if (hasLastEnd) {
if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() > lastEnd) {
// New data was added after our previous end, we can clear the EOS flag.
decoder.mDemuxEOS = false;
}
decoder.mLastTimeRangesEnd = Some(lastEnd);
}
decoder.mReceivedNewData = false;
if (decoder.mTimeThreshold) {
decoder.mTimeThreshold.ref().mWaiting = false;
}
decoder.mWaitingForData = false;
if (decoder.mError) {
return false;
@ -808,6 +828,8 @@ MediaFormatReader::RequestDemuxSamples(TrackType aTrack)
if (decoder.mDemuxEOS) {
// Nothing left to demux.
// We do not want to attempt to demux while in waiting for data mode
// as it would retrigger an unecessary drain.
return;
}
@ -883,6 +905,7 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
info->GetID());
decoder.mInfo = info;
decoder.mLastStreamSourceID = info->GetID();
decoder.mNextStreamSourceID.reset();
// Flush will clear our array of queued samples. So make a copy now.
nsTArray<RefPtr<MediaRawData>> samples{decoder.mQueuedSamples};
Flush(aTrack);
@ -892,38 +915,11 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
decoder.mQueuedSamples.AppendElements(Move(samples));
NotifyDecodingRequested(aTrack);
} else {
MOZ_ASSERT(decoder.mTimeThreshold.isNothing());
SeekTarget seekTarget =
decoder.mTimeThreshold.refOr(SeekTarget(TimeUnit::FromMicroseconds(sample->mTime), false));
LOG("Stream change occurred on a non-keyframe. Seeking to:%lld",
sample->mTime);
decoder.mTimeThreshold = Some(TimeUnit::FromMicroseconds(sample->mTime));
RefPtr<MediaFormatReader> self = this;
decoder.ResetDemuxer();
decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref())
->Then(OwnerThread(), __func__,
[self, aTrack] (media::TimeUnit aTime) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
self->NotifyDecodingRequested(aTrack);
},
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::END_OF_STREAM:
self->NotifyEndOfStream(aTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::SHUTDOWN:
break;
default:
self->NotifyError(aTrack);
break;
}
decoder.mTimeThreshold.reset();
}));
seekTarget.mTime.ToMicroseconds());
InternalSeek(aTrack, seekTarget);
}
return;
}
@ -957,6 +953,42 @@ MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
decoder.mInputExhausted = false;
}
void
MediaFormatReader::InternalSeek(TrackType aTrack, const SeekTarget& aTarget)
{
MOZ_ASSERT(OnTaskQueue());
auto& decoder = GetDecoderData(aTrack);
decoder.mTimeThreshold = Some(aTarget);
RefPtr<MediaFormatReader> self = this;
decoder.ResetDemuxer();
decoder.mSeekRequest.Begin(decoder.mTrackDemuxer->Seek(decoder.mTimeThreshold.ref().mTime)
->Then(OwnerThread(), __func__,
[self, aTrack] (media::TimeUnit aTime) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
self->NotifyDecodingRequested(aTrack);
},
[self, aTrack] (DemuxerFailureReason aResult) {
auto& decoder = self->GetDecoderData(aTrack);
decoder.mSeekRequest.Complete();
switch (aResult) {
case DemuxerFailureReason::WAITING_FOR_DATA:
self->NotifyWaitingForData(aTrack);
break;
case DemuxerFailureReason::END_OF_STREAM:
self->NotifyEndOfStream(aTrack);
break;
case DemuxerFailureReason::CANCELED:
case DemuxerFailureReason::SHUTDOWN:
break;
default:
self->NotifyError(aTrack);
break;
}
decoder.mTimeThreshold.reset();
}));
}
void
MediaFormatReader::DrainDecoder(TrackType aTrack)
{
@ -992,7 +1024,6 @@ MediaFormatReader::Update(TrackType aTrack)
LOGV("Processing update for %s", TrackTypeToStr(aTrack));
bool needInput = false;
bool needOutput = false;
auto& decoder = GetDecoderData(aTrack);
decoder.mUpdateScheduled = false;
@ -1006,87 +1037,108 @@ MediaFormatReader::Update(TrackType aTrack)
return;
}
if (!decoder.HasPromise() && decoder.mWaitingForData) {
// Nothing more we can do at present.
LOGV("Still waiting for data.");
return;
}
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
if (aTrack == TrackInfo::kVideoTrack) {
uint64_t delta =
decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
a.mDecoded = static_cast<uint32_t>(delta);
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
// Drop any frames found prior our internal seek target.
while (decoder.mTimeThreshold && decoder.mOutput.Length()) {
RefPtr<MediaData>& output = decoder.mOutput[0];
SeekTarget target = decoder.mTimeThreshold.ref();
media::TimeUnit time = media::TimeUnit::FromMicroseconds(output->mTime);
if (time >= target.mTime) {
// We have reached our internal seek target.
decoder.mTimeThreshold.reset();
}
if (time < target.mTime || target.mDropTarget) {
LOGV("Internal Seeking: Dropping %s frame time:%f wanted:%f (kf:%d)",
TrackTypeToStr(aTrack),
media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
target.mTime.ToSeconds(),
output->mKeyframe);
decoder.mOutput.RemoveElementAt(0);
}
}
if (decoder.HasPromise()) {
needOutput = true;
if (!decoder.mOutput.IsEmpty()) {
if (decoder.mOutput.Length()) {
// We have a decoded sample ready to be returned.
if (aTrack == TrackType::kVideoTrack) {
uint64_t delta =
decoder.mNumSamplesOutputTotal - mLastReportedNumDecodedFrames;
a.mDecoded = static_cast<uint32_t>(delta);
mLastReportedNumDecodedFrames = decoder.mNumSamplesOutputTotal;
nsCString error;
mVideo.mIsHardwareAccelerated =
mVideo.mDecoder && mVideo.mDecoder->IsHardwareAccelerated(error);
}
while (decoder.mOutput.Length()) {
RefPtr<MediaData> output = decoder.mOutput[0];
decoder.mOutput.RemoveElementAt(0);
decoder.mSizeOfQueue -= 1;
if (decoder.mTimeThreshold.isNothing() ||
media::TimeUnit::FromMicroseconds(output->mTime) >= decoder.mTimeThreshold.ref()) {
decoder.mLastSampleTime =
Some(media::TimeUnit::FromMicroseconds(output->mTime));
ReturnOutput(output, aTrack);
decoder.mTimeThreshold.reset();
break;
} else {
LOGV("Internal Seeking: Dropping frame time:%f wanted:%f (kf:%d)",
media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(),
decoder.mTimeThreshold.ref().ToSeconds(),
output->mKeyframe);
}
}
} else if (decoder.mError) {
LOG("Rejecting %s promise: DECODE_ERROR", TrackTypeToStr(aTrack));
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mDrainComplete) {
bool wasDraining = decoder.mDraining;
decoder.mDrainComplete = false;
decoder.mDraining = false;
if (decoder.mError) {
LOG("Decoding Error");
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mDemuxEOS) {
if (decoder.mDemuxEOS) {
LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
decoder.RejectPromise(END_OF_STREAM, __func__);
}
} else if (decoder.mError) {
decoder.RejectPromise(DECODE_ERROR, __func__);
return;
} else if (decoder.mWaitingForData) {
LOG("Waiting For Data");
if (wasDraining && decoder.mLastSampleTime &&
!decoder.mNextStreamSourceID) {
// We have completed draining the decoder following WaitingForData.
// Set up the internal seek machinery to be able to resume from the
// last sample decoded.
LOG("Seeking to last sample time: %lld",
decoder.mLastSampleTime.ref().ToMicroseconds());
InternalSeek(aTrack, SeekTarget(decoder.mLastSampleTime.ref(), true));
}
LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
decoder.RejectPromise(WAITING_FOR_DATA, __func__);
}
// Now that draining has completed, we check if we have received
// new data again as the result may now be different from the earlier
// run.
if (UpdateReceivedNewData(aTrack)) {
LOGV("Nothing more to do");
return;
}
}
}
if (decoder.mNeedDraining) {
DrainDecoder(aTrack);
return;
}
if (!NeedInput(decoder)) {
bool needInput = NeedInput(decoder);
LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u waiting:%d ahead:%d sid:%u",
TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
decoder.mWaitingForData, !decoder.HasPromise(), decoder.mLastStreamSourceID);
if (decoder.mWaitingForData &&
(!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) {
// Nothing more we can do at present.
LOGV("Still waiting for data.");
return;
}
if (!needInput) {
LOGV("No need for additional input (pending:%u)",
uint32_t(decoder.mOutput.Length()));
return;
}
needInput = true;
LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u ahead:%d sid:%u",
TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
!decoder.HasPromise(), decoder.mLastStreamSourceID);
// Demux samples if we don't have some.
RequestDemuxSamples(aTrack);

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

@ -127,6 +127,22 @@ private:
// Decode any pending already demuxed samples.
bool DecodeDemuxedSamples(TrackType aTrack,
MediaRawData* aSample);
struct SeekTarget {
SeekTarget(const media::TimeUnit& aTime, bool aDropTarget)
: mTime(aTime)
, mDropTarget(aDropTarget)
, mWaiting(false)
{}
media::TimeUnit mTime;
bool mDropTarget;
bool mWaiting;
};
// Perform an internal seek to aTime. If aDropTarget is true then
// the first sample past the target will be dropped.
void InternalSeek(TrackType aTrack, const SeekTarget& aTarget);
// Drain the current decoder.
void DrainDecoder(TrackType aTrack);
void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
@ -261,8 +277,11 @@ private:
bool mDraining;
bool mDrainComplete;
// If set, all decoded samples prior mTimeThreshold will be dropped.
// Used for internal seeking when a change of stream is detected.
Maybe<media::TimeUnit> mTimeThreshold;
// Used for internal seeking when a change of stream is detected or when
// encountering data discontinuity.
Maybe<SeekTarget> mTimeThreshold;
// Time of last sample returned.
Maybe<media::TimeUnit> mLastSampleTime;
// Decoded samples returned my mDecoder awaiting being returned to
// state machine upon request.
@ -300,6 +319,7 @@ private:
mDraining = false;
mDrainComplete = false;
mTimeThreshold.reset();
mLastSampleTime.reset();
mOutput.Clear();
mNumSamplesInput = 0;
mNumSamplesOutput = 0;
@ -316,6 +336,7 @@ private:
uint32_t mLastStreamSourceID;
Maybe<uint32_t> mNextStreamSourceID;
media::TimeIntervals mTimeRanges;
Maybe<media::TimeUnit> mLastTimeRangesEnd;
RefPtr<SharedTrackInfo> mInfo;
};

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

@ -480,7 +480,10 @@ DecodedStream::CreateData(MozPromiseHolder<GenericPromise>&& aPromise)
self->mOutputStreamManager.Disconnect();
delete data;
});
AbstractThread::MainThread()->Dispatch(r.forget());
// We are in tail dispatching phase. Don't call
// AbstractThread::MainThread()->Dispatch() to avoid reentrant
// AutoTaskDispatcher.
NS_DispatchToMainThread(r.forget());
}
}
RefPtr<DecodedStream> mThis;

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

@ -4,6 +4,7 @@
* 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 "MediaQueue.h"
#include "VideoSink.h"
namespace mozilla {

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

@ -295,7 +295,7 @@ MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
, mManager(aManager)
, mType(aType)
, mMonitor("MediaSourceTrackDemuxer")
, mLastSeek(Some(TimeUnit()))
, mReset(true)
{
}
@ -328,7 +328,8 @@ MediaSourceTrackDemuxer::Reset()
RefPtr<MediaSourceTrackDemuxer> self = this;
nsCOMPtr<nsIRunnable> task =
NS_NewRunnableFunction([self] () {
self->mLastSeek = Some(TimeUnit());
self->mNextSample.reset();
self->mReset = true;
self->mManager->Seek(self->mType, TimeUnit(), TimeUnit());
{
MonitorAutoLock mon(self->mMonitor);
@ -380,42 +381,50 @@ MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
if (!buffered.Contains(aTime)) {
mLastSeek = Some(aTime);
// We don't have the data to seek to.
return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
__func__);
}
TimeUnit seekTime =
mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
bool error;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
media::TimeUnit(),
error);
MOZ_ASSERT(!error && sample);
mNextSample = Some(sample);
mReset = false;
{
MonitorAutoLock mon(mMonitor);
mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
}
mLastSeek = Some(aTime);
return SeekPromise::CreateAndResolve(seekTime, __func__);
}
RefPtr<MediaSourceTrackDemuxer::SamplesPromise>
MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
{
if (mLastSeek) {
if (mReset) {
// If a seek (or reset) was recently performed, we ensure that the data
// we are about to retrieve is still available.
TimeIntervals buffered = mManager->Buffered(mType);
buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
if (!buffered.Contains(mLastSeek.ref())) {
if (!buffered.Contains(TimeUnit::FromMicroseconds(0))) {
return SamplesPromise::CreateAndReject(
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
mLastSeek.reset();
mReset = false;
}
bool error;
RefPtr<MediaRawData> sample =
mManager->GetSample(mType,
MediaSourceDemuxer::EOS_FUZZ,
error);
bool error = false;
RefPtr<MediaRawData> sample;
if (mNextSample) {
sample = mNextSample.ref();
mNextSample.reset();
} else {
sample = mManager->GetSample(mType, MediaSourceDemuxer::EOS_FUZZ, error);
if (!sample) {
if (error) {
return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
@ -424,6 +433,7 @@ MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
DemuxerFailureReason::WAITING_FOR_DATA, __func__);
}
}
RefPtr<SamplesHolder> samples = new SamplesHolder;
samples->mSamples.AppendElement(sample);
if (mNextRandomAccessPoint.ToMicroseconds() <= sample->mTime) {

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

@ -129,7 +129,10 @@ private:
// Monitor protecting members below accessed from multiple threads.
Monitor mMonitor;
media::TimeUnit mNextRandomAccessPoint;
Maybe<media::TimeUnit> mLastSeek;
Maybe<RefPtr<MediaRawData>> mNextSample;
// Set to true following a reset. Ensure that the next sample demuxed
// is available at position 0.
bool mReset;
};
} // namespace mozilla

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

@ -40,7 +40,9 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
[test_BufferingWait.html]
skip-if = toolkit == 'android' #timeout android bug 1199531
[test_BufferingWait_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_DrainOnMissingData_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_EndOfStream.html]
skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946
[test_EndOfStream_mp4.html]
@ -51,7 +53,7 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
[test_FrameSelection.html]
[test_HaveMetadataUnbufferedSeek.html]
[test_HaveMetadataUnbufferedSeek_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_LoadedDataFired_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_LoadedMetadataFired.html]
@ -98,11 +100,11 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_TruncatedDuration.html]
[test_TruncatedDuration_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_WaitingOnMissingData.html]
skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized.
[test_WaitingOnMissingData_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_WaitingToEndedTransition_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac") || (os == "win" && os_version == "6.1")) # Only supported on osx and vista+, disabling on win7 bug 1191138
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+

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

@ -41,14 +41,15 @@ runWithMSE(function(ms, v) {
/* Note - Missing |46712, 67833 - 46712| segment here corresponding to (0.8, 1.2] */
/* Note - Missing |67833, 88966 - 67833| segment here corresponding to (1.2, 1.6] */
loadSegment.bind(null, sb, new Uint8Array(arrayBuffer, 88966))).then(function() {
var promise = waitUntilTime(0.27);
// 0.767 is the time of the last video sample +- 40ms.
var promise = waitUntilTime(.767-0.04);
info("Playing video. It should play for a bit, then fire 'waiting'");
v.play();
return promise;
}).then(function() {
window.firstStop = Date.now();
loadSegment(sb, new Uint8Array(arrayBuffer, 46712, 67833 - 46712));
return waitUntilTime(0.66);
return waitUntilTime(1.167-0.04);
}).then(function() {
var waitDuration = (Date.now() - window.firstStop) / 1000;
ok(waitDuration < 15, "Should not spend an inordinate amount of time buffering: " + waitDuration);

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

@ -41,18 +41,16 @@ runWithMSE(function(ms, v) {
/* Note - Missing |bipbop4| segment here corresponding to (2.41, 3.20] */
.then(fetchAndLoad.bind(null, sb, 'bipbop/bipbop', ['5'], '.m4s'))
.then(function() {
// Some decoders (Windows in particular) may keep up to 25 frames queued
// before returning a sample. 0.7 is 1.62s - 25 * 0.03333
var promise = waitUntilTime(0.7);
// last audio sample has a start time of 1.578956s
var promise = waitUntilTime(1.57895);
info("Playing video. It should play for a bit, then fire 'waiting'");
v.play();
return promise;
}).then(function() {
window.firstStop = Date.now();
fetchAndLoad(sb, 'bipbop/bipbop', ['3'], '.m4s');
// Some decoders (Windows in particular) may keep up to 25 frames queued
// before returning a sample. 1.5 is 2.41s - 25 * 0.03333
return waitUntilTime(1.5);
// last audio sample has a start time of 2.368435
return waitUntilTime(2.36843);
}).then(function() {
var waitDuration = (Date.now() - window.firstStop) / 1000;
ok(waitDuration < 15, "Should not spend an inordinate amount of time buffering: " + waitDuration);

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

@ -0,0 +1,60 @@
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=windows-1252">
<title>MSE: |waiting| event when source data is missing</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test"><script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
.then(function() {
// Set appendWindowEnd to ensure we only have about 6 frames worth.
// We must feed at least 6 frames to pass the MDSM pre-roll.
videosb.appendWindowEnd = .4;
return fetchAndLoad(videosb, 'bipbop/bipbop_video', ['1'], '.m4s');
})
.then(function() {
info("Invoking play()");
var promises = [];
promises.push(once(el, 'playing'));
el.play();
return Promise.all(promises);
})
.then(function() {
info("got playing");
return once(el, 'waiting');
}).then(function() {
info("got waiting");
info("Loading more data");
// Waiting will be fired on the last frame +- 40ms.
isfuzzy(el.currentTime, videosb.buffered.end(0) - 1/30,
0.04, "Got a waiting event at " + el.currentTime);
videosb.appendWindowEnd = 1;
var p = once(el, 'ended');
var loads = fetchAndLoad(videosb, 'bipbop/bipbop_video', [1], '.m4s');
loads.then(() => ms.endOfStream());
return p;
}).then(function() {
// These fuzz factors are bigger than they should be. We should investigate
// and fix them in bug 1137574.
is(el.duration, 0.801666, "Video has correct duration: " + el.duration);
is(el.currentTime, el.duration, "Video has correct currentTime.");
SimpleTest.finish();
});
});
});
</script>
</pre>
</body>
</html>

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

@ -24,6 +24,18 @@ SimpleTest.waitForExplicitFinish();
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {
// Log events for debugging.
var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
"loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
"waiting", "pause", "durationchange", "seeking", "seeked"];
function logEvent(e) {
var v = e.target;
info("got " + e.type + " event");
}
events.forEach(function(e) {
el.addEventListener(e, logEvent, false);
});
ok(true, "Receive a sourceopen event");
var videosb = ms.addSourceBuffer("video/mp4");
el.addEventListener("error", function(e) {
@ -37,9 +49,6 @@ runWithMSE(function(ms, el) {
var promises = [];
promises.push(once(el, 'loadeddata'));
promises.push(once(el, 'canplay'));
// Load [0, 1.601666). We must ensure that we load over 25 frames as the
// windows H264 decoder will not produce a sample until then
// (bug 1191138).
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 3), '.m4s'));
return Promise.all(promises);
})

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

@ -40,9 +40,9 @@ runWithMSE(function(ms, el) {
// currentTime is based on the current video frame, so if the audio ends just before
// the next video frame, currentTime can be up to 1 frame's worth earlier than
// min(audioEnd, videoEnd).
// Some decoders (Windows in particular) may keep up to 25 frames queued.
isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60,
25 * 1/30, "Got a waiting event at " + el.currentTime);
// 0.0465 is the length of the last audio frame.
ok(el.currentTime >= (Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 0.0465),
"Got a waiting event at " + el.currentTime);
info("Loading more data");
var p = once(el, 'ended');
var loads = Promise.all([fetchAndLoad(audiosb, 'bipbop/bipbop_audio', [5], '.m4s'),

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

@ -17,6 +17,10 @@ runWithMSE(function(ms, el) {
ok(true, "Receive a sourceopen event");
var audiosb = ms.addSourceBuffer("audio/mp4");
var videosb = ms.addSourceBuffer("video/mp4");
// ensure tracks end at approximately the same time to ensure ended event is
// always fired (bug 1233639).
audiosb.appendWindowEnd = 3.9;
videosb.appendWindowEnd = 3.9;
fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
.then(fetchAndLoad.bind(null, audiosb, 'bipbop/bipbop_audio', range(1, 5), '.m4s'))
@ -34,19 +38,12 @@ runWithMSE(function(ms, el) {
var p = once(el, 'waiting');
el.play();
return p;
}).then(function() {
// currentTime is based on the current video frame, so if the audio ends just before
// the next video frame, currentTime can be up to 1 frame's worth earlier than
// min(audioEnd, videoEnd).
// Some decoders (Windows in particular) may keep up to 25 frames queued.
isfuzzy(el.currentTime, Math.min(audiosb.buffered.end(0), videosb.buffered.end(0)) - 1/60,
25 * 1/30, "Got a waiting event at " + el.currentTime);
}).then(function() {
var p = once(el, 'ended');
ms.endOfStream();
return p;
}).then(function() {
is(el.duration, 4.005, "Video has correct duration: " + el.duration);
is(el.duration, 3.854512, "Video has correct duration: " + el.duration);
is(el.currentTime, el.duration, "Video has correct currentTime.");
SimpleTest.finish();
});

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

@ -226,23 +226,12 @@ OmxDataDecoder::DoAsyncShutdown()
mWatchManager.Unwatch(mOmxState, &OmxDataDecoder::OmxStateRunner);
mWatchManager.Unwatch(mPortSettingsChanged, &OmxDataDecoder::PortSettingsChanged);
// Do flush so all port can be returned to client.
// Flush to all ports, so all buffers can be returned from component.
RefPtr<OmxDataDecoder> self = this;
mOmxLayer->SendCommand(OMX_CommandFlush, OMX_ALL, nullptr)
->Then(mOmxTaskQueue, __func__,
[self] () -> RefPtr<OmxCommandPromise> {
LOG("DoAsyncShutdown: flush complete, collecting buffers...");
self->CollectBufferPromises(OMX_DirMax)
->Then(self->mOmxTaskQueue, __func__,
[self] () {
LOG("DoAsyncShutdown: releasing all buffers.");
self->ReleaseBuffers(OMX_DirInput);
self->ReleaseBuffers(OMX_DirOutput);
},
[self] () {
self->mOmxLayer->Shutdown();
});
LOG("DoAsyncShutdown: flush complete");
return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateIdle, nullptr);
},
[self] () {
@ -251,8 +240,30 @@ OmxDataDecoder::DoAsyncShutdown()
->CompletionPromise()
->Then(mOmxTaskQueue, __func__,
[self] () -> RefPtr<OmxCommandPromise> {
LOG("DoAsyncShutdown: OMX_StateIdle");
return self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
RefPtr<OmxCommandPromise> p =
self->mOmxLayer->SendCommand(OMX_CommandStateSet, OMX_StateLoaded, nullptr);
LOG("DoAsyncShutdown: collecting buffers...");
self->CollectBufferPromises(OMX_DirMax)
->Then(self->mOmxTaskQueue, __func__,
[self] () {
// According to spec 3.1.1.2.2.1:
// OMX_StateLoaded needs to be sent before releasing buffers.
// And state transition from OMX_StateIdle to OMX_StateLoaded
// is completed when all of the buffers have been removed
// from the component.
// Here the buffer promises are not resolved due to displaying
// in layer, it needs to wait before the layer returns the
// buffers.
LOG("DoAsyncShutdown: all buffers collected, releasing buffers...");
self->ReleaseBuffers(OMX_DirInput);
self->ReleaseBuffers(OMX_DirOutput);
},
[self] () {
self->mOmxLayer->Shutdown();
});
return p;
},
[self] () {
self->mOmxLayer->Shutdown();

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

@ -196,7 +196,7 @@ MediaEngineTabVideoSource::NotifyPull(MediaStreamGraph*,
MonitorAutoLock mon(mMonitor);
// Note: we're not giving up mImage here
RefPtr<layers::CairoImage> image = mImage;
RefPtr<layers::SourceSurfaceImage> image = mImage;
StreamTime delta = aDesiredTime - aSource->GetEndOfAppendedData(aID);
if (delta > 0) {
// nullptr images are allowed
@ -298,7 +298,7 @@ MediaEngineTabVideoSource::Draw() {
return;
}
RefPtr<layers::CairoImage> image = new layers::CairoImage(size, surface);
RefPtr<layers::SourceSurfaceImage> image = new layers::SourceSurfaceImage(size, surface);
MonitorAutoLock mon(mMonitor);
mImage = image;

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

@ -88,7 +88,7 @@ private:
ScopedFreePtr<unsigned char> mData;
size_t mDataSize;
nsCOMPtr<nsIDOMWindow> mWindow;
RefPtr<layers::CairoImage> mImage;
RefPtr<layers::SourceSurfaceImage> mImage;
nsCOMPtr<nsITimer> mTimer;
Monitor mMonitor;
nsCOMPtr<nsITabSource> mTabSource;

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

@ -957,7 +957,7 @@ PluginInstanceParent::RecvShow(const NPRect& updatedRect,
RefPtr<gfx::SourceSurface> sourceSurface =
gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(nullptr, surface);
RefPtr<CairoImage> image = new CairoImage(surface->GetSize(), sourceSurface);
RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), sourceSurface);
nsAutoTArray<ImageContainer::NonOwningImage,1> imageList;
imageList.AppendElement(

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

@ -139,62 +139,6 @@ CSPService::ShouldLoad(uint32_t aContentType,
return NS_OK;
}
// ----- THIS IS A TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
// ----- PLEASE REMOVE ONCE bug 925004 LANDS. -----
// Cache the app status for this origin.
uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
nsAutoCString sourceOrigin;
if (aRequestPrincipal && aRequestOrigin) {
aRequestOrigin->GetPrePath(sourceOrigin);
if (!mAppStatusCache.Get(sourceOrigin, &status)) {
aRequestPrincipal->GetAppStatus(&status);
mAppStatusCache.Put(sourceOrigin, status);
}
}
if (status == nsIPrincipal::APP_STATUS_CERTIFIED) {
// The CSP for certified apps is :
// "default-src * data: blob:; script-src 'self'; object-src 'none'; style-src 'self' app://theme.gaiamobile.org:*"
// That means we can optimize for this case by:
// - loading same origin scripts and stylesheets, and stylesheets from the
// theme url space.
// - never loading objects.
// - accepting everything else.
switch (aContentType) {
case nsIContentPolicy::TYPE_SCRIPT:
case nsIContentPolicy::TYPE_STYLESHEET:
{
// Whitelist the theme resources.
auto themeOrigin = Preferences::GetCString("b2g.theme.origin");
nsAutoCString contentOrigin;
aContentLocation->GetPrePath(contentOrigin);
if (!(sourceOrigin.Equals(contentOrigin) ||
(themeOrigin && themeOrigin.Equals(contentOrigin)))) {
*aDecision = nsIContentPolicy::REJECT_SERVER;
}
}
break;
case nsIContentPolicy::TYPE_OBJECT:
*aDecision = nsIContentPolicy::REJECT_SERVER;
break;
default:
*aDecision = nsIContentPolicy::ACCEPT;
}
// Only cache and return if we are successful. If not, we want the error
// to be reported, and thus fallback to the slow path.
if (*aDecision == nsIContentPolicy::ACCEPT) {
return NS_OK;
}
}
// ----- END OF TEMPORARY FAST PATH FOR CERTIFIED APPS. -----
// query the principal of the document; if no document is passed, then
// fall back to using the requestPrincipal (e.g. service workers do not
// pass a document).

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

@ -703,6 +703,8 @@ var interfaceNamesInGlobalScope =
"Image",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmap",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmapRenderingContext",
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "ImageCapture", disabled: true},
// IMPORTANT: Do not change this list without review from a DOM peer!

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

@ -11,7 +11,7 @@
*/
[Constructor,
Exposed=(Window,System)]
Exposed=(Window,Worker,System)]
interface FileReader : EventTarget {
// async read methods
[Throws]

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

@ -23,6 +23,18 @@ interface ImageBitmap {
readonly attribute unsigned long height;
};
// It's crucial that there be a way to explicitly dispose of ImageBitmaps
// since they refer to potentially large graphics resources. Some uses
// of this API proposal will result in repeated allocations of ImageBitmaps,
// and garbage collection will not reliably reclaim them quickly enough.
// Here we reuse close(), which also exists on another Transferable type,
// MessagePort. Potentially, all Transferable types should inherit from a
// new interface type "Closeable".
partial interface ImageBitmap {
// Dispose of all graphical resources associated with this ImageBitmap.
void close();
};
[NoInterfaceObject, Exposed=(Window,Worker)]
interface ImageBitmapFactories {
[Throws]

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

@ -0,0 +1,36 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* The origin of this IDL file is
* https://wiki.whatwg.org/wiki/OffscreenCanvas
*
* © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
* Opera Software ASA. You are granted a license to use, reproduce
* and create derivative works of this document.
*/
// The new ImageBitmapRenderingContext is a canvas rendering context
// which only provides the functionality to replace the canvas's
// contents with the given ImageBitmap. Its context id (the first argument
// to getContext) is "bitmaprenderer".
[Exposed=(Window,Worker)]
interface ImageBitmapRenderingContext {
// Displays the given ImageBitmap in the canvas associated with this
// rendering context. Ownership of the ImageBitmap is transferred to
// the canvas. The caller may not use its reference to the ImageBitmap
// after making this call. (This semantic is crucial to enable prompt
// reclamation of expensive graphics resources, rather than relying on
// garbage collection to do so.)
//
// The ImageBitmap conceptually replaces the canvas's bitmap, but
// it does not change the canvas's intrinsic width or height.
//
// The ImageBitmap, when displayed, is clipped to the rectangle
// defined by the canvas's instrinsic width and height. Pixels that
// would be covered by the canvas's bitmap which are not covered by
// the supplied ImageBitmap are rendered transparent black. Any CSS
// styles affecting the display of the canvas are applied as usual.
void transferImageBitmap(ImageBitmap bitmap);
};

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

@ -5,14 +5,10 @@
*
* For more information on this interface, please see
* https://wiki.whatwg.org/wiki/OffscreenCanvas
*
* Current implementation focus on transfer canvas from main thread to worker.
* So there are some spec doesn't implement, such as [Constructor], toBlob() and
* transferToImageBitmap in OffscreenCanvas. Bug 1172796 will implement
* remaining spec.
*/
[Exposed=(Window,Worker),
[Constructor(unsigned long width, unsigned long height),
Exposed=(Window,Worker),
Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
interface OffscreenCanvas : EventTarget {
[Pure, SetterThrows]
@ -23,6 +19,11 @@ interface OffscreenCanvas : EventTarget {
[Throws]
nsISupports? getContext(DOMString contextId,
optional any contextOptions = null);
ImageBitmap transferToImageBitmap();
[Throws]
Promise<Blob> toBlob(optional DOMString type = "",
optional any encoderOptions);
};
// OffscreenCanvas implements Transferable;

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

@ -265,6 +265,7 @@ WEBIDL_FILES = [
'IDBTransaction.webidl',
'IDBVersionChangeEvent.webidl',
'ImageBitmap.webidl',
'ImageBitmapRenderingContext.webidl',
'ImageCapture.webidl',
'ImageData.webidl',
'ImageDocument.webidl',

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

@ -629,6 +629,7 @@ private:
// If one load info cancels or hits an error, it can race with the start
// callback coming from another load info.
if (mCanceledMainThread || !mCacheCreator) {
aRequest->Cancel(NS_ERROR_FAILURE);
return NS_ERROR_FAILURE;
}

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

@ -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!
@ -144,6 +146,8 @@ var interfaceNamesInGlobalScope =
"IDBVersionChangeEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmap",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmapRenderingContext",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageData",
// IMPORTANT: Do not change this list without review from a DOM peer!

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

@ -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!
@ -136,6 +138,8 @@ var interfaceNamesInGlobalScope =
"IDBVersionChangeEvent",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmap",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageBitmapRenderingContext",
// IMPORTANT: Do not change this list without review from a DOM peer!
"ImageData",
// IMPORTANT: Do not change this list without review from a DOM peer!

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

@ -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;
}

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

@ -317,8 +317,13 @@ class DrawTargetCaptureImpl;
* This is the base class for source surfaces. These objects are surfaces
* which may be used as a source in a SurfacePattern or a DrawSurface call.
* They cannot be drawn to directly.
*
* Although SourceSurface has thread-safe refcount, some SourceSurface cannot
* be used on random threads at the same time. Only DataSourceSurface can be
* used on random threads now. This will be fixed in the future. Eventually
* all SourceSurface should be thread-safe.
*/
class SourceSurface : public RefCounted<SourceSurface>
class SourceSurface : public external::AtomicRefCounted<SourceSurface>
{
public:
MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(SourceSurface)
@ -1227,6 +1232,11 @@ public:
CreateWrappingDataSourceSurface(uint8_t *aData, int32_t aStride,
const IntSize &aSize, SurfaceFormat aFormat);
static void
CopyDataSourceSurface(DataSourceSurface* aSource,
DataSourceSurface* aDest);
static already_AddRefed<DrawEventRecorder>
CreateEventRecorderForFile(const char *aFilename);

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

@ -919,6 +919,97 @@ Factory::CreateDataSourceSurfaceWithStride(const IntSize &aSize,
return nullptr;
}
static uint16_t
PackRGB565(uint8_t r, uint8_t g, uint8_t b)
{
uint16_t pixel = ((r << 11) & 0xf800) |
((g << 5) & 0x07e0) |
((b ) & 0x001f);
return pixel;
}
void
Factory::CopyDataSourceSurface(DataSourceSurface* aSource,
DataSourceSurface* aDest)
{
// Don't worry too much about speed.
MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16);
const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
const bool needsSwap02 = isSrcBGR != isDestBGR;
const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16;
DataSourceSurface::MappedSurface srcMap;
DataSourceSurface::MappedSurface destMap;
if (!aSource->Map(DataSourceSurface::MapType::READ, &srcMap) ||
!aDest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
return;
}
MOZ_ASSERT(srcMap.mStride >= 0);
MOZ_ASSERT(destMap.mStride >= 0);
const size_t srcBPP = BytesPerPixel(aSource->GetFormat());
const size_t srcRowBytes = aSource->GetSize().width * srcBPP;
const size_t srcRowHole = srcMap.mStride - srcRowBytes;
const size_t destBPP = BytesPerPixel(aDest->GetFormat());
const size_t destRowBytes = aDest->GetSize().width * destBPP;
const size_t destRowHole = destMap.mStride - destRowBytes;
uint8_t* srcRow = srcMap.mData;
uint8_t* destRow = destMap.mData;
const size_t rows = aSource->GetSize().height;
for (size_t i = 0; i < rows; i++) {
const uint8_t* srcRowEnd = srcRow + srcRowBytes;
while (srcRow != srcRowEnd) {
uint8_t d0 = needsSwap02 ? srcRow[2] : srcRow[0];
uint8_t d1 = srcRow[1];
uint8_t d2 = needsSwap02 ? srcRow[0] : srcRow[2];
uint8_t d3 = needsAlphaMask ? 0xff : srcRow[3];
if (needsConvertTo16Bits) {
*(uint16_t*)destRow = PackRGB565(d0, d1, d2);
} else {
destRow[0] = d0;
destRow[1] = d1;
destRow[2] = d2;
destRow[3] = d3;
}
srcRow += srcBPP;
destRow += destBPP;
}
srcRow += srcRowHole;
destRow += destRowHole;
}
aSource->Unmap();
aDest->Unmap();
}
already_AddRefed<DrawEventRecorder>
Factory::CreateEventRecorderForFile(const char *aFilename)
{

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

@ -250,96 +250,6 @@ SwapRAndBComponents(DataSourceSurface* surf)
surf->Unmap();
}
static uint16_t
PackRGB565(uint8_t r, uint8_t g, uint8_t b)
{
uint16_t pixel = ((r << 11) & 0xf800) |
((g << 5) & 0x07e0) |
((b ) & 0x001f);
return pixel;
}
static void
CopyDataSourceSurface(DataSourceSurface* aSource,
DataSourceSurface* aDest)
{
// Don't worry too much about speed.
MOZ_ASSERT(aSource->GetSize() == aDest->GetSize());
MOZ_ASSERT(aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aSource->GetFormat() == SurfaceFormat::R8G8B8X8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8X8);
MOZ_ASSERT(aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aDest->GetFormat() == SurfaceFormat::R8G8B8X8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8X8 ||
aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16);
const bool isSrcBGR = aSource->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8X8;
const bool isDestBGR = aDest->GetFormat() == SurfaceFormat::B8G8R8A8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8X8;
const bool needsSwap02 = isSrcBGR != isDestBGR;
const bool srcHasAlpha = aSource->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aSource->GetFormat() == SurfaceFormat::B8G8R8A8;
const bool destHasAlpha = aDest->GetFormat() == SurfaceFormat::R8G8B8A8 ||
aDest->GetFormat() == SurfaceFormat::B8G8R8A8;
const bool needsAlphaMask = !srcHasAlpha && destHasAlpha;
const bool needsConvertTo16Bits = aDest->GetFormat() == SurfaceFormat::R5G6B5_UINT16;
DataSourceSurface::MappedSurface srcMap;
DataSourceSurface::MappedSurface destMap;
if (!aSource->Map(DataSourceSurface::MapType::READ, &srcMap) ||
!aDest->Map(DataSourceSurface::MapType::WRITE, &destMap)) {
MOZ_ASSERT(false, "CopyDataSourceSurface: Failed to map surface.");
return;
}
MOZ_ASSERT(srcMap.mStride >= 0);
MOZ_ASSERT(destMap.mStride >= 0);
const size_t srcBPP = BytesPerPixel(aSource->GetFormat());
const size_t srcRowBytes = aSource->GetSize().width * srcBPP;
const size_t srcRowHole = srcMap.mStride - srcRowBytes;
const size_t destBPP = BytesPerPixel(aDest->GetFormat());
const size_t destRowBytes = aDest->GetSize().width * destBPP;
const size_t destRowHole = destMap.mStride - destRowBytes;
uint8_t* srcRow = srcMap.mData;
uint8_t* destRow = destMap.mData;
const size_t rows = aSource->GetSize().height;
for (size_t i = 0; i < rows; i++) {
const uint8_t* srcRowEnd = srcRow + srcRowBytes;
while (srcRow != srcRowEnd) {
uint8_t d0 = needsSwap02 ? srcRow[2] : srcRow[0];
uint8_t d1 = srcRow[1];
uint8_t d2 = needsSwap02 ? srcRow[0] : srcRow[2];
uint8_t d3 = needsAlphaMask ? 0xff : srcRow[3];
if (needsConvertTo16Bits) {
*(uint16_t*)destRow = PackRGB565(d0, d1, d2);
} else {
destRow[0] = d0;
destRow[1] = d1;
destRow[2] = d2;
destRow[3] = d3;
}
srcRow += srcBPP;
destRow += destBPP;
}
srcRow += srcRowHole;
destRow += destRowHole;
}
aSource->Unmap();
aDest->Unmap();
}
static int
CalcRowStride(int width, int pixelSize, int alignment)
{
@ -500,7 +410,7 @@ ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest)
if (readSurf != dest) {
MOZ_ASSERT(readFormat == LOCAL_GL_RGBA);
MOZ_ASSERT(readType == LOCAL_GL_UNSIGNED_BYTE);
CopyDataSourceSurface(readSurf, dest);
gfx::Factory::CopyDataSourceSurface(readSurf, dest);
}
// Check if GL is giving back 1.0 alpha for

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

@ -549,18 +549,18 @@ PlanarYCbCrImage::GetAsSourceSurface()
return surface.forget();
}
CairoImage::CairoImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
SourceSurfaceImage::SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface)
: Image(nullptr, ImageFormat::CAIRO_SURFACE),
mSize(aSize),
mSourceSurface(aSourceSurface)
{}
CairoImage::~CairoImage()
SourceSurfaceImage::~SourceSurfaceImage()
{
}
TextureClient*
CairoImage::GetTextureClient(CompositableClient *aClient)
SourceSurfaceImage::GetTextureClient(CompositableClient *aClient)
{
if (!aClient) {
return nullptr;

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

@ -83,6 +83,50 @@ public:
}
};
class nsOwningThreadSourceSurfaceRef;
template <>
class nsAutoRefTraits<nsOwningThreadSourceSurfaceRef> {
public:
typedef mozilla::gfx::SourceSurface* RawRef;
/**
* The XPCOM event that will do the actual release on the creation thread.
*/
class SurfaceReleaser : public nsRunnable {
public:
explicit SurfaceReleaser(RawRef aRef) : mRef(aRef) {}
NS_IMETHOD Run() {
mRef->Release();
return NS_OK;
}
RawRef mRef;
};
static RawRef Void() { return nullptr; }
void Release(RawRef aRawRef)
{
MOZ_ASSERT(mOwningThread);
bool current;
mOwningThread->IsOnCurrentThread(&current);
if (current) {
aRawRef->Release();
return;
}
nsCOMPtr<nsIRunnable> runnable = new SurfaceReleaser(aRawRef);
mOwningThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
}
void AddRef(RawRef aRawRef)
{
MOZ_ASSERT(!mOwningThread);
NS_GetCurrentThread(getter_AddRefs(mOwningThread));
aRawRef->AddRef();
}
private:
nsCOMPtr<nsIThread> mOwningThread;
};
#endif
#ifdef XP_WIN
@ -763,11 +807,11 @@ protected:
};
/**
* Currently, the data in a CairoImage surface is treated as being in the
* Currently, the data in a SourceSurfaceImage surface is treated as being in the
* device output color space. This class is very simple as all backends
* have to know about how to deal with drawing a cairo image.
*/
class CairoImage final : public Image {
class SourceSurfaceImage final : public Image {
public:
virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override
{
@ -779,12 +823,12 @@ public:
virtual gfx::IntSize GetSize() override { return mSize; }
CairoImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface);
~CairoImage();
SourceSurfaceImage(const gfx::IntSize& aSize, gfx::SourceSurface* aSourceSurface);
~SourceSurfaceImage();
private:
gfx::IntSize mSize;
nsCountedRef<nsMainThreadSourceSurfaceRef> mSourceSurface;
nsCountedRef<nsOwningThreadSourceSurfaceRef> mSourceSurface;
nsDataHashtable<nsUint32HashKey, RefPtr<TextureClient> > mTextureClients;
};

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

@ -41,7 +41,7 @@ enum class ImageFormat {
SHARED_RGB,
/**
* The CAIRO_SURFACE format creates a CairoImage. All backends should
* The CAIRO_SURFACE format creates a SourceSurfaceImage. All backends should
* support this format, because video rendering sometimes requires it.
*
* This format is useful even though a PaintedLayer could be used.

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

@ -614,6 +614,7 @@ bool
PanGestureBlockState::SetContentResponse(bool aPreventDefault)
{
if (aPreventDefault) {
TBS_LOG("%p setting interrupted flag\n", this);
mInterrupted = true;
}
bool stateChanged = CancelableBlockState::SetContentResponse(aPreventDefault);

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

@ -207,7 +207,8 @@ InputQueue::ReceiveMouseInput(const RefPtr<AsyncPanZoomController>& aTarget,
*aOutInputBlockId = block->GetBlockId();
}
INPQ_LOG("started new drag block %p for target %p\n", block, aTarget.get());
INPQ_LOG("started new drag block %p id %" PRIu64 " for target %p\n",
block, block->GetBlockId(), aTarget.get());
SweepDepletedBlocks();
mInputBlockQueue.AppendElement(block);
@ -262,7 +263,8 @@ InputQueue::ReceiveScrollWheelInput(const RefPtr<AsyncPanZoomController>& aTarge
if (!block) {
block = new WheelBlockState(aTarget, aTargetConfirmed, aEvent);
INPQ_LOG("started new scroll wheel block %p for target %p\n", block, aTarget.get());
INPQ_LOG("started new scroll wheel block %p id %" PRIu64 " for target %p\n",
block, block->GetBlockId(), aTarget.get());
SweepDepletedBlocks();
mInputBlockQueue.AppendElement(block);
@ -321,19 +323,25 @@ InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget
block = mInputBlockQueue.LastElement()->AsPanGestureBlock();
}
PanGestureInput event = aEvent;
nsEventStatus result = nsEventStatus_eConsumeDoDefault;
if (!block || block->WasInterrupted()) {
if (aEvent.mType != PanGestureInput::PANGESTURE_START) {
// Only PANGESTURE_START events are allowed to start a new pan gesture block.
return nsEventStatus_eConsumeDoDefault;
if (event.mType != PanGestureInput::PANGESTURE_START) {
// Only PANGESTURE_START events are allowed to start a new pan gesture
// block, but we really want to start a new block here, so we magically
// turn this input into a PANGESTURE_START.
INPQ_LOG("transmogrifying pan input %d to PANGESTURE_START for new block\n",
event.mType);
event.mType = PanGestureInput::PANGESTURE_START;
}
block = new PanGestureBlockState(aTarget, aTargetConfirmed, aEvent);
INPQ_LOG("started new pan gesture block %p for target %p\n", block, aTarget.get());
block = new PanGestureBlockState(aTarget, aTargetConfirmed, event);
INPQ_LOG("started new pan gesture block %p id %" PRIu64 " for target %p\n",
block, block->GetBlockId(), aTarget.get());
if (aTargetConfirmed &&
aEvent.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
!CanScrollTargetHorizontally(aEvent, block)) {
event.mRequiresContentResponseIfCannotScrollHorizontallyInStartDirection &&
!CanScrollTargetHorizontally(event, block)) {
// This event may trigger a swipe gesture, depending on what our caller
// wants to do it. We need to suspend handling of this block until we get
// a content response which will tell us whether to proceed or abort the
@ -363,8 +371,8 @@ InputQueue::ReceivePanGestureInput(const RefPtr<AsyncPanZoomController>& aTarget
// null) should take priority. This is equivalent to just always using the
// target (confirmed or not) from the block, which is what
// MaybeHandleCurrentBlock() does.
if (!MaybeHandleCurrentBlock(block, aEvent)) {
block->AddEvent(aEvent.AsPanGestureInput());
if (!MaybeHandleCurrentBlock(block, event)) {
block->AddEvent(event.AsPanGestureInput());
}
return result;

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

@ -8,6 +8,7 @@
#include "ContentHelper.h"
#include "gfxPlatform.h" // For gfxPlatform::UseTiling
#include "gfxPrefs.h"
#include "LayersLogging.h" // For Stringify
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/IntegerPrintfMacros.h"

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

@ -605,6 +605,12 @@ public:
virtual void FlushContentDrawing() {}
// If a device reset has occurred, update the necessary platform backend
// bits.
virtual bool UpdateForDeviceReset() {
return false;
}
/**
* Helper method, creates a draw target for a specific Azure backend.
* Used by CreateOffscreenDrawTarget.

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

@ -428,6 +428,7 @@ private:
DECL_GFX_PREF(Live, "webgl.enable-draft-extensions", WebGLDraftExtensionsEnabled, bool, false);
DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions", WebGLPrivilegedExtensionsEnabled, bool, false);
DECL_GFX_PREF(Once, "webgl.enable-prototype-webgl2", WebGL2Enabled, bool, false);
DECL_GFX_PREF(Live, "webgl.force-enabled", WebGLForceEnabled, bool, false);
DECL_GFX_PREF(Once, "webgl.force-layers-readback", WebGLForceLayersReadback, bool, false);
DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);

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

@ -1192,6 +1192,30 @@ gfxWindowsPlatform::DidRenderingDeviceReset(DeviceResetReason* aResetReason)
return false;
}
BOOL CALLBACK
InvalidateWindowForDeviceReset(HWND aWnd, LPARAM aMsg)
{
RedrawWindow(aWnd, nullptr, nullptr,
RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_FRAME);
return TRUE;
}
bool
gfxWindowsPlatform::UpdateForDeviceReset()
{
if (!DidRenderingDeviceReset()) {
return false;
}
// Trigger an ::OnPaint for each window.
::EnumThreadWindows(GetCurrentThreadId(),
InvalidateWindowForDeviceReset,
0);
gfxCriticalNote << "Detected rendering device reset on refresh";
return true;
}
void
gfxWindowsPlatform::GetPlatformCMSOutputProfile(void* &mem, size_t &mem_size)
{

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

@ -213,7 +213,8 @@ public:
*/
virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) override;
virtual bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) override;
bool DidRenderingDeviceReset(DeviceResetReason* aResetReason = nullptr) override;
bool UpdateForDeviceReset() override;
mozilla::gfx::BackendType GetContentBackendFor(mozilla::layers::LayersBackend aLayers) override;

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

@ -632,7 +632,7 @@ RasterImage::GetCurrentImage(ImageContainer* aContainer, uint32_t aFlags)
GetWidth(&size.width);
GetHeight(&size.height);
RefPtr<layers::Image> image = new layers::CairoImage(size, surface);
RefPtr<layers::Image> image = new layers::SourceSurfaceImage(size, surface);
return MakePair(drawResult, Move(image));
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше