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 #if !defined(mozilla_dom_HTMLCanvasElement_h) 7 # define mozilla_dom_HTMLCanvasElement_h 8 9 # include "mozilla/Attributes.h" 10 # include "mozilla/StateWatching.h" 11 # include "mozilla/WeakPtr.h" 12 # include "nsIDOMEventListener.h" 13 # include "nsIObserver.h" 14 # include "nsGenericHTMLElement.h" 15 # include "nsGkAtoms.h" 16 # include "nsSize.h" 17 # include "nsError.h" 18 19 # include "mozilla/dom/CanvasRenderingContextHelper.h" 20 # include "mozilla/gfx/Rect.h" 21 # include "mozilla/layers/LayersTypes.h" 22 23 class nsICanvasRenderingContextInternal; 24 class nsIInputStream; 25 class nsITimerCallback; 26 enum class gfxAlphaType; 27 enum class FrameCaptureState : uint8_t; 28 29 namespace mozilla { 30 31 class nsDisplayListBuilder; 32 class ClientWebGLContext; 33 34 namespace layers { 35 class CanvasRenderer; 36 class Image; 37 class ImageContainer; 38 class Layer; 39 class LayerManager; 40 class OOPCanvasRenderer; 41 class SharedSurfaceTextureClient; 42 class WebRenderCanvasData; 43 } // namespace layers 44 namespace gfx { 45 class SourceSurface; 46 class VRLayerChild; 47 } // namespace gfx 48 namespace webgpu { 49 class CanvasContext; 50 } // namespace webgpu 51 52 namespace dom { 53 class BlobCallback; 54 class CanvasCaptureMediaStream; 55 class File; 56 class HTMLCanvasPrintState; 57 class OffscreenCanvas; 58 class OffscreenCanvasDisplayHelper; 59 class PrintCallback; 60 class PWebGLChild; 61 class RequestedFrameRefreshObserver; 62 63 // Listen visibilitychange and memory-pressure event and inform 64 // context when event is fired. 65 class HTMLCanvasElementObserver final : public nsIObserver { 66 public: 67 NS_DECL_ISUPPORTS 68 NS_DECL_NSIOBSERVER 69 70 explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement); 71 void Destroy(); 72 73 void RegisterObserverEvents(); 74 void UnregisterObserverEvents(); 75 76 private: 77 ~HTMLCanvasElementObserver(); 78 79 HTMLCanvasElement* mElement; 80 }; 81 82 /* 83 * FrameCaptureListener is used by captureStream() as a way of getting video 84 * frames from the canvas. On a refresh driver tick after something has been 85 * drawn to the canvas since the last such tick, all registered 86 * FrameCaptureListeners that report true for FrameCaptureRequested() will be 87 * given a copy of the just-painted canvas. 88 * All FrameCaptureListeners get the same copy. 89 */ 90 class FrameCaptureListener : public SupportsWeakPtr { 91 public: 92 FrameCaptureListener() = default; 93 94 /* 95 * Indicates to the canvas whether or not this listener has requested a frame. 96 */ 97 virtual bool FrameCaptureRequested(const TimeStamp& aTime) const = 0; 98 99 /* 100 * Interface through which new video frames will be provided while 101 * `mFrameCaptureRequested` is `true`. 102 */ 103 virtual void NewFrame(already_AddRefed<layers::Image> aImage, 104 const TimeStamp& aTime) = 0; 105 106 protected: 107 virtual ~FrameCaptureListener() = default; 108 }; 109 110 class HTMLCanvasElement final : public nsGenericHTMLElement, 111 public CanvasRenderingContextHelper, 112 public SupportsWeakPtr { 113 enum { DEFAULT_CANVAS_WIDTH = 300, DEFAULT_CANVAS_HEIGHT = 150 }; 114 115 typedef layers::CanvasRenderer CanvasRenderer; 116 typedef layers::Layer Layer; 117 typedef layers::LayerManager LayerManager; 118 typedef layers::WebRenderCanvasData WebRenderCanvasData; 119 120 public: 121 explicit HTMLCanvasElement( 122 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo); 123 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLCanvasElement,canvas)124 NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLCanvasElement, canvas) 125 126 // nsISupports 127 NS_DECL_ISUPPORTS_INHERITED 128 129 // CC 130 NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLCanvasElement, 131 nsGenericHTMLElement) 132 133 // WebIDL 134 uint32_t Height() { 135 return GetUnsignedIntAttr(nsGkAtoms::height, DEFAULT_CANVAS_HEIGHT); 136 } Width()137 uint32_t Width() { 138 return GetUnsignedIntAttr(nsGkAtoms::width, DEFAULT_CANVAS_WIDTH); 139 } 140 void SetHeight(uint32_t aHeight, ErrorResult& aRv); 141 void SetWidth(uint32_t aWidth, ErrorResult& aRv); 142 143 already_AddRefed<nsISupports> GetContext( 144 JSContext* aCx, const nsAString& aContextId, 145 JS::Handle<JS::Value> aContextOptions, ErrorResult& aRv); 146 147 void ToDataURL(JSContext* aCx, const nsAString& aType, 148 JS::Handle<JS::Value> aParams, nsAString& aDataURL, 149 nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv); 150 151 void ToBlob(JSContext* aCx, BlobCallback& aCallback, const nsAString& aType, 152 JS::Handle<JS::Value> aParams, nsIPrincipal& aSubjectPrincipal, 153 ErrorResult& aRv); 154 155 OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv); 156 MozOpaque()157 bool MozOpaque() const { return GetBoolAttr(nsGkAtoms::moz_opaque); } SetMozOpaque(bool aValue,ErrorResult & aRv)158 void SetMozOpaque(bool aValue, ErrorResult& aRv) { 159 if (mOffscreenCanvas) { 160 aRv.Throw(NS_ERROR_FAILURE); 161 return; 162 } 163 164 SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv); 165 } 166 already_AddRefed<File> MozGetAsFile(const nsAString& aName, 167 const nsAString& aType, 168 nsIPrincipal& aSubjectPrincipal, 169 ErrorResult& aRv); 170 already_AddRefed<nsISupports> MozGetIPCContext(const nsAString& aContextId, 171 ErrorResult& aRv); 172 PrintCallback* GetMozPrintCallback() const; 173 void SetMozPrintCallback(PrintCallback* aCallback); 174 175 already_AddRefed<CanvasCaptureMediaStream> CaptureStream( 176 const Optional<double>& aFrameRate, nsIPrincipal& aSubjectPrincipal, 177 ErrorResult& aRv); 178 179 /** 180 * Get the size in pixels of this canvas element 181 */ 182 nsIntSize GetSize(); 183 184 /** 185 * Determine whether the canvas is write-only. 186 */ 187 bool IsWriteOnly() const; 188 189 /** 190 * Force the canvas to be write-only, except for readers from 191 * a specific extension's content script expanded principal, if 192 * available. 193 */ 194 void SetWriteOnly(nsIPrincipal* aExpandedReader = nullptr); 195 196 /** 197 * Notify the placeholder offscreen canvas of an updated size. 198 */ 199 void InvalidateCanvasPlaceholder(uint32_t aWidth, uint32_t aHeight); 200 201 /** 202 * Notify that some canvas content has changed and the window may 203 * need to be updated. aDamageRect is in canvas coordinates. 204 */ 205 void InvalidateCanvasContent(const mozilla::gfx::Rect* aDamageRect); 206 /* 207 * Notify that we need to repaint the entire canvas, including updating of 208 * the layer tree. 209 */ 210 void InvalidateCanvas(); 211 GetCurrentContext()212 nsICanvasRenderingContextInternal* GetCurrentContext() { 213 return mCurrentContext; 214 } 215 216 /* 217 * Returns true if the canvas context content is guaranteed to be opaque 218 * across its entire area. 219 */ 220 bool GetIsOpaque(); 221 virtual bool GetOpaqueAttr() override; 222 223 virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot( 224 gfxAlphaType* aOutAlphaType = nullptr); 225 226 /* 227 * Register a FrameCaptureListener with this canvas. 228 * The canvas hooks into the RefreshDriver while there are 229 * FrameCaptureListeners registered. 230 * The registered FrameCaptureListeners are stored as WeakPtrs, thus it's the 231 * caller's responsibility to keep them alive. Once a registered 232 * FrameCaptureListener is destroyed it will be automatically deregistered. 233 */ 234 nsresult RegisterFrameCaptureListener(FrameCaptureListener* aListener, 235 bool aReturnPlaceholderData); 236 237 /* 238 * Returns true when there is at least one registered FrameCaptureListener 239 * that has requested a frame capture. 240 */ 241 bool IsFrameCaptureRequested(const TimeStamp& aTime) const; 242 243 /* 244 * Processes destroyed FrameCaptureListeners and removes them if necessary. 245 * Should there be none left, the FrameRefreshObserver will be unregistered. 246 */ 247 void ProcessDestroyedFrameListeners(); 248 249 /* 250 * Called by the RefreshDriver hook when a frame has been captured. 251 * Makes a copy of the provided surface and hands it to all 252 * FrameCaptureListeners having requested frame capture. 253 */ 254 void SetFrameCapture(already_AddRefed<gfx::SourceSurface> aSurface, 255 const TimeStamp& aTime); 256 257 virtual bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute, 258 const nsAString& aValue, 259 nsIPrincipal* aMaybeScriptedPrincipal, 260 nsAttrValue& aResult) override; 261 NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override; 262 nsChangeHint GetAttributeChangeHint(const nsAtom* aAttribute, 263 int32_t aModType) const override; 264 nsMapRuleToAttributesFunc GetAttributeMappingFunction() const override; 265 266 virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override; 267 nsresult CopyInnerTo(HTMLCanvasElement* aDest); 268 269 void GetEventTargetParent(EventChainPreVisitor& aVisitor) override; 270 271 static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, 272 MappedDeclarations&); 273 274 /* 275 * Helpers called by various users of Canvas 276 */ 277 278 already_AddRefed<layers::Image> GetAsImage(); 279 bool UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder, 280 WebRenderCanvasData* aCanvasData); 281 bool InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder, 282 CanvasRenderer* aRenderer); 283 284 // Call this whenever we need future changes to the canvas 285 // to trigger fresh invalidation requests. This needs to be called 286 // whenever we render the canvas contents to the screen, or whenever we 287 // take a snapshot of the canvas that needs to be "live" (e.g. -moz-element). 288 void MarkContextClean(); 289 290 // Call this after capturing a frame, so we can avoid unnecessary surface 291 // copies for future frames when no drawing has occurred. 292 void MarkContextCleanForFrameCapture(); 293 294 // Returns non-null when the current context supports captureStream(). 295 // The FrameCaptureState gets set to DIRTY when something is drawn. 296 Watchable<FrameCaptureState>* GetFrameCaptureState(); 297 298 nsresult GetContext(const nsAString& aContextId, nsISupports** aContext); 299 300 layers::LayersBackend GetCompositorBackendType() const; 301 302 void OnMemoryPressure(); 303 void OnDeviceReset(); 304 305 already_AddRefed<layers::SharedSurfaceTextureClient> GetVRFrame(); 306 void ClearVRFrame(); 307 MaybeModified()308 bool MaybeModified() const { return mMaybeModified; }; 309 310 protected: 311 virtual ~HTMLCanvasElement(); 312 void Destroy(); 313 314 virtual JSObject* WrapNode(JSContext* aCx, 315 JS::Handle<JSObject*> aGivenProto) override; 316 317 virtual nsIntSize GetWidthHeight() override; 318 319 virtual already_AddRefed<nsICanvasRenderingContextInternal> CreateContext( 320 CanvasContextType aContextType) override; 321 322 nsresult ExtractData(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, 323 nsAString& aType, const nsAString& aOptions, 324 nsIInputStream** aStream); 325 nsresult ToDataURLImpl(JSContext* aCx, nsIPrincipal& aSubjectPrincipal, 326 const nsAString& aMimeType, 327 const JS::Value& aEncoderOptions, nsAString& aDataURL); 328 nsresult MozGetAsFileImpl(const nsAString& aName, const nsAString& aType, 329 nsIPrincipal& aSubjectPrincipal, File** aResult); 330 MOZ_CAN_RUN_SCRIPT void CallPrintCallback(); 331 332 virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 333 const nsAttrValue* aValue, 334 const nsAttrValue* aOldValue, 335 nsIPrincipal* aSubjectPrincipal, 336 bool aNotify) override; 337 virtual nsresult OnAttrSetButNotChanged(int32_t aNamespaceID, nsAtom* aName, 338 const nsAttrValueOrString& aValue, 339 bool aNotify) override; 340 341 public: 342 ClientWebGLContext* GetWebGLContext(); 343 webgpu::CanvasContext* GetWebGPUContext(); 344 IsOffscreen()345 bool IsOffscreen() const { return !!mOffscreenCanvas; } GetOffscreenCanvas()346 OffscreenCanvas* GetOffscreenCanvas() const { return mOffscreenCanvas; } 347 348 RefPtr<layers::ImageContainer> GetImageContainer(); 349 350 protected: 351 bool mResetLayer; 352 bool mMaybeModified; // we fetched the context, so we may have written to the 353 // canvas 354 RefPtr<HTMLCanvasElement> mOriginalCanvas; 355 RefPtr<PrintCallback> mPrintCallback; 356 RefPtr<HTMLCanvasPrintState> mPrintState; 357 nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners; 358 RefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver; 359 RefPtr<CanvasRenderer> mCanvasRenderer; 360 RefPtr<OffscreenCanvas> mOffscreenCanvas; 361 RefPtr<OffscreenCanvasDisplayHelper> mOffscreenDisplay; 362 RefPtr<HTMLCanvasElementObserver> mContextObserver; 363 364 public: 365 // Record whether this canvas should be write-only or not. 366 // We set this when script paints an image from a different origin. 367 // We also transitively set it when script paints a canvas which 368 // is itself write-only. 369 bool mWriteOnly; 370 371 // When this canvas is (only) tainted by an image from an extension 372 // content script, allow reads from the same extension afterwards. 373 RefPtr<nsIPrincipal> mExpandedReader; 374 375 // Determines if the caller should be able to read the content. 376 bool CallerCanRead(JSContext* aCx); 377 378 bool IsPrintCallbackDone(); 379 380 void HandlePrintCallback(nsPresContext*); 381 382 nsresult DispatchPrintCallback(nsITimerCallback* aCallback); 383 384 void ResetPrintCallback(); 385 386 HTMLCanvasElement* GetOriginalCanvas(); 387 388 CanvasContextType GetCurrentContextType(); 389 390 private: 391 /** 392 * This function is called by AfterSetAttr and OnAttrSetButNotChanged. 393 * This function will be called by AfterSetAttr whether the attribute is being 394 * set or unset. 395 * 396 * @param aNamespaceID the namespace of the attr being set 397 * @param aName the localname of the attribute being set 398 * @param aNotify Whether we plan to notify document observers. 399 */ 400 void AfterMaybeChangeAttr(int32_t aNamespaceID, nsAtom* aName, bool aNotify); 401 }; 402 403 class HTMLCanvasPrintState final : public nsWrapperCache { 404 public: 405 HTMLCanvasPrintState(HTMLCanvasElement* aCanvas, 406 nsICanvasRenderingContextInternal* aContext, 407 nsITimerCallback* aCallback); 408 409 nsISupports* Context() const; 410 411 void Done(); 412 413 void NotifyDone(); 414 415 bool mIsDone; 416 417 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(HTMLCanvasPrintState) 418 NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(HTMLCanvasPrintState) 419 420 virtual JSObject* WrapObject(JSContext* cx, 421 JS::Handle<JSObject*> aGivenProto) override; 422 GetParentObject()423 HTMLCanvasElement* GetParentObject() { return mCanvas; } 424 425 private: 426 ~HTMLCanvasPrintState(); 427 bool mPendingNotify; 428 429 protected: 430 RefPtr<HTMLCanvasElement> mCanvas; 431 nsCOMPtr<nsICanvasRenderingContextInternal> mContext; 432 nsCOMPtr<nsITimerCallback> mCallback; 433 }; 434 435 } // namespace dom 436 } // namespace mozilla 437 438 #endif /* mozilla_dom_HTMLCanvasElement_h */ 439