Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/HTMLCanvasElement.h"
8 :
9 : #include "ImageEncoder.h"
10 : #include "jsapi.h"
11 : #include "jsfriendapi.h"
12 : #include "Layers.h"
13 : #include "MediaSegment.h"
14 : #include "mozilla/Assertions.h"
15 : #include "mozilla/Base64.h"
16 : #include "mozilla/CheckedInt.h"
17 : #include "mozilla/dom/CanvasCaptureMediaStream.h"
18 : #include "mozilla/dom/CanvasRenderingContext2D.h"
19 : #include "mozilla/dom/File.h"
20 : #include "mozilla/dom/HTMLCanvasElementBinding.h"
21 : #include "mozilla/dom/MediaStreamTrack.h"
22 : #include "mozilla/dom/MouseEvent.h"
23 : #include "mozilla/dom/OffscreenCanvas.h"
24 : #include "mozilla/EventDispatcher.h"
25 : #include "mozilla/gfx/Rect.h"
26 : #include "mozilla/layers/AsyncCanvasRenderer.h"
27 : #include "mozilla/MouseEvents.h"
28 : #include "mozilla/Preferences.h"
29 : #include "mozilla/Telemetry.h"
30 : #include "nsAttrValueInlines.h"
31 : #include "nsContentUtils.h"
32 : #include "nsDisplayList.h"
33 : #include "nsDOMJSUtils.h"
34 : #include "nsIScriptSecurityManager.h"
35 : #include "nsITimer.h"
36 : #include "nsIWritablePropertyBag2.h"
37 : #include "nsIXPConnect.h"
38 : #include "nsJSUtils.h"
39 : #include "nsLayoutUtils.h"
40 : #include "nsMathUtils.h"
41 : #include "nsNetUtil.h"
42 : #include "nsRefreshDriver.h"
43 : #include "nsStreamUtils.h"
44 : #include "ActiveLayerTracker.h"
45 : #include "VRManagerChild.h"
46 : #include "WebGL1Context.h"
47 : #include "WebGL2Context.h"
48 :
49 : using namespace mozilla::layers;
50 : using namespace mozilla::gfx;
51 :
52 0 : NS_IMPL_NS_NEW_HTML_ELEMENT(Canvas)
53 :
54 : namespace mozilla {
55 : namespace dom {
56 :
57 : class RequestedFrameRefreshObserver : public nsARefreshObserver
58 : {
59 0 : NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RequestedFrameRefreshObserver, override)
60 :
61 : public:
62 0 : RequestedFrameRefreshObserver(HTMLCanvasElement* const aOwningElement,
63 : nsRefreshDriver* aRefreshDriver)
64 0 : : mRegistered(false),
65 : mOwningElement(aOwningElement),
66 0 : mRefreshDriver(aRefreshDriver)
67 : {
68 0 : MOZ_ASSERT(mOwningElement);
69 0 : }
70 :
71 : static already_AddRefed<DataSourceSurface>
72 0 : CopySurface(const RefPtr<SourceSurface>& aSurface)
73 : {
74 0 : RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
75 0 : if (!data) {
76 0 : return nullptr;
77 : }
78 :
79 0 : DataSourceSurface::ScopedMap read(data, DataSourceSurface::READ);
80 0 : if (!read.IsMapped()) {
81 0 : return nullptr;
82 : }
83 :
84 : RefPtr<DataSourceSurface> copy =
85 0 : Factory::CreateDataSourceSurfaceWithStride(data->GetSize(),
86 0 : data->GetFormat(),
87 0 : read.GetStride());
88 0 : if (!copy) {
89 0 : return nullptr;
90 : }
91 :
92 0 : DataSourceSurface::ScopedMap write(copy, DataSourceSurface::WRITE);
93 0 : if (!write.IsMapped()) {
94 0 : return nullptr;
95 : }
96 :
97 0 : MOZ_ASSERT(read.GetStride() == write.GetStride());
98 0 : MOZ_ASSERT(data->GetSize() == copy->GetSize());
99 0 : MOZ_ASSERT(data->GetFormat() == copy->GetFormat());
100 :
101 0 : memcpy(write.GetData(), read.GetData(),
102 0 : write.GetStride() * copy->GetSize().height);
103 :
104 0 : return copy.forget();
105 : }
106 :
107 0 : void WillRefresh(TimeStamp aTime) override
108 : {
109 0 : MOZ_ASSERT(NS_IsMainThread());
110 :
111 0 : AUTO_PROFILER_LABEL("RequestedFrameRefreshObserver::WillRefresh", OTHER);
112 :
113 0 : if (!mOwningElement) {
114 0 : return;
115 : }
116 :
117 0 : if (mOwningElement->IsWriteOnly()) {
118 0 : return;
119 : }
120 :
121 0 : if (mOwningElement->IsContextCleanForFrameCapture()) {
122 0 : return;
123 : }
124 :
125 0 : mOwningElement->ProcessDestroyedFrameListeners();
126 :
127 0 : if (!mOwningElement->IsFrameCaptureRequested()) {
128 0 : return;
129 : }
130 :
131 0 : RefPtr<SourceSurface> snapshot;
132 : {
133 0 : AUTO_PROFILER_LABEL(
134 : "RequestedFrameRefreshObserver::WillRefresh:GetSnapshot", OTHER);
135 0 : snapshot = mOwningElement->GetSurfaceSnapshot(nullptr);
136 0 : if (!snapshot) {
137 0 : return;
138 : }
139 : }
140 :
141 0 : RefPtr<DataSourceSurface> copy;
142 : {
143 0 : AUTO_PROFILER_LABEL(
144 : "RequestedFrameRefreshObserver::WillRefresh:CopySurface", OTHER);
145 0 : copy = CopySurface(snapshot);
146 0 : if (!copy) {
147 0 : return;
148 : }
149 : }
150 :
151 : {
152 0 : AUTO_PROFILER_LABEL(
153 : "RequestedFrameRefreshObserver::WillRefresh:SetFrame", OTHER);
154 0 : mOwningElement->SetFrameCapture(copy.forget(), aTime);
155 0 : mOwningElement->MarkContextCleanForFrameCapture();
156 : }
157 : }
158 :
159 0 : void DetachFromRefreshDriver()
160 : {
161 0 : MOZ_ASSERT(mOwningElement);
162 0 : MOZ_ASSERT(mRefreshDriver);
163 :
164 0 : Unregister();
165 0 : mRefreshDriver = nullptr;
166 0 : }
167 :
168 0 : void Register()
169 : {
170 0 : if (mRegistered) {
171 0 : return;
172 : }
173 :
174 0 : MOZ_ASSERT(mRefreshDriver);
175 0 : if (mRefreshDriver) {
176 0 : mRefreshDriver->AddRefreshObserver(this, FlushType::Display);
177 0 : mRegistered = true;
178 : }
179 : }
180 :
181 0 : void Unregister()
182 : {
183 0 : if (!mRegistered) {
184 0 : return;
185 : }
186 :
187 0 : MOZ_ASSERT(mRefreshDriver);
188 0 : if (mRefreshDriver) {
189 0 : mRefreshDriver->RemoveRefreshObserver(this, FlushType::Display);
190 0 : mRegistered = false;
191 : }
192 : }
193 :
194 : private:
195 0 : virtual ~RequestedFrameRefreshObserver()
196 0 : {
197 0 : MOZ_ASSERT(!mRefreshDriver);
198 0 : MOZ_ASSERT(!mRegistered);
199 0 : }
200 :
201 : bool mRegistered;
202 : HTMLCanvasElement* const mOwningElement;
203 : RefPtr<nsRefreshDriver> mRefreshDriver;
204 : };
205 :
206 : // ---------------------------------------------------------------------------
207 :
208 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLCanvasPrintState, mCanvas,
209 : mContext, mCallback)
210 :
211 0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(HTMLCanvasPrintState, AddRef)
212 0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(HTMLCanvasPrintState, Release)
213 :
214 0 : HTMLCanvasPrintState::HTMLCanvasPrintState(HTMLCanvasElement* aCanvas,
215 : nsICanvasRenderingContextInternal* aContext,
216 0 : nsITimerCallback* aCallback)
217 : : mIsDone(false), mPendingNotify(false), mCanvas(aCanvas),
218 0 : mContext(aContext), mCallback(aCallback)
219 : {
220 0 : }
221 :
222 0 : HTMLCanvasPrintState::~HTMLCanvasPrintState()
223 : {
224 0 : }
225 :
226 : /* virtual */ JSObject*
227 0 : HTMLCanvasPrintState::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
228 : {
229 0 : return MozCanvasPrintStateBinding::Wrap(aCx, this, aGivenProto);
230 : }
231 :
232 : nsISupports*
233 0 : HTMLCanvasPrintState::Context() const
234 : {
235 0 : return mContext;
236 : }
237 :
238 : void
239 0 : HTMLCanvasPrintState::Done()
240 : {
241 0 : if (!mPendingNotify && !mIsDone) {
242 : // The canvas needs to be invalidated for printing reftests on linux to
243 : // work.
244 0 : if (mCanvas) {
245 0 : mCanvas->InvalidateCanvas();
246 : }
247 : RefPtr<nsRunnableMethod<HTMLCanvasPrintState>> doneEvent =
248 0 : NewRunnableMethod("dom::HTMLCanvasPrintState::NotifyDone",
249 : this,
250 0 : &HTMLCanvasPrintState::NotifyDone);
251 0 : if (NS_SUCCEEDED(NS_DispatchToCurrentThread(doneEvent))) {
252 0 : mPendingNotify = true;
253 : }
254 : }
255 0 : }
256 :
257 : void
258 0 : HTMLCanvasPrintState::NotifyDone()
259 : {
260 0 : mIsDone = true;
261 0 : mPendingNotify = false;
262 0 : if (mCallback) {
263 0 : mCallback->Notify(nullptr);
264 : }
265 0 : }
266 :
267 : // ---------------------------------------------------------------------------
268 :
269 0 : HTMLCanvasElementObserver::HTMLCanvasElementObserver(HTMLCanvasElement* aElement)
270 0 : : mElement(aElement)
271 : {
272 0 : RegisterVisibilityChangeEvent();
273 0 : RegisterMemoryPressureEvent();
274 0 : }
275 :
276 0 : HTMLCanvasElementObserver::~HTMLCanvasElementObserver()
277 : {
278 0 : Destroy();
279 0 : }
280 :
281 : void
282 0 : HTMLCanvasElementObserver::Destroy()
283 : {
284 0 : UnregisterMemoryPressureEvent();
285 0 : UnregisterVisibilityChangeEvent();
286 0 : mElement = nullptr;
287 0 : }
288 :
289 : void
290 0 : HTMLCanvasElementObserver::RegisterVisibilityChangeEvent()
291 : {
292 0 : if (!mElement) {
293 0 : return;
294 : }
295 :
296 0 : nsIDocument* document = mElement->OwnerDoc();
297 0 : document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
298 0 : this, true, false);
299 : }
300 :
301 : void
302 0 : HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent()
303 : {
304 0 : if (!mElement) {
305 0 : return;
306 : }
307 :
308 0 : nsIDocument* document = mElement->OwnerDoc();
309 0 : document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
310 0 : this, true);
311 : }
312 :
313 : void
314 0 : HTMLCanvasElementObserver::RegisterMemoryPressureEvent()
315 : {
316 0 : if (!mElement) {
317 0 : return;
318 : }
319 :
320 : nsCOMPtr<nsIObserverService> observerService =
321 0 : mozilla::services::GetObserverService();
322 :
323 0 : MOZ_ASSERT(observerService);
324 :
325 0 : if (observerService)
326 0 : observerService->AddObserver(this, "memory-pressure", false);
327 : }
328 :
329 : void
330 0 : HTMLCanvasElementObserver::UnregisterMemoryPressureEvent()
331 : {
332 0 : if (!mElement) {
333 0 : return;
334 : }
335 :
336 : nsCOMPtr<nsIObserverService> observerService =
337 0 : mozilla::services::GetObserverService();
338 :
339 : // Do not assert on observerService here. This might be triggered by
340 : // the cycle collector at a late enough time, that XPCOM services are
341 : // no longer available. See bug 1029504.
342 0 : if (observerService)
343 0 : observerService->RemoveObserver(this, "memory-pressure");
344 : }
345 :
346 : NS_IMETHODIMP
347 0 : HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*)
348 : {
349 0 : if (!mElement || strcmp(aTopic, "memory-pressure")) {
350 0 : return NS_OK;
351 : }
352 :
353 0 : mElement->OnMemoryPressure();
354 :
355 0 : return NS_OK;
356 : }
357 :
358 : NS_IMETHODIMP
359 0 : HTMLCanvasElementObserver::HandleEvent(nsIDOMEvent* aEvent)
360 : {
361 0 : nsAutoString type;
362 0 : aEvent->GetType(type);
363 0 : if (!mElement || !type.EqualsLiteral("visibilitychange")) {
364 0 : return NS_OK;
365 : }
366 :
367 0 : mElement->OnVisibilityChange();
368 :
369 0 : return NS_OK;
370 : }
371 :
372 0 : NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
373 :
374 : // ---------------------------------------------------------------------------
375 :
376 0 : HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
377 : : nsGenericHTMLElement(aNodeInfo),
378 : mResetLayer(true) ,
379 : mVRPresentationActive(false),
380 0 : mWriteOnly(false)
381 0 : {}
382 :
383 0 : HTMLCanvasElement::~HTMLCanvasElement()
384 : {
385 0 : if (mContextObserver) {
386 0 : mContextObserver->Destroy();
387 0 : mContextObserver = nullptr;
388 : }
389 :
390 0 : ResetPrintCallback();
391 0 : if (mRequestedFrameRefreshObserver) {
392 0 : mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
393 : }
394 :
395 0 : if (mAsyncCanvasRenderer) {
396 0 : mAsyncCanvasRenderer->mHTMLCanvasElement = nullptr;
397 : }
398 0 : }
399 :
400 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
401 : mCurrentContext, mPrintCallback,
402 : mPrintState, mOriginalCanvas,
403 : mOffscreenCanvas)
404 :
405 0 : NS_IMPL_ADDREF_INHERITED(HTMLCanvasElement, Element)
406 0 : NS_IMPL_RELEASE_INHERITED(HTMLCanvasElement, Element)
407 :
408 0 : NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement)
409 0 : NS_INTERFACE_TABLE_INHERITED(HTMLCanvasElement, nsIDOMHTMLCanvasElement)
410 0 : NS_INTERFACE_TABLE_TAIL_INHERITING(nsGenericHTMLElement)
411 :
412 0 : NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
413 :
414 : /* virtual */ JSObject*
415 0 : HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
416 : {
417 0 : return HTMLCanvasElementBinding::Wrap(aCx, this, aGivenProto);
418 : }
419 :
420 : already_AddRefed<nsICanvasRenderingContextInternal>
421 0 : HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
422 : {
423 : // Note that the compositor backend will be LAYERS_NONE if there is no widget.
424 : RefPtr<nsICanvasRenderingContextInternal> ret =
425 0 : CreateContextHelper(aContextType, GetCompositorBackendType());
426 :
427 : // Add Observer for webgl canvas.
428 0 : if (aContextType == CanvasContextType::WebGL1 ||
429 : aContextType == CanvasContextType::WebGL2) {
430 0 : if (!mContextObserver) {
431 0 : mContextObserver = new HTMLCanvasElementObserver(this);
432 : }
433 : }
434 :
435 0 : ret->SetCanvasElement(this);
436 0 : return ret.forget();
437 : }
438 :
439 : nsIntSize
440 0 : HTMLCanvasElement::GetWidthHeight()
441 : {
442 0 : nsIntSize size(DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);
443 : const nsAttrValue* value;
444 :
445 0 : if ((value = GetParsedAttr(nsGkAtoms::width)) &&
446 0 : value->Type() == nsAttrValue::eInteger)
447 : {
448 0 : size.width = value->GetIntegerValue();
449 : }
450 :
451 0 : if ((value = GetParsedAttr(nsGkAtoms::height)) &&
452 0 : value->Type() == nsAttrValue::eInteger)
453 : {
454 0 : size.height = value->GetIntegerValue();
455 : }
456 :
457 0 : MOZ_ASSERT(size.width >= 0 && size.height >= 0,
458 : "we should've required <canvas> width/height attrs to be "
459 : "unsigned (non-negative) values");
460 :
461 0 : return size;
462 : }
463 :
464 0 : NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Width, width, DEFAULT_CANVAS_WIDTH)
465 0 : NS_IMPL_UINT_ATTR_DEFAULT_VALUE(HTMLCanvasElement, Height, height, DEFAULT_CANVAS_HEIGHT)
466 0 : NS_IMPL_BOOL_ATTR(HTMLCanvasElement, MozOpaque, moz_opaque)
467 :
468 : nsresult
469 0 : HTMLCanvasElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
470 : const nsAttrValue* aValue,
471 : const nsAttrValue* aOldValue, bool aNotify)
472 : {
473 0 : AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
474 :
475 0 : return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
476 0 : aOldValue, aNotify);
477 : }
478 :
479 : nsresult
480 0 : HTMLCanvasElement::OnAttrSetButNotChanged(int32_t aNamespaceID, nsIAtom* aName,
481 : const nsAttrValueOrString& aValue,
482 : bool aNotify)
483 : {
484 0 : AfterMaybeChangeAttr(aNamespaceID, aName, aNotify);
485 :
486 0 : return nsGenericHTMLElement::OnAttrSetButNotChanged(aNamespaceID, aName,
487 0 : aValue, aNotify);
488 : }
489 :
490 : void
491 0 : HTMLCanvasElement::AfterMaybeChangeAttr(int32_t aNamespaceID, nsIAtom* aName,
492 : bool aNotify)
493 : {
494 0 : if (mCurrentContext && aNamespaceID == kNameSpaceID_None &&
495 0 : (aName == nsGkAtoms::width || aName == nsGkAtoms::height ||
496 0 : aName == nsGkAtoms::moz_opaque)) {
497 0 : ErrorResult dummy;
498 0 : UpdateContext(nullptr, JS::NullHandleValue, dummy);
499 : }
500 0 : }
501 :
502 : void
503 0 : HTMLCanvasElement::HandlePrintCallback(nsPresContext::nsPresContextType aType)
504 : {
505 : // Only call the print callback here if 1) we're in a print testing mode or
506 : // print preview mode, 2) the canvas has a print callback and 3) the callback
507 : // hasn't already been called. For real printing the callback is handled in
508 : // nsSimplePageSequenceFrame::PrePrintNextPage.
509 0 : if ((aType == nsPresContext::eContext_PageLayout ||
510 0 : aType == nsPresContext::eContext_PrintPreview) &&
511 0 : !mPrintState && GetMozPrintCallback()) {
512 0 : DispatchPrintCallback(nullptr);
513 : }
514 0 : }
515 :
516 : nsresult
517 0 : HTMLCanvasElement::DispatchPrintCallback(nsITimerCallback* aCallback)
518 : {
519 : // For print reftests the context may not be initialized yet, so get a context
520 : // so mCurrentContext is set.
521 0 : if (!mCurrentContext) {
522 : nsresult rv;
523 0 : nsCOMPtr<nsISupports> context;
524 0 : rv = GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(context));
525 0 : NS_ENSURE_SUCCESS(rv, rv);
526 : }
527 0 : mPrintState = new HTMLCanvasPrintState(this, mCurrentContext, aCallback);
528 :
529 : RefPtr<nsRunnableMethod<HTMLCanvasElement>> renderEvent =
530 0 : NewRunnableMethod("dom::HTMLCanvasElement::CallPrintCallback",
531 : this,
532 0 : &HTMLCanvasElement::CallPrintCallback);
533 0 : return OwnerDoc()->Dispatch("HTMLCanvasElement::CallPrintCallback",
534 : TaskCategory::Other,
535 0 : renderEvent.forget());
536 : }
537 :
538 : void
539 0 : HTMLCanvasElement::CallPrintCallback()
540 : {
541 0 : ErrorResult rv;
542 0 : GetMozPrintCallback()->Call(*mPrintState, rv);
543 0 : }
544 :
545 : void
546 0 : HTMLCanvasElement::ResetPrintCallback()
547 : {
548 0 : if (mPrintState) {
549 0 : mPrintState = nullptr;
550 : }
551 0 : }
552 :
553 : bool
554 0 : HTMLCanvasElement::IsPrintCallbackDone()
555 : {
556 0 : if (mPrintState == nullptr) {
557 0 : return true;
558 : }
559 :
560 0 : return mPrintState->mIsDone;
561 : }
562 :
563 : HTMLCanvasElement*
564 0 : HTMLCanvasElement::GetOriginalCanvas()
565 : {
566 0 : return mOriginalCanvas ? mOriginalCanvas.get() : this;
567 : }
568 :
569 : nsresult
570 0 : HTMLCanvasElement::CopyInnerTo(Element* aDest,
571 : bool aPreallocateChildren)
572 : {
573 0 : nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest, aPreallocateChildren);
574 0 : NS_ENSURE_SUCCESS(rv, rv);
575 0 : if (aDest->OwnerDoc()->IsStaticDocument()) {
576 0 : HTMLCanvasElement* dest = static_cast<HTMLCanvasElement*>(aDest);
577 0 : dest->mOriginalCanvas = this;
578 :
579 0 : nsCOMPtr<nsISupports> cxt;
580 0 : dest->GetContext(NS_LITERAL_STRING("2d"), getter_AddRefs(cxt));
581 : RefPtr<CanvasRenderingContext2D> context2d =
582 0 : static_cast<CanvasRenderingContext2D*>(cxt.get());
583 0 : if (context2d && !mPrintCallback) {
584 0 : CanvasImageSource source;
585 0 : source.SetAsHTMLCanvasElement() = this;
586 0 : ErrorResult err;
587 0 : context2d->DrawImage(source,
588 0 : 0.0, 0.0, err);
589 0 : rv = err.StealNSResult();
590 : }
591 : }
592 0 : return rv;
593 : }
594 :
595 0 : nsresult HTMLCanvasElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
596 : {
597 0 : if (aVisitor.mEvent->mClass == eMouseEventClass) {
598 0 : WidgetMouseEventBase* evt = (WidgetMouseEventBase*)aVisitor.mEvent;
599 0 : if (mCurrentContext) {
600 0 : nsIFrame *frame = GetPrimaryFrame();
601 0 : if (!frame)
602 0 : return NS_OK;
603 0 : nsPoint ptInRoot = nsLayoutUtils::GetEventCoordinatesRelativeTo(evt, frame);
604 0 : nsRect paddingRect = frame->GetContentRectRelativeToSelf();
605 0 : Point hitpoint;
606 0 : hitpoint.x = (ptInRoot.x - paddingRect.x) / AppUnitsPerCSSPixel();
607 0 : hitpoint.y = (ptInRoot.y - paddingRect.y) / AppUnitsPerCSSPixel();
608 :
609 0 : evt->region = mCurrentContext->GetHitRegion(hitpoint);
610 0 : aVisitor.mCanHandle = true;
611 : }
612 : }
613 0 : return nsGenericHTMLElement::GetEventTargetParent(aVisitor);
614 : }
615 :
616 : nsChangeHint
617 0 : HTMLCanvasElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
618 : int32_t aModType) const
619 : {
620 : nsChangeHint retval =
621 0 : nsGenericHTMLElement::GetAttributeChangeHint(aAttribute, aModType);
622 0 : if (aAttribute == nsGkAtoms::width ||
623 0 : aAttribute == nsGkAtoms::height)
624 : {
625 0 : retval |= NS_STYLE_HINT_REFLOW;
626 0 : } else if (aAttribute == nsGkAtoms::moz_opaque)
627 : {
628 0 : retval |= NS_STYLE_HINT_VISUAL;
629 : }
630 0 : return retval;
631 : }
632 :
633 : bool
634 0 : HTMLCanvasElement::ParseAttribute(int32_t aNamespaceID,
635 : nsIAtom* aAttribute,
636 : const nsAString& aValue,
637 : nsAttrValue& aResult)
638 : {
639 0 : if (aNamespaceID == kNameSpaceID_None &&
640 0 : (aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height)) {
641 0 : return aResult.ParseNonNegativeIntValue(aValue);
642 : }
643 :
644 0 : return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
645 0 : aResult);
646 : }
647 :
648 :
649 :
650 : void
651 0 : HTMLCanvasElement::ToDataURL(JSContext* aCx, const nsAString& aType,
652 : JS::Handle<JS::Value> aParams,
653 : nsAString& aDataURL,
654 : ErrorResult& aRv)
655 : {
656 : // do a trust check if this is a write-only canvas
657 0 : if (mWriteOnly &&
658 0 : !nsContentUtils::CallerHasPermission(aCx, NS_LITERAL_STRING("<all_urls>"))) {
659 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
660 0 : return;
661 : }
662 :
663 0 : aRv = ToDataURLImpl(aCx, aType, aParams, aDataURL);
664 : }
665 :
666 : void
667 0 : HTMLCanvasElement::SetMozPrintCallback(PrintCallback* aCallback)
668 : {
669 0 : mPrintCallback = aCallback;
670 0 : }
671 :
672 : PrintCallback*
673 0 : HTMLCanvasElement::GetMozPrintCallback() const
674 : {
675 0 : if (mOriginalCanvas) {
676 0 : return mOriginalCanvas->GetMozPrintCallback();
677 : }
678 0 : return mPrintCallback;
679 : }
680 :
681 : class CanvasCaptureTrackSource : public MediaStreamTrackSource
682 : {
683 : public:
684 : NS_DECL_ISUPPORTS_INHERITED
685 0 : NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CanvasCaptureTrackSource,
686 : MediaStreamTrackSource)
687 :
688 0 : CanvasCaptureTrackSource(nsIPrincipal* aPrincipal,
689 : CanvasCaptureMediaStream* aCaptureStream)
690 0 : : MediaStreamTrackSource(aPrincipal, nsString())
691 0 : , mCaptureStream(aCaptureStream) {}
692 :
693 0 : MediaSourceEnum GetMediaSource() const override
694 : {
695 0 : return MediaSourceEnum::Other;
696 : }
697 :
698 0 : void Stop() override
699 : {
700 0 : if (!mCaptureStream) {
701 0 : NS_ERROR("No stream");
702 0 : return;
703 : }
704 :
705 0 : mCaptureStream->StopCapture();
706 : }
707 :
708 : private:
709 0 : virtual ~CanvasCaptureTrackSource() {}
710 :
711 : RefPtr<CanvasCaptureMediaStream> mCaptureStream;
712 : };
713 :
714 0 : NS_IMPL_ADDREF_INHERITED(CanvasCaptureTrackSource,
715 : MediaStreamTrackSource)
716 0 : NS_IMPL_RELEASE_INHERITED(CanvasCaptureTrackSource,
717 : MediaStreamTrackSource)
718 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(CanvasCaptureTrackSource)
719 0 : NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSource)
720 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(CanvasCaptureTrackSource,
721 : MediaStreamTrackSource,
722 : mCaptureStream)
723 :
724 : already_AddRefed<CanvasCaptureMediaStream>
725 0 : HTMLCanvasElement::CaptureStream(const Optional<double>& aFrameRate,
726 : ErrorResult& aRv)
727 : {
728 0 : if (IsWriteOnly()) {
729 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
730 0 : return nullptr;
731 : }
732 :
733 0 : nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
734 0 : if (!window) {
735 0 : aRv.Throw(NS_ERROR_FAILURE);
736 0 : return nullptr;
737 : }
738 :
739 0 : if (!mCurrentContext) {
740 0 : aRv.Throw(NS_ERROR_NOT_INITIALIZED);
741 0 : return nullptr;
742 : }
743 :
744 : RefPtr<CanvasCaptureMediaStream> stream =
745 0 : CanvasCaptureMediaStream::CreateSourceStream(window, this);
746 0 : if (!stream) {
747 0 : aRv.Throw(NS_ERROR_FAILURE);
748 0 : return nullptr;
749 : }
750 :
751 0 : TrackID videoTrackId = 1;
752 0 : nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
753 : nsresult rv =
754 0 : stream->Init(aFrameRate, videoTrackId, principal);
755 0 : if (NS_FAILED(rv)) {
756 0 : aRv.Throw(rv);
757 0 : return nullptr;
758 : }
759 :
760 : RefPtr<MediaStreamTrack> track =
761 0 : stream->CreateDOMTrack(videoTrackId, MediaSegment::VIDEO,
762 0 : new CanvasCaptureTrackSource(principal, stream));
763 0 : stream->AddTrackInternal(track);
764 :
765 0 : rv = RegisterFrameCaptureListener(stream->FrameCaptureListener());
766 0 : if (NS_FAILED(rv)) {
767 0 : aRv.Throw(rv);
768 0 : return nullptr;
769 : }
770 :
771 0 : return stream.forget();
772 : }
773 :
774 : nsresult
775 0 : HTMLCanvasElement::ExtractData(nsAString& aType,
776 : const nsAString& aOptions,
777 : nsIInputStream** aStream)
778 : {
779 0 : return ImageEncoder::ExtractData(aType,
780 : aOptions,
781 : GetSize(),
782 : mCurrentContext,
783 : mAsyncCanvasRenderer,
784 0 : aStream);
785 : }
786 :
787 : nsresult
788 0 : HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
789 : const nsAString& aMimeType,
790 : const JS::Value& aEncoderOptions,
791 : nsAString& aDataURL)
792 : {
793 0 : nsIntSize size = GetWidthHeight();
794 0 : if (size.height == 0 || size.width == 0) {
795 0 : aDataURL = NS_LITERAL_STRING("data:,");
796 0 : return NS_OK;
797 : }
798 :
799 0 : nsAutoString type;
800 0 : nsContentUtils::ASCIIToLower(aMimeType, type);
801 :
802 0 : nsAutoString params;
803 : bool usingCustomParseOptions;
804 : nsresult rv =
805 0 : ParseParams(aCx, type, aEncoderOptions, params, &usingCustomParseOptions);
806 0 : if (NS_FAILED(rv)) {
807 0 : return rv;
808 : }
809 :
810 0 : nsCOMPtr<nsIInputStream> stream;
811 0 : rv = ExtractData(type, params, getter_AddRefs(stream));
812 :
813 : // If there are unrecognized custom parse options, we should fall back to
814 : // the default values for the encoder without any options at all.
815 0 : if (rv == NS_ERROR_INVALID_ARG && usingCustomParseOptions) {
816 0 : rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
817 : }
818 :
819 0 : NS_ENSURE_SUCCESS(rv, rv);
820 :
821 : // build data URL string
822 0 : aDataURL = NS_LITERAL_STRING("data:") + type + NS_LITERAL_STRING(";base64,");
823 :
824 : uint64_t count;
825 0 : rv = stream->Available(&count);
826 0 : NS_ENSURE_SUCCESS(rv, rv);
827 0 : NS_ENSURE_TRUE(count <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
828 :
829 0 : return Base64EncodeInputStream(stream, aDataURL, (uint32_t)count, aDataURL.Length());
830 : }
831 :
832 : void
833 0 : HTMLCanvasElement::ToBlob(JSContext* aCx,
834 : BlobCallback& aCallback,
835 : const nsAString& aType,
836 : JS::Handle<JS::Value> aParams,
837 : ErrorResult& aRv)
838 : {
839 : // do a trust check if this is a write-only canvas
840 0 : if (mWriteOnly &&
841 0 : !nsContentUtils::CallerHasPermission(aCx, NS_LITERAL_STRING("<all_urls>"))) {
842 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
843 0 : return;
844 : }
845 :
846 0 : nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
847 0 : MOZ_ASSERT(global);
848 :
849 0 : nsIntSize elemSize = GetWidthHeight();
850 0 : if (elemSize.width == 0 || elemSize.height == 0) {
851 : // According to spec, blob should return null if either its horizontal
852 : // dimension or its vertical dimension is zero. See link below.
853 : // https://html.spec.whatwg.org/multipage/scripting.html#dom-canvas-toblob
854 0 : OwnerDoc()->Dispatch(
855 : "FireNullBlobEvent",
856 : TaskCategory::Other,
857 0 : NewRunnableMethod<Blob*, const char*>(
858 : "dom::HTMLCanvasElement::ToBlob",
859 0 : &aCallback,
860 : static_cast<void (BlobCallback::*)(Blob*, const char*)>(
861 : &BlobCallback::Call),
862 : nullptr,
863 0 : nullptr));
864 0 : return;
865 : }
866 :
867 0 : CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
868 0 : aParams, aRv);
869 :
870 : }
871 :
872 : OffscreenCanvas*
873 0 : HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
874 : {
875 0 : if (mCurrentContext) {
876 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
877 0 : return nullptr;
878 : }
879 :
880 0 : if (!mOffscreenCanvas) {
881 0 : nsIntSize sz = GetWidthHeight();
882 0 : RefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
883 0 : renderer->SetWidth(sz.width);
884 0 : renderer->SetHeight(sz.height);
885 :
886 : nsCOMPtr<nsIGlobalObject> global =
887 0 : do_QueryInterface(OwnerDoc()->GetInnerWindow());
888 : mOffscreenCanvas = new OffscreenCanvas(global,
889 0 : sz.width,
890 0 : sz.height,
891 0 : GetCompositorBackendType(),
892 0 : renderer);
893 0 : if (mWriteOnly) {
894 0 : mOffscreenCanvas->SetWriteOnly();
895 : }
896 :
897 0 : if (!mContextObserver) {
898 0 : mContextObserver = new HTMLCanvasElementObserver(this);
899 : }
900 : } else {
901 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
902 : }
903 :
904 0 : return mOffscreenCanvas;
905 : }
906 :
907 : already_AddRefed<File>
908 0 : HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
909 : const nsAString& aType,
910 : CallerType aCallerType,
911 : ErrorResult& aRv)
912 : {
913 0 : OwnerDoc()->WarnOnceAbout(nsIDocument::eMozGetAsFile);
914 :
915 : // do a trust check if this is a write-only canvas
916 0 : if (mWriteOnly && aCallerType != CallerType::System) {
917 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
918 0 : return nullptr;
919 : }
920 :
921 :
922 0 : RefPtr<File> file;
923 0 : aRv = MozGetAsFileImpl(aName, aType, getter_AddRefs(file));
924 0 : if (NS_WARN_IF(aRv.Failed())) {
925 0 : return nullptr;
926 : }
927 0 : return file.forget();
928 : }
929 :
930 : nsresult
931 0 : HTMLCanvasElement::MozGetAsFileImpl(const nsAString& aName,
932 : const nsAString& aType,
933 : File** aResult)
934 : {
935 0 : nsCOMPtr<nsIInputStream> stream;
936 0 : nsAutoString type(aType);
937 0 : nsresult rv = ExtractData(type, EmptyString(), getter_AddRefs(stream));
938 0 : NS_ENSURE_SUCCESS(rv, rv);
939 :
940 : uint64_t imgSize;
941 0 : rv = stream->Available(&imgSize);
942 0 : NS_ENSURE_SUCCESS(rv, rv);
943 0 : NS_ENSURE_TRUE(imgSize <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG);
944 :
945 0 : void* imgData = nullptr;
946 0 : rv = NS_ReadInputStreamToBuffer(stream, &imgData, (uint32_t)imgSize);
947 0 : NS_ENSURE_SUCCESS(rv, rv);
948 :
949 0 : JSContext* cx = nsContentUtils::GetCurrentJSContext();
950 0 : if (cx) {
951 0 : JS_updateMallocCounter(cx, imgSize);
952 : }
953 :
954 0 : nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(OwnerDoc()->GetScopeObject());
955 :
956 : // The File takes ownership of the buffer
957 : RefPtr<File> file =
958 0 : File::CreateMemoryFile(win, imgData, (uint32_t)imgSize, aName, type,
959 0 : PR_Now());
960 :
961 0 : file.forget(aResult);
962 0 : return NS_OK;
963 : }
964 :
965 : nsresult
966 0 : HTMLCanvasElement::GetContext(const nsAString& aContextId,
967 : nsISupports** aContext)
968 : {
969 0 : ErrorResult rv;
970 0 : *aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
971 0 : return rv.StealNSResult();
972 : }
973 :
974 : already_AddRefed<nsISupports>
975 0 : HTMLCanvasElement::GetContext(JSContext* aCx,
976 : const nsAString& aContextId,
977 : JS::Handle<JS::Value> aContextOptions,
978 : ErrorResult& aRv)
979 : {
980 0 : if (mOffscreenCanvas) {
981 0 : return nullptr;
982 : }
983 :
984 : return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
985 0 : aContextOptions.isObject() ? aContextOptions : JS::NullHandleValue,
986 0 : aRv);
987 : }
988 :
989 : already_AddRefed<nsISupports>
990 0 : HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
991 : ErrorResult& aRv)
992 : {
993 : // Note that we're a [ChromeOnly] method, so from JS we can only be called by
994 : // system code.
995 :
996 : // We only support 2d shmem contexts for now.
997 0 : if (!aContextId.EqualsLiteral("2d")) {
998 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
999 0 : return nullptr;
1000 : }
1001 :
1002 0 : CanvasContextType contextType = CanvasContextType::Canvas2D;
1003 :
1004 0 : if (!mCurrentContext) {
1005 : // This canvas doesn't have a context yet.
1006 :
1007 0 : RefPtr<nsICanvasRenderingContextInternal> context;
1008 0 : context = CreateContext(contextType);
1009 0 : if (!context) {
1010 0 : return nullptr;
1011 : }
1012 :
1013 0 : mCurrentContext = context;
1014 0 : mCurrentContext->SetIsIPC(true);
1015 0 : mCurrentContextType = contextType;
1016 :
1017 0 : ErrorResult dummy;
1018 0 : nsresult rv = UpdateContext(nullptr, JS::NullHandleValue, dummy);
1019 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
1020 0 : aRv.Throw(rv);
1021 0 : return nullptr;
1022 : }
1023 : } else {
1024 : // We already have a context of some type.
1025 0 : if (contextType != mCurrentContextType) {
1026 0 : aRv.Throw(NS_ERROR_INVALID_ARG);
1027 0 : return nullptr;
1028 : }
1029 : }
1030 :
1031 0 : nsCOMPtr<nsISupports> context(mCurrentContext);
1032 0 : return context.forget();
1033 : }
1034 :
1035 :
1036 : nsIntSize
1037 0 : HTMLCanvasElement::GetSize()
1038 : {
1039 0 : return GetWidthHeight();
1040 : }
1041 :
1042 : bool
1043 0 : HTMLCanvasElement::IsWriteOnly()
1044 : {
1045 0 : return mWriteOnly;
1046 : }
1047 :
1048 : void
1049 0 : HTMLCanvasElement::SetWriteOnly()
1050 : {
1051 0 : mWriteOnly = true;
1052 0 : }
1053 :
1054 : void
1055 0 : HTMLCanvasElement::InvalidateCanvasContent(const gfx::Rect* damageRect)
1056 : {
1057 : // We don't need to flush anything here; if there's no frame or if
1058 : // we plan to reframe we don't need to invalidate it anyway.
1059 0 : nsIFrame *frame = GetPrimaryFrame();
1060 0 : if (!frame)
1061 0 : return;
1062 :
1063 0 : ActiveLayerTracker::NotifyContentChange(frame);
1064 :
1065 0 : Layer* layer = nullptr;
1066 0 : if (damageRect) {
1067 0 : nsIntSize size = GetWidthHeight();
1068 0 : if (size.width != 0 && size.height != 0) {
1069 0 : gfx::IntRect invalRect = gfx::IntRect::Truncate(*damageRect);
1070 0 : layer = frame->InvalidateLayer(nsDisplayItem::TYPE_CANVAS, &invalRect);
1071 : }
1072 : } else {
1073 0 : layer = frame->InvalidateLayer(nsDisplayItem::TYPE_CANVAS);
1074 : }
1075 0 : if (layer) {
1076 0 : static_cast<CanvasLayer*>(layer)->Updated();
1077 : }
1078 :
1079 : /*
1080 : * Treat canvas invalidations as animation activity for JS. Frequently
1081 : * invalidating a canvas will feed into heuristics and cause JIT code to be
1082 : * kept around longer, for smoother animations.
1083 : */
1084 : nsCOMPtr<nsIGlobalObject> global =
1085 0 : do_QueryInterface(OwnerDoc()->GetInnerWindow());
1086 :
1087 0 : if (global) {
1088 0 : if (JSObject *obj = global->GetGlobalJSObject()) {
1089 0 : js::NotifyAnimationActivity(obj);
1090 : }
1091 : }
1092 : }
1093 :
1094 : void
1095 0 : HTMLCanvasElement::InvalidateCanvas()
1096 : {
1097 : // We don't need to flush anything here; if there's no frame or if
1098 : // we plan to reframe we don't need to invalidate it anyway.
1099 0 : nsIFrame *frame = GetPrimaryFrame();
1100 0 : if (!frame)
1101 0 : return;
1102 :
1103 0 : frame->InvalidateFrame();
1104 : }
1105 :
1106 : int32_t
1107 0 : HTMLCanvasElement::CountContexts()
1108 : {
1109 0 : if (mCurrentContext)
1110 0 : return 1;
1111 :
1112 0 : return 0;
1113 : }
1114 :
1115 : nsICanvasRenderingContextInternal *
1116 0 : HTMLCanvasElement::GetContextAtIndex(int32_t index)
1117 : {
1118 0 : if (mCurrentContext && index == 0)
1119 0 : return mCurrentContext;
1120 :
1121 0 : return nullptr;
1122 : }
1123 :
1124 : bool
1125 0 : HTMLCanvasElement::GetIsOpaque()
1126 : {
1127 0 : if (mCurrentContext) {
1128 0 : return mCurrentContext->GetIsOpaque();
1129 : }
1130 :
1131 0 : return GetOpaqueAttr();
1132 : }
1133 :
1134 : bool
1135 0 : HTMLCanvasElement::GetOpaqueAttr()
1136 : {
1137 0 : return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
1138 : }
1139 :
1140 : already_AddRefed<Layer>
1141 0 : HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
1142 : Layer *aOldLayer,
1143 : LayerManager *aManager)
1144 : {
1145 : // The address of sOffscreenCanvasLayerUserDataDummy is used as the user
1146 : // data key for retained LayerManagers managed by FrameLayerBuilder.
1147 : // We don't much care about what value in it, so just assign a dummy
1148 : // value for it.
1149 : static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
1150 :
1151 0 : if (mCurrentContext) {
1152 0 : return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager, mVRPresentationActive);
1153 : }
1154 :
1155 0 : if (mOffscreenCanvas) {
1156 0 : if (!mResetLayer &&
1157 0 : aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
1158 0 : RefPtr<Layer> ret = aOldLayer;
1159 0 : return ret.forget();
1160 : }
1161 :
1162 0 : RefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
1163 0 : if (!layer) {
1164 0 : NS_WARNING("CreateCanvasLayer failed!");
1165 0 : return nullptr;
1166 : }
1167 :
1168 0 : LayerUserData* userData = nullptr;
1169 0 : layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
1170 :
1171 0 : CanvasLayer::Data data;
1172 0 : data.mRenderer = GetAsyncCanvasRenderer();
1173 0 : data.mSize = GetWidthHeight();
1174 0 : layer->Initialize(data);
1175 :
1176 0 : layer->Updated();
1177 0 : return layer.forget();
1178 : }
1179 :
1180 0 : return nullptr;
1181 : }
1182 :
1183 : bool
1184 0 : HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager)
1185 : {
1186 0 : if (mCurrentContext) {
1187 0 : return mCurrentContext->ShouldForceInactiveLayer(aManager);
1188 : }
1189 :
1190 0 : if (mOffscreenCanvas) {
1191 : // TODO: We should handle offscreen canvas case.
1192 0 : return false;
1193 : }
1194 :
1195 0 : return true;
1196 : }
1197 :
1198 : void
1199 0 : HTMLCanvasElement::MarkContextClean()
1200 : {
1201 0 : if (!mCurrentContext)
1202 0 : return;
1203 :
1204 0 : mCurrentContext->MarkContextClean();
1205 : }
1206 :
1207 : void
1208 0 : HTMLCanvasElement::MarkContextCleanForFrameCapture()
1209 : {
1210 0 : if (!mCurrentContext)
1211 0 : return;
1212 :
1213 0 : mCurrentContext->MarkContextCleanForFrameCapture();
1214 : }
1215 :
1216 : bool
1217 0 : HTMLCanvasElement::IsContextCleanForFrameCapture()
1218 : {
1219 0 : return mCurrentContext && mCurrentContext->IsContextCleanForFrameCapture();
1220 : }
1221 :
1222 : nsresult
1223 0 : HTMLCanvasElement::RegisterFrameCaptureListener(FrameCaptureListener* aListener)
1224 : {
1225 0 : WeakPtr<FrameCaptureListener> listener = aListener;
1226 :
1227 0 : if (mRequestedFrameListeners.Contains(listener)) {
1228 0 : return NS_OK;
1229 : }
1230 :
1231 0 : if (!mRequestedFrameRefreshObserver) {
1232 0 : nsIDocument* doc = OwnerDoc();
1233 0 : if (!doc) {
1234 0 : return NS_ERROR_FAILURE;
1235 : }
1236 :
1237 0 : while (doc->GetParentDocument()) {
1238 0 : doc = doc->GetParentDocument();
1239 : }
1240 :
1241 0 : nsIPresShell* shell = doc->GetShell();
1242 0 : if (!shell) {
1243 0 : return NS_ERROR_FAILURE;
1244 : }
1245 :
1246 0 : nsPresContext* context = shell->GetPresContext();
1247 0 : if (!context) {
1248 0 : return NS_ERROR_FAILURE;
1249 : }
1250 :
1251 0 : context = context->GetRootPresContext();
1252 0 : if (!context) {
1253 0 : return NS_ERROR_FAILURE;
1254 : }
1255 :
1256 0 : nsRefreshDriver* driver = context->RefreshDriver();
1257 0 : if (!driver) {
1258 0 : return NS_ERROR_FAILURE;
1259 : }
1260 :
1261 : mRequestedFrameRefreshObserver =
1262 0 : new RequestedFrameRefreshObserver(this, driver);
1263 : }
1264 :
1265 0 : mRequestedFrameListeners.AppendElement(listener);
1266 0 : mRequestedFrameRefreshObserver->Register();
1267 0 : return NS_OK;
1268 : }
1269 :
1270 : bool
1271 0 : HTMLCanvasElement::IsFrameCaptureRequested() const
1272 : {
1273 0 : for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1274 0 : if (!listener) {
1275 0 : continue;
1276 : }
1277 :
1278 0 : if (listener->FrameCaptureRequested()) {
1279 0 : return true;
1280 : }
1281 : }
1282 0 : return false;
1283 : }
1284 :
1285 : void
1286 0 : HTMLCanvasElement::ProcessDestroyedFrameListeners()
1287 : {
1288 : // Loop backwards to allow removing elements in the loop.
1289 0 : for (int i = mRequestedFrameListeners.Length() - 1; i >= 0; --i) {
1290 0 : WeakPtr<FrameCaptureListener> listener = mRequestedFrameListeners[i];
1291 0 : if (!listener) {
1292 : // listener was destroyed. Remove it from the list.
1293 0 : mRequestedFrameListeners.RemoveElementAt(i);
1294 0 : continue;
1295 : }
1296 : }
1297 :
1298 0 : if (mRequestedFrameListeners.IsEmpty()) {
1299 0 : mRequestedFrameRefreshObserver->Unregister();
1300 : }
1301 0 : }
1302 :
1303 : void
1304 0 : HTMLCanvasElement::SetFrameCapture(already_AddRefed<SourceSurface> aSurface,
1305 : const TimeStamp& aTime)
1306 : {
1307 0 : RefPtr<SourceSurface> surface = aSurface;
1308 0 : RefPtr<SourceSurfaceImage> image = new SourceSurfaceImage(surface->GetSize(), surface);
1309 :
1310 0 : for (WeakPtr<FrameCaptureListener> listener : mRequestedFrameListeners) {
1311 0 : if (!listener) {
1312 0 : continue;
1313 : }
1314 :
1315 0 : RefPtr<Image> imageRefCopy = image.get();
1316 0 : listener->NewFrame(imageRefCopy.forget(), aTime);
1317 : }
1318 0 : }
1319 :
1320 : already_AddRefed<SourceSurface>
1321 0 : HTMLCanvasElement::GetSurfaceSnapshot(gfxAlphaType* const aOutAlphaType)
1322 : {
1323 0 : if (!mCurrentContext)
1324 0 : return nullptr;
1325 :
1326 0 : return mCurrentContext->GetSurfaceSnapshot(aOutAlphaType);
1327 : }
1328 :
1329 : AsyncCanvasRenderer*
1330 0 : HTMLCanvasElement::GetAsyncCanvasRenderer()
1331 : {
1332 0 : if (!mAsyncCanvasRenderer) {
1333 0 : mAsyncCanvasRenderer = new AsyncCanvasRenderer();
1334 0 : mAsyncCanvasRenderer->mHTMLCanvasElement = this;
1335 : }
1336 :
1337 0 : return mAsyncCanvasRenderer;
1338 : }
1339 :
1340 : layers::LayersBackend
1341 0 : HTMLCanvasElement::GetCompositorBackendType() const
1342 : {
1343 0 : nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
1344 0 : if (docWidget) {
1345 0 : layers::LayerManager* layerManager = docWidget->GetLayerManager();
1346 0 : if (layerManager) {
1347 0 : return layerManager->GetCompositorBackendType();
1348 : }
1349 : }
1350 :
1351 0 : return LayersBackend::LAYERS_NONE;
1352 : }
1353 :
1354 : void
1355 0 : HTMLCanvasElement::OnVisibilityChange()
1356 : {
1357 0 : if (OwnerDoc()->Hidden()) {
1358 0 : return;
1359 : }
1360 :
1361 0 : if (mOffscreenCanvas) {
1362 0 : class Runnable final : public CancelableRunnable
1363 : {
1364 : public:
1365 0 : explicit Runnable(AsyncCanvasRenderer* aRenderer)
1366 0 : : mozilla::CancelableRunnable("Runnable")
1367 0 : , mRenderer(aRenderer)
1368 0 : {}
1369 :
1370 0 : NS_IMETHOD Run() override
1371 : {
1372 0 : if (mRenderer && mRenderer->mContext) {
1373 0 : mRenderer->mContext->OnVisibilityChange();
1374 : }
1375 :
1376 0 : return NS_OK;
1377 : }
1378 :
1379 : private:
1380 : RefPtr<AsyncCanvasRenderer> mRenderer;
1381 : };
1382 :
1383 0 : RefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
1384 0 : nsCOMPtr<nsIEventTarget> activeTarget = mAsyncCanvasRenderer->GetActiveEventTarget();
1385 0 : if (activeTarget) {
1386 0 : activeTarget->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
1387 : }
1388 0 : return;
1389 : }
1390 :
1391 0 : if (mCurrentContext) {
1392 0 : mCurrentContext->OnVisibilityChange();
1393 : }
1394 : }
1395 :
1396 : void
1397 0 : HTMLCanvasElement::OnMemoryPressure()
1398 : {
1399 0 : if (mOffscreenCanvas) {
1400 0 : class Runnable final : public CancelableRunnable
1401 : {
1402 : public:
1403 0 : explicit Runnable(AsyncCanvasRenderer* aRenderer)
1404 0 : : mozilla::CancelableRunnable("Runnable")
1405 0 : , mRenderer(aRenderer)
1406 0 : {}
1407 :
1408 0 : NS_IMETHOD Run() override
1409 : {
1410 0 : if (mRenderer && mRenderer->mContext) {
1411 0 : mRenderer->mContext->OnMemoryPressure();
1412 : }
1413 :
1414 0 : return NS_OK;
1415 : }
1416 :
1417 : private:
1418 : RefPtr<AsyncCanvasRenderer> mRenderer;
1419 : };
1420 :
1421 0 : RefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
1422 0 : nsCOMPtr<nsIEventTarget> activeTarget = mAsyncCanvasRenderer->GetActiveEventTarget();
1423 0 : if (activeTarget) {
1424 0 : activeTarget->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
1425 : }
1426 0 : return;
1427 : }
1428 :
1429 0 : if (mCurrentContext) {
1430 0 : mCurrentContext->OnMemoryPressure();
1431 : }
1432 : }
1433 :
1434 : /* static */ void
1435 0 : HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
1436 : {
1437 0 : HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
1438 0 : if (!element) {
1439 0 : return;
1440 : }
1441 :
1442 0 : if (element->GetWidthHeight() == aRenderer->GetSize()) {
1443 0 : return;
1444 : }
1445 :
1446 0 : gfx::IntSize asyncCanvasSize = aRenderer->GetSize();
1447 :
1448 0 : ErrorResult rv;
1449 0 : element->SetUnsignedIntAttr(nsGkAtoms::width, asyncCanvasSize.width,
1450 0 : DEFAULT_CANVAS_WIDTH, rv);
1451 0 : if (rv.Failed()) {
1452 0 : NS_WARNING("Failed to set width attribute to a canvas element asynchronously.");
1453 : }
1454 :
1455 0 : element->SetUnsignedIntAttr(nsGkAtoms::height, asyncCanvasSize.height,
1456 0 : DEFAULT_CANVAS_HEIGHT, rv);
1457 0 : if (rv.Failed()) {
1458 0 : NS_WARNING("Failed to set height attribute to a canvas element asynchronously.");
1459 : }
1460 :
1461 0 : element->mResetLayer = true;
1462 : }
1463 :
1464 : /* static */ void
1465 0 : HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
1466 : {
1467 0 : HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
1468 0 : if (!element) {
1469 0 : return;
1470 : }
1471 :
1472 0 : element->InvalidateCanvasContent(nullptr);
1473 : }
1474 :
1475 : void
1476 0 : HTMLCanvasElement::StartVRPresentation()
1477 : {
1478 0 : if (GetCurrentContextType() != CanvasContextType::WebGL1 &&
1479 0 : GetCurrentContextType() != CanvasContextType::WebGL2) {
1480 0 : return;
1481 : }
1482 :
1483 0 : WebGLContext* webgl = static_cast<WebGLContext*>(GetContextAtIndex(0));
1484 0 : if (!webgl) {
1485 0 : return;
1486 : }
1487 :
1488 0 : if (!webgl->StartVRPresentation()) {
1489 0 : return;
1490 : }
1491 :
1492 0 : mVRPresentationActive = true;
1493 : }
1494 :
1495 : void
1496 0 : HTMLCanvasElement::StopVRPresentation()
1497 : {
1498 0 : mVRPresentationActive = false;
1499 0 : }
1500 :
1501 : already_AddRefed<layers::SharedSurfaceTextureClient>
1502 0 : HTMLCanvasElement::GetVRFrame()
1503 : {
1504 0 : if (GetCurrentContextType() != CanvasContextType::WebGL1 &&
1505 0 : GetCurrentContextType() != CanvasContextType::WebGL2) {
1506 0 : return nullptr;
1507 : }
1508 :
1509 0 : WebGLContext* webgl = static_cast<WebGLContext*>(GetContextAtIndex(0));
1510 0 : if (!webgl) {
1511 0 : return nullptr;
1512 : }
1513 :
1514 0 : return webgl->GetVRFrame();
1515 : }
1516 :
1517 : } // namespace dom
1518 : } // namespace mozilla
|