/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
if (!mRefreshDriver) {
PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, "Abort: no refresh driver"_ns); return;
}
if (!mRefreshDriver->IsThrottled()) {
PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, "Abort: not throttled"_ns); return;
}
TimeStamp now = TimeStamp::Now();
TimeStamp next =
mLastCaptureTime.IsNull()
? now
: mLastCaptureTime + TimeDuration::FromMilliseconds(
nsRefreshDriver::DefaultInterval()); if (mLastCaptureTime.IsNull() || next <= now) {
AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, "CaptureFrame direct while throttled"_ns);
CaptureFrame(now); return;
}
nsCString str; if (profiler_thread_is_being_profiled_for_markers()) {
str.AppendPrintf("Delaying CaptureFrame by %.2fms",
(next - now).ToMilliseconds());
}
AUTO_PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, str);
mPendingThrottledCapture = true;
AbstractThread::MainThread()->DelayedDispatch(
NS_NewRunnableFunction(
__func__,
[this, self = RefPtr<RequestedFrameRefreshObserver>(this), next] {
mPendingThrottledCapture = false;
AUTO_PROFILER_MARKER_TEXT( "Canvas CaptureStream", MEDIA_RT, {}, "CaptureFrame after delay while throttled"_ns);
CaptureFrame(next);
}), // next >= now, so this is a guard for (next - now) flooring to 0.
std::max<uint32_t>(
1, static_cast<uint32_t>((next - now).ToMilliseconds())));
}
// Mark the context already now, since if the frame capture state is DIRTY // and we catch an early return below (not marking it CLEAN), the next draw // will not trigger a capture state change from the // Watchable<FrameCaptureState>.
mOwningElement->MarkContextCleanForFrameCapture();
mOwningElement->ProcessDestroyedFrameListeners();
if (!mOwningElement->IsFrameCaptureRequested(aTime)) {
PROFILER_MARKER_TEXT("Canvas CaptureStream", MEDIA_RT, {}, "Abort: no capture requested"_ns); return;
}
void HTMLCanvasPrintState::Done() { if (!mPendingNotify && !mIsDone) { // The canvas needs to be invalidated for printing reftests on linux to // work. if (mCanvas) {
mCanvas->InvalidateCanvas();
}
RefPtr<nsRunnableMethod<HTMLCanvasPrintState>> doneEvent =
NewRunnableMethod("dom::HTMLCanvasPrintState::NotifyDone", this,
&HTMLCanvasPrintState::NotifyDone); if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) {
mPendingNotify = true;
}
}
}
// Do not assert on observerService here. This might be triggered by // the cycle collector at a late enough time, that XPCOM services are // no longer available. See bug 1029504. if (observerService) {
observerService->RemoveObserver(this, "memory-pressure");
observerService->RemoveObserver(this, "canvas-device-reset");
}
}
already_AddRefed<nsICanvasRenderingContextInternal>
HTMLCanvasElement::CreateContext(CanvasContextType aContextType) { // Note that the compositor backend will be LAYERS_NONE if there is no widget.
RefPtr<nsICanvasRenderingContextInternal> ret =
CreateContextHelper(aContextType, GetCompositorBackendType()); if (NS_WARN_IF(!ret)) { return nullptr;
}
// Add Observer for webgl canvas. if (aContextType == CanvasContextType::WebGL1 ||
aContextType == CanvasContextType::WebGL2 ||
aContextType == CanvasContextType::Canvas2D) { if (!mContextObserver) {
mContextObserver = new HTMLCanvasElementObserver(this);
}
}
// If we have a mRequestedFrameRefreshObserver that wasn't fully registered, // retry that now. if (mRequestedFrameRefreshObserver.get() &&
!mRequestedFrameRefreshObserver->IsRegisteredAndWatching()) {
mRequestedFrameRefreshObserver->Register();
}
void HTMLCanvasElement::HandlePrintCallback(nsPresContext* aPresContext) { // Only call the print callback here if 1) we're in a print testing mode or // print preview mode, 2) the canvas has a print callback and 3) the callback // hasn't already been called. For real printing the callback is handled in // nsPageSequenceFrame::PrePrintNextSheet. if ((aPresContext->Type() == nsPresContext::eContext_PageLayout ||
aPresContext->Type() == nsPresContext::eContext_PrintPreview) &&
!mPrintState && GetMozPrintCallback()) {
DispatchPrintCallback(nullptr);
}
}
nsresult HTMLCanvasElement::DispatchPrintCallback(nsITimerCallback* aCallback) { // For print reftests the context may not be initialized yet, so get a context // so mCurrentContext is set. if (!mCurrentContext) {
nsresult rv;
nsCOMPtr<nsISupports> context;
rv = GetContext(u"2d"_ns, getter_AddRefs(context));
NS_ENSURE_SUCCESS(rv, rv);
}
mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback);
void HTMLCanvasElement::CallPrintCallback() {
AUTO_PROFILER_MARKER_TEXT("HTMLCanvasElement Printing", LAYOUT_Printing, {}, "HTMLCanvasElement::CallPrintCallback"_ns); if (!mPrintState) { // `mPrintState` might have been destroyed by cancelling the previous // printing (especially the canvas frame destruction) during processing // event loops in the printing. return;
}
RefPtr<PrintCallback> callback = GetMozPrintCallback();
RefPtr<HTMLCanvasPrintState> state = mPrintState;
callback->Call(*state);
}
nsresult HTMLCanvasElement::CopyInnerTo(HTMLCanvasElement* aDest) {
nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
NS_ENSURE_SUCCESS(rv, rv);
Document* destDoc = aDest->OwnerDoc(); if (destDoc->IsStaticDocument()) { // The Firefox print preview code can create a static clone from an // existing static clone, so we may not be the original 'canvas' element.
aDest->mOriginalCanvas = GetOriginalCanvas();
if (GetMozPrintCallback()) {
destDoc->SetHasPrintCallbacks();
}
// We make sure that the canvas is not zero sized since that would cause // the DrawImage call below to return an error, which would cause printing // to fail.
CSSIntSize size = GetWidthHeight(); if (size.height > 0 && size.width > 0) {
nsCOMPtr<nsISupports> cxt;
aDest->GetContext(u"2d"_ns, getter_AddRefs(cxt));
RefPtr<CanvasRenderingContext2D> context2d = static_cast<CanvasRenderingContext2D*>(cxt.get()); if (context2d && !mPrintCallback) {
CanvasImageSource source;
source.SetAsHTMLCanvasElement() = this;
ErrorResult err;
context2d->DrawImage(source, 0.0, 0.0, err);
rv = err.StealNSResult();
}
}
} return rv;
}
auto stream = MakeRefPtr<CanvasCaptureMediaStream>(window, this);
nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
nsresult rv = stream->Init(aFrameRate, principal); if (NS_FAILED(rv)) {
aRv.Throw(rv); return nullptr;
}
RefPtr<MediaStreamTrack> track = new VideoStreamTrack(window, stream->GetSourceStream(), new CanvasCaptureTrackSource(principal, stream));
stream->AddTrackInternal(track);
// Check site-specific permission and display prompt if appropriate. // If no permission, arrange for the frame capture listener to return // all-white, opaque image data. bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
OwnerDoc(), nsContentUtils::GetCurrentJSContext(), aSubjectPrincipal);
// If there are unrecognized custom parse options, we should fall back to // the default values for the encoder without any options at all. if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
rv = ExtractData(aCx, aSubjectPrincipal, type, u""_ns,
getter_AddRefs(stream));
}
NS_ENSURE_SUCCESS(rv, rv);
// build data URL string
aDataURL = u"data:"_ns + type + u";base64,"_ns;
void HTMLCanvasElement::ToBlob(JSContext* aCx, BlobCallback& aCallback, const nsAString& aType,
JS::Handle<JS::Value> aParams,
nsIPrincipal& aSubjectPrincipal,
ErrorResult& aRv) { // mWriteOnly check is redundant, but optimizes for the common case. if (mWriteOnly && !CallerCanRead(aSubjectPrincipal)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); return;
}
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
CSSIntSize elemSize = GetWidthHeight(); if (elemSize.width == 0 || elemSize.height == 0) { // According to spec, blob should return null if either its horizontal // dimension or its vertical dimension is zero. See link below. // https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
OwnerDoc()->Dispatch(NewRunnableMethod<Blob*, constchar*>( "dom::HTMLCanvasElement::ToBlob", &aCallback, static_cast<void (BlobCallback::*)(Blob*, constchar*)>(
&BlobCallback::Call),
nullptr, nullptr)); return;
}
// Check site-specific permission and display prompt if appropriate. // If no permission, return all-white, opaque image data. bool usePlaceholder = !CanvasUtils::IsImageExtractionAllowed(
OwnerDoc(), aCx, aSubjectPrincipal);
CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType, aParams,
usePlaceholder, aRv);
}
bool HTMLCanvasElement::CallerCanRead(nsIPrincipal& aPrincipal) const { if (!mWriteOnly) { returntrue;
}
// If mExpandedReader is set, this canvas was tainted only by // mExpandedReader's resources. So allow reading if the subject // principal subsumes mExpandedReader. if (mExpandedReader && aPrincipal.Subsumes(mExpandedReader)) { returntrue;
}
void HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect) { // Cache the current ImageContainer to avoid contention on the mutex. if (mOffscreenDisplay) {
mImageContainer = mOffscreenDisplay->GetImageContainer();
}
// We don't need to flush anything here; if there's no frame or if // we plan to reframe we don't need to invalidate it anyway.
nsIFrame* frame = GetPrimaryFrame(); if (!frame) return;
// When using layers-free WebRender, we cannot invalidate the layer (because // there isn't one). Instead, we mark the CanvasRenderer dirty and scheduling // an empty transaction which is effectively equivalent.
CanvasRenderer* renderer = nullptr; constauto key = static_cast<uint32_t>(DisplayItemType::TYPE_CANVAS);
RefPtr<WebRenderCanvasData> data =
GetWebRenderUserData<WebRenderCanvasData>(frame, key); if (data) {
renderer = data->GetCanvasRenderer();
}
// This path is taken in two situations: // 1) WebRender is enabled and has not yet processed a display list. // 2) WebRender is disabled and layer invalidation failed. // In both cases, schedule a full paint to properly update canvas.
frame->SchedulePaint(nsIFrame::PAINT_DEFAULT, false);
}
/* * Treat canvas invalidations as animation activity for JS. Frequently * invalidating a canvas will feed into heuristics and cause JIT code to be * kept around longer, for smoother animations.
*/
nsPIDOMWindowInner* win = OwnerDoc()->GetInnerWindow();
if (win) { if (JSObject* obj = win->AsGlobal()->GetGlobalJSObject()) {
js::NotifyAnimationActivity(obj);
}
}
}
void HTMLCanvasElement::InvalidateCanvas() { // We don't need to flush anything here; if there's no frame or if // we plan to reframe we don't need to invalidate it anyway.
nsIFrame* frame = GetPrimaryFrame(); if (!frame) return;
frame->InvalidateFrame();
}
bool HTMLCanvasElement::GetIsOpaque() { if (mCurrentContext) { return mCurrentContext->GetIsOpaque();
}
void HTMLCanvasElement::OnMemoryPressure() { // FIXME(aosmond): We need to implement memory pressure handling for // OffscreenCanvas when it is on worker threads. See bug 1746260.
if (mCurrentContext) {
mCurrentContext->OnMemoryPressure();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.