Coverage Report

Created: 2018-09-25 14:53

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