/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 |