nsICanvasRenderingContextInternal.cpp (8976B)
1 /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "nsICanvasRenderingContextInternal.h" 7 8 #include "mozilla/ErrorResult.h" 9 #include "mozilla/PresShell.h" 10 #include "mozilla/dom/CanvasUtils.h" 11 #include "mozilla/dom/Document.h" 12 #include "mozilla/dom/Event.h" 13 #include "mozilla/dom/WorkerCommon.h" 14 #include "mozilla/dom/WorkerPrivate.h" 15 #include "mozilla/dom/WorkerRunnable.h" 16 #include "mozilla/gfx/DrawTargetRecording.h" 17 #include "nsContentUtils.h" 18 #include "nsPIDOMWindow.h" 19 #include "nsRFPService.h" 20 #include "nsRefreshDriver.h" 21 #include "nsThreadUtils.h" 22 23 static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection"); 24 25 nsICanvasRenderingContextInternal::nsICanvasRenderingContextInternal() = 26 default; 27 28 nsICanvasRenderingContextInternal::~nsICanvasRenderingContextInternal() = 29 default; 30 31 mozilla::PresShell* nsICanvasRenderingContextInternal::GetPresShell() { 32 if (mCanvasElement) { 33 return mCanvasElement->OwnerDoc()->GetPresShell(); 34 } 35 return nullptr; 36 } 37 38 nsIGlobalObject* nsICanvasRenderingContextInternal::GetParentObject() const { 39 if (mCanvasElement) { 40 return mCanvasElement->OwnerDoc()->GetScopeObject(); 41 } 42 if (mOffscreenCanvas) { 43 return mOffscreenCanvas->GetParentObject(); 44 } 45 return nullptr; 46 } 47 48 class RecordCanvasUsageRunnable final 49 : public mozilla::dom::WorkerMainThreadRunnable { 50 public: 51 RecordCanvasUsageRunnable(mozilla::dom::WorkerPrivate* aWorkerPrivate, 52 const mozilla::CanvasUsage& aUsage) 53 : WorkerMainThreadRunnable(aWorkerPrivate, 54 "RecordCanvasUsageRunnable"_ns), 55 mUsage(aUsage) { 56 MOZ_ASSERT(aWorkerPrivate); 57 aWorkerPrivate->AssertIsOnWorkerThread(); 58 } 59 60 protected: 61 MOZ_CAN_RUN_SCRIPT_BOUNDARY bool MainThreadRun() override { 62 mozilla::AssertIsOnMainThread(); 63 RefPtr<mozilla::dom::Document> doc; 64 if (!mWorkerRef) { 65 MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Error, 66 ("RecordCanvasUsageRunnable::MainThreadRun - null mWorkerRef")); 67 return false; 68 } 69 auto* priv = mWorkerRef->Private(); 70 if (!priv) { 71 MOZ_LOG( 72 gFingerprinterDetection, mozilla::LogLevel::Error, 73 ("RecordCanvasUsageRunnable::MainThreadRun - null worker private")); 74 return false; 75 } 76 doc = priv->GetDocument(); 77 if (!doc) { 78 MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Error, 79 ("RecordCanvasUsageRunnable::MainThreadRun - null document")); 80 return false; 81 } 82 doc->RecordCanvasUsage(mUsage); 83 return true; 84 } 85 86 private: 87 mozilla::CanvasUsage mUsage; 88 }; 89 90 void nsICanvasRenderingContextInternal::RecordCanvasUsage( 91 mozilla::CanvasExtractionAPI aAPI, mozilla::CSSIntSize size) const { 92 mozilla::dom::CanvasContextType contextType; 93 if (mCanvasElement) { 94 contextType = mCanvasElement->GetCurrentContextType(); 95 auto usage = 96 mozilla::CanvasUsage::CreateUsage(false, contextType, aAPI, size, this); 97 mCanvasElement->OwnerDoc()->RecordCanvasUsage(usage); 98 } 99 if (mOffscreenCanvas) { 100 contextType = mOffscreenCanvas->GetContextType(); 101 auto usage = 102 mozilla::CanvasUsage::CreateUsage(true, contextType, aAPI, size, this); 103 if (NS_IsMainThread()) { 104 nsIGlobalObject* global = mOffscreenCanvas->GetOwnerGlobal(); 105 if (global) { 106 if (nsPIDOMWindowInner* inner = global->GetAsInnerWindow()) { 107 if (mozilla::dom::Document* doc = inner->GetExtantDoc()) { 108 doc->RecordCanvasUsage(usage); 109 } 110 } 111 } 112 } else { 113 mozilla::dom::WorkerPrivate* workerPrivate = 114 mozilla::dom::GetCurrentThreadWorkerPrivate(); 115 if (workerPrivate) { 116 RefPtr<RecordCanvasUsageRunnable> runnable = 117 new RecordCanvasUsageRunnable(workerPrivate, usage); 118 mozilla::ErrorResult rv; 119 runnable->Dispatch(workerPrivate, mozilla::dom::WorkerStatus::Canceling, 120 rv); 121 if (rv.Failed()) { 122 rv.SuppressException(); 123 MOZ_LOG(gFingerprinterDetection, mozilla::LogLevel::Error, 124 ("RecordCanvasUsageRunnable dispatch failed")); 125 } 126 } 127 } 128 } 129 } 130 131 nsIPrincipal* nsICanvasRenderingContextInternal::PrincipalOrNull() const { 132 if (mCanvasElement) { 133 return mCanvasElement->NodePrincipal(); 134 } 135 if (mOffscreenCanvas) { 136 nsIGlobalObject* global = mOffscreenCanvas->GetParentObject(); 137 if (global) { 138 return global->PrincipalOrNull(); 139 } 140 } 141 return nullptr; 142 } 143 144 nsICookieJarSettings* nsICanvasRenderingContextInternal::GetCookieJarSettings() 145 const { 146 if (mCanvasElement) { 147 return mCanvasElement->OwnerDoc()->CookieJarSettings(); 148 } 149 150 // If there is an offscreen canvas, attempt to retrieve its owner window 151 // and return the cookieJarSettings for the window's document, if available. 152 if (mOffscreenCanvas) { 153 nsCOMPtr<nsPIDOMWindowInner> win = 154 do_QueryInterface(mOffscreenCanvas->GetOwnerGlobal()); 155 156 if (win) { 157 return win->GetExtantDoc()->CookieJarSettings(); 158 } 159 160 // If the owner window cannot be retrieved, check if there is a current 161 // worker and return its cookie jar settings if available. 162 mozilla::dom::WorkerPrivate* worker = 163 mozilla::dom::GetCurrentThreadWorkerPrivate(); 164 165 if (worker) { 166 return worker->CookieJarSettings(); 167 } 168 } 169 170 return nullptr; 171 } 172 173 void nsICanvasRenderingContextInternal::RemovePostRefreshObserver() { 174 if (mRefreshDriver) { 175 mRefreshDriver->RemovePostRefreshObserver(this); 176 mRefreshDriver = nullptr; 177 } 178 } 179 180 void nsICanvasRenderingContextInternal::AddPostRefreshObserverIfNecessary() { 181 if (!GetPresShell() || !GetPresShell()->GetPresContext() || 182 !GetPresShell()->GetPresContext()->RefreshDriver()) { 183 return; 184 } 185 mRefreshDriver = GetPresShell()->GetPresContext()->RefreshDriver(); 186 mRefreshDriver->AddPostRefreshObserver(this); 187 } 188 189 void nsICanvasRenderingContextInternal::DoSecurityCheck( 190 nsIPrincipal* aPrincipal, bool aForceWriteOnly, bool aCORSUsed) { 191 if (mCanvasElement) { 192 mozilla::CanvasUtils::DoDrawImageSecurityCheck(mCanvasElement, aPrincipal, 193 aForceWriteOnly, aCORSUsed); 194 } else if (mOffscreenCanvas) { 195 mozilla::CanvasUtils::DoDrawImageSecurityCheck(mOffscreenCanvas, aPrincipal, 196 aForceWriteOnly, aCORSUsed); 197 } 198 } 199 200 bool nsICanvasRenderingContextInternal::ShouldResistFingerprinting( 201 mozilla::RFPTarget aTarget) const { 202 if (mCanvasElement) { 203 return mCanvasElement->OwnerDoc()->ShouldResistFingerprinting(aTarget); 204 } 205 if (mOffscreenCanvas) { 206 return mOffscreenCanvas->ShouldResistFingerprinting(aTarget); 207 } 208 // Last resort, just check the global preference 209 return nsContentUtils::ShouldResistFingerprinting("Fallback", aTarget); 210 } 211 212 bool nsICanvasRenderingContextInternal::DispatchEvent( 213 const nsAString& eventName, mozilla::CanBubble aCanBubble, 214 mozilla::Cancelable aIsCancelable) const { 215 bool useDefaultHandler = true; 216 217 if (mCanvasElement) { 218 nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(), 219 mCanvasElement, eventName, aCanBubble, 220 aIsCancelable, &useDefaultHandler); 221 } else if (mOffscreenCanvas) { 222 // OffscreenCanvas case 223 auto event = mozilla::MakeRefPtr<mozilla::dom::Event>(mOffscreenCanvas, 224 nullptr, nullptr); 225 event->InitEvent(eventName, aCanBubble, aIsCancelable); 226 event->SetTrusted(true); 227 useDefaultHandler = mOffscreenCanvas->DispatchEvent( 228 *event, mozilla::dom::CallerType::System, mozilla::IgnoreErrors()); 229 } 230 return useDefaultHandler; 231 } 232 233 already_AddRefed<mozilla::gfx::SourceSurface> 234 nsICanvasRenderingContextInternal::GetOptimizedSnapshot( 235 mozilla::gfx::DrawTarget* aTarget, gfxAlphaType* out_alphaType) { 236 if (aTarget && 237 aTarget->GetBackendType() == mozilla::gfx::BackendType::RECORDING) { 238 if (auto* actor = SupportsSnapshotExternalCanvas()) { 239 // If this snapshot is for a recording target, then try to avoid reading 240 // back any data by using SnapshotExternalCanvas instead. This avoids 241 // having sync interactions between GPU and content process. 242 if (RefPtr<mozilla::gfx::SourceSurface> surf = 243 static_cast<mozilla::gfx::DrawTargetRecording*>(aTarget) 244 ->SnapshotExternalCanvas(this, actor)) { 245 if (out_alphaType) { 246 *out_alphaType = 247 GetIsOpaque() ? gfxAlphaType::Opaque : gfxAlphaType::Premult; 248 } 249 return surf.forget(); 250 } 251 } 252 } 253 254 return GetSurfaceSnapshot(out_alphaType); 255 }