2018-11-30 22:52:05 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2012-05-21 15:12:37 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
2009-06-26 00:30:56 +04:00
|
|
|
|
2009-09-03 04:47:49 +04:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdarg.h>
|
|
|
|
|
2009-06-26 00:30:56 +04:00
|
|
|
#include "nsICanvasRenderingContextInternal.h"
|
2012-11-11 03:30:15 +04:00
|
|
|
#include "nsIHTMLCollection.h"
|
2019-09-20 14:05:12 +03:00
|
|
|
#include "mozilla/dom/BrowserChild.h"
|
2019-09-20 23:51:25 +03:00
|
|
|
#include "mozilla/dom/HTMLCanvasElement.h"
|
|
|
|
#include "mozilla/dom/UserActivation.h"
|
2019-12-05 07:44:32 +03:00
|
|
|
#include "mozilla/BasePrincipal.h"
|
2019-07-26 04:10:23 +03:00
|
|
|
#include "mozilla/StaticPrefs_privacy.h"
|
2020-01-09 01:19:16 +03:00
|
|
|
#include "mozilla/StaticPrefs_webgl.h"
|
2009-06-26 00:30:56 +04:00
|
|
|
#include "nsIPrincipal.h"
|
|
|
|
|
|
|
|
#include "nsGfxCIID.h"
|
|
|
|
|
|
|
|
#include "nsTArray.h"
|
|
|
|
|
|
|
|
#include "CanvasUtils.h"
|
2011-06-30 01:34:58 +04:00
|
|
|
#include "mozilla/gfx/Matrix.h"
|
2015-10-12 06:21:03 +03:00
|
|
|
#include "WebGL2Context.h"
|
2009-06-26 00:30:56 +04:00
|
|
|
|
2019-05-02 00:51:41 +03:00
|
|
|
#include "nsIScriptError.h"
|
2017-08-22 09:23:41 +03:00
|
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
|
|
#include "nsIPermissionManager.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
#include "mozilla/Services.h"
|
|
|
|
#include "mozIThirdPartyUtil.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nsPrintfCString.h"
|
|
|
|
#include "jsapi.h"
|
|
|
|
|
|
|
|
#define TOPIC_CANVAS_PERMISSIONS_PROMPT "canvas-permissions-prompt"
|
2018-12-17 00:30:59 +03:00
|
|
|
#define TOPIC_CANVAS_PERMISSIONS_PROMPT_HIDE_DOORHANGER \
|
|
|
|
"canvas-permissions-prompt-hide-doorhanger"
|
2019-02-22 01:54:28 +03:00
|
|
|
#define PERMISSION_CANVAS_EXTRACT_DATA NS_LITERAL_CSTRING("canvas")
|
2017-08-22 09:23:41 +03:00
|
|
|
|
2012-10-27 21:24:04 +04:00
|
|
|
using namespace mozilla::gfx;
|
|
|
|
|
2011-06-30 01:34:58 +04:00
|
|
|
namespace mozilla {
|
|
|
|
namespace CanvasUtils {
|
2009-06-26 00:30:56 +04:00
|
|
|
|
2019-01-02 16:05:23 +03:00
|
|
|
bool IsImageExtractionAllowed(Document* aDocument, JSContext* aCx,
|
2018-05-14 21:49:32 +03:00
|
|
|
nsIPrincipal& aPrincipal) {
|
2017-08-22 09:23:41 +03:00
|
|
|
// Do the rest of the checks only if privacy.resistFingerprinting is on.
|
2019-03-06 02:20:08 +03:00
|
|
|
if (!nsContentUtils::ShouldResistFingerprinting(aDocument)) {
|
2017-08-22 09:23:41 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't proceed if we don't have a document or JavaScript context.
|
|
|
|
if (!aDocument || !aCx) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-14 21:49:32 +03:00
|
|
|
// The system principal can always extract canvas data.
|
2019-12-05 07:44:32 +03:00
|
|
|
if (aPrincipal.IsSystemPrincipal()) {
|
2018-05-14 21:49:32 +03:00
|
|
|
return true;
|
2017-08-22 09:23:41 +03:00
|
|
|
}
|
|
|
|
|
2018-05-14 21:49:32 +03:00
|
|
|
// Allow extension principals.
|
|
|
|
auto principal = BasePrincipal::Cast(&aPrincipal);
|
|
|
|
if (principal->AddonPolicy() || principal->ContentScriptAddonPolicy()) {
|
2017-08-22 09:23:41 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the document URI and its spec.
|
|
|
|
nsIURI* docURI = aDocument->GetDocumentURI();
|
|
|
|
nsCString docURISpec;
|
|
|
|
docURI->GetSpec(docURISpec);
|
|
|
|
|
|
|
|
// Allow local files to extract canvas data.
|
2019-08-02 11:54:18 +03:00
|
|
|
if (docURI->SchemeIs("file")) {
|
2017-08-22 09:23:41 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-05-02 00:54:43 +03:00
|
|
|
// Don't show canvas prompt for PDF.js
|
2017-08-22 09:23:41 +03:00
|
|
|
JS::AutoFilename scriptFile;
|
2019-05-02 00:54:43 +03:00
|
|
|
if (JS::DescribeScriptedCaller(aCx, &scriptFile) && scriptFile.get() &&
|
|
|
|
strcmp(scriptFile.get(), "resource://pdf.js/build/pdf.js") == 0) {
|
|
|
|
return true;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2017-08-22 09:23:41 +03:00
|
|
|
|
2019-01-02 16:05:23 +03:00
|
|
|
Document* topLevelDocument = aDocument->GetTopLevelContentDocument();
|
2017-08-22 09:23:41 +03:00
|
|
|
nsIURI* topLevelDocURI =
|
|
|
|
topLevelDocument ? topLevelDocument->GetDocumentURI() : nullptr;
|
|
|
|
nsCString topLevelDocURISpec;
|
|
|
|
if (topLevelDocURI) {
|
|
|
|
topLevelDocURI->GetSpec(topLevelDocURISpec);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load Third Party Util service.
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
|
|
|
|
do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2017-08-22 09:23:41 +03:00
|
|
|
// Block all third-party attempts to extract canvas.
|
|
|
|
bool isThirdParty = true;
|
|
|
|
rv = thirdPartyUtil->IsThirdPartyURI(topLevelDocURI, docURI, &isThirdParty);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
if (isThirdParty) {
|
2019-05-02 00:51:41 +03:00
|
|
|
nsAutoString message;
|
2019-05-02 00:54:43 +03:00
|
|
|
message.AppendPrintf("Blocked third party %s from extracting canvas data.",
|
|
|
|
docURISpec.get());
|
2019-05-02 00:51:41 +03:00
|
|
|
nsContentUtils::ReportToConsoleNonLocalized(
|
|
|
|
message, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security"),
|
|
|
|
aDocument);
|
2017-08-22 09:23:41 +03:00
|
|
|
return false;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2017-08-22 09:23:41 +03:00
|
|
|
|
|
|
|
// Load Permission Manager service.
|
|
|
|
nsCOMPtr<nsIPermissionManager> permissionManager =
|
|
|
|
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2017-08-22 09:23:41 +03:00
|
|
|
// Check if the site has permission to extract canvas data.
|
|
|
|
// Either permit or block extraction if a stored permission setting exists.
|
|
|
|
uint32_t permission;
|
2019-05-17 16:23:04 +03:00
|
|
|
rv = permissionManager->TestPermissionFromPrincipal(
|
|
|
|
principal, PERMISSION_CANVAS_EXTRACT_DATA, &permission);
|
2017-08-22 09:23:41 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
switch (permission) {
|
|
|
|
case nsIPermissionManager::ALLOW_ACTION:
|
|
|
|
return true;
|
|
|
|
case nsIPermissionManager::DENY_ACTION:
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
break;
|
2018-02-17 22:53:05 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// At this point, permission is unknown
|
2017-08-22 09:23:41 +03:00
|
|
|
// (nsIPermissionManager::UNKNOWN_ACTION).
|
2018-02-17 22:53:05 +03:00
|
|
|
|
|
|
|
// Check if the request is in response to user input
|
2018-12-17 00:30:59 +03:00
|
|
|
bool isAutoBlockCanvas =
|
|
|
|
StaticPrefs::
|
2018-02-17 22:53:05 +03:00
|
|
|
privacy_resistFingerprinting_autoDeclineNoUserInputCanvasPrompts() &&
|
2019-09-20 23:51:25 +03:00
|
|
|
!UserActivation::IsHandlingUserInput();
|
2018-12-17 00:30:59 +03:00
|
|
|
|
|
|
|
if (isAutoBlockCanvas) {
|
2019-05-02 00:51:41 +03:00
|
|
|
nsAutoString message;
|
2018-02-17 22:53:05 +03:00
|
|
|
message.AppendPrintf(
|
2019-05-02 00:54:43 +03:00
|
|
|
"Blocked %s from extracting canvas data because no user input was "
|
|
|
|
"detected.",
|
|
|
|
docURISpec.get());
|
2019-05-02 00:51:41 +03:00
|
|
|
nsContentUtils::ReportToConsoleNonLocalized(
|
|
|
|
message, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security"),
|
|
|
|
aDocument);
|
2018-12-17 00:30:59 +03:00
|
|
|
} else {
|
|
|
|
// It was in response to user input, so log and display the prompt.
|
2019-05-02 00:51:41 +03:00
|
|
|
nsAutoString message;
|
2018-12-17 00:30:59 +03:00
|
|
|
message.AppendPrintf(
|
2019-05-02 00:54:43 +03:00
|
|
|
"Blocked %s from extracting canvas data, but prompting the user.",
|
|
|
|
docURISpec.get());
|
2019-05-02 00:51:41 +03:00
|
|
|
nsContentUtils::ReportToConsoleNonLocalized(
|
|
|
|
message, nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Security"),
|
|
|
|
aDocument);
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2017-08-22 09:23:41 +03:00
|
|
|
// Prompt the user (asynchronous).
|
2018-05-22 21:46:01 +03:00
|
|
|
nsPIDOMWindowOuter* win = aDocument->GetWindow();
|
2019-05-17 16:23:04 +03:00
|
|
|
nsAutoCString origin;
|
|
|
|
rv = principal->GetOrigin(origin);
|
|
|
|
NS_ENSURE_SUCCESS(rv, false);
|
|
|
|
|
2017-08-22 09:23:41 +03:00
|
|
|
if (XRE_IsContentProcess()) {
|
2019-04-10 01:39:01 +03:00
|
|
|
BrowserChild* browserChild = BrowserChild::GetFrom(win);
|
|
|
|
if (browserChild) {
|
2019-06-04 09:32:37 +03:00
|
|
|
browserChild->SendShowCanvasPermissionPrompt(origin, isAutoBlockCanvas);
|
2017-08-22 09:23:41 +03:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
|
|
if (obs) {
|
2018-12-17 00:30:59 +03:00
|
|
|
obs->NotifyObservers(win,
|
|
|
|
isAutoBlockCanvas
|
|
|
|
? TOPIC_CANVAS_PERMISSIONS_PROMPT_HIDE_DOORHANGER
|
|
|
|
: TOPIC_CANVAS_PERMISSIONS_PROMPT,
|
2019-05-17 16:23:04 +03:00
|
|
|
NS_ConvertUTF8toUTF16(origin).get());
|
2017-08-22 09:23:41 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2017-08-22 09:23:41 +03:00
|
|
|
|
|
|
|
// We don't extract the image for now -- user may override at prompt.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-10-12 06:21:03 +03:00
|
|
|
bool GetCanvasContextType(const nsAString& str,
|
|
|
|
dom::CanvasContextType* const out_type) {
|
|
|
|
if (str.EqualsLiteral("2d")) {
|
|
|
|
*out_type = dom::CanvasContextType::Canvas2D;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-22 02:10:30 +03:00
|
|
|
if (str.EqualsLiteral("webgl") || str.EqualsLiteral("experimental-webgl")) {
|
2015-10-12 06:21:03 +03:00
|
|
|
*out_type = dom::CanvasContextType::WebGL1;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-01-09 01:19:16 +03:00
|
|
|
if (StaticPrefs::webgl_enable_webgl2()) {
|
2015-10-12 06:21:03 +03:00
|
|
|
if (str.EqualsLiteral("webgl2")) {
|
|
|
|
*out_type = dom::CanvasContextType::WebGL2;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-07 01:29:18 +03:00
|
|
|
if (StaticPrefs::dom_webgpu_enabled()) {
|
|
|
|
if (str.EqualsLiteral("gpupresent")) {
|
|
|
|
*out_type = dom::CanvasContextType::WebGPU;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-12-18 09:52:17 +03:00
|
|
|
if (str.EqualsLiteral("bitmaprenderer")) {
|
|
|
|
*out_type = dom::CanvasContextType::ImageBitmap;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-10-12 06:21:03 +03:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-30 21:49:00 +03:00
|
|
|
/**
|
|
|
|
* This security check utility might be called from an source that never taints
|
|
|
|
* others. For example, while painting a CanvasPattern, which is created from an
|
|
|
|
* ImageBitmap, onto a canvas. In this case, the caller could set the CORSUsed
|
|
|
|
* true in order to pass this check and leave the aPrincipal to be a nullptr
|
|
|
|
* since the aPrincipal is not going to be used.
|
|
|
|
*/
|
2013-01-04 09:16:14 +04:00
|
|
|
void DoDrawImageSecurityCheck(dom::HTMLCanvasElement* aCanvasElement,
|
2011-06-30 01:34:58 +04:00
|
|
|
nsIPrincipal* aPrincipal, bool forceWriteOnly,
|
2011-09-29 10:19:26 +04:00
|
|
|
bool CORSUsed) {
|
2009-06-26 00:30:56 +04:00
|
|
|
// Callers should ensure that mCanvasElement is non-null before calling this
|
|
|
|
if (!aCanvasElement) {
|
|
|
|
NS_WARNING("DoDrawImageSecurityCheck called without canvas element!");
|
|
|
|
return;
|
2018-09-26 21:29:36 +03:00
|
|
|
}
|
2009-06-26 00:30:56 +04:00
|
|
|
|
|
|
|
if (aCanvasElement->IsWriteOnly() && !aCanvasElement->mExpandedReader) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-09-10 01:58:35 +04:00
|
|
|
// If we explicitly set WriteOnly just do it and get out
|
|
|
|
if (forceWriteOnly) {
|
|
|
|
aCanvasElement->SetWriteOnly();
|
|
|
|
return;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2011-09-10 01:58:35 +04:00
|
|
|
|
2018-04-28 22:50:58 +03:00
|
|
|
// No need to do a security check if the image used CORS for the load
|
|
|
|
if (CORSUsed) return;
|
2015-07-30 21:49:00 +03:00
|
|
|
|
2014-02-14 06:57:36 +04:00
|
|
|
MOZ_ASSERT(aPrincipal, "Must have a principal here");
|
2009-06-26 00:30:56 +04:00
|
|
|
|
2018-09-26 21:29:36 +03:00
|
|
|
if (aCanvasElement->NodePrincipal()->Subsumes(aPrincipal)) {
|
|
|
|
// This canvas has access to that image anyway
|
|
|
|
return;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-09-26 21:29:36 +03:00
|
|
|
|
|
|
|
if (BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
|
|
|
|
// This is a resource from an extension content script principal.
|
|
|
|
|
|
|
|
if (aCanvasElement->mExpandedReader &&
|
|
|
|
aCanvasElement->mExpandedReader->Subsumes(aPrincipal)) {
|
|
|
|
// This canvas already allows reading from this principal.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aCanvasElement->mExpandedReader) {
|
|
|
|
// Allow future reads from this same princial only.
|
|
|
|
aCanvasElement->SetWriteOnly(aPrincipal);
|
2018-11-30 13:46:48 +03:00
|
|
|
return;
|
2018-09-26 21:29:36 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// If we got here, this must be the *second* extension tainting
|
|
|
|
// the canvas. Fall through to mark it WriteOnly for everyone.
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
|
2009-06-26 00:30:56 +04:00
|
|
|
aCanvasElement->SetWriteOnly();
|
|
|
|
}
|
2009-09-03 04:47:49 +04:00
|
|
|
|
2016-09-11 12:15:24 +03:00
|
|
|
bool CoerceDouble(const JS::Value& v, double* d) {
|
2014-04-28 06:35:40 +04:00
|
|
|
if (v.isDouble()) {
|
2014-04-28 06:38:31 +04:00
|
|
|
*d = v.toDouble();
|
2014-04-28 06:47:02 +04:00
|
|
|
} else if (v.isInt32()) {
|
2014-04-28 06:55:08 +04:00
|
|
|
*d = double(v.toInt32());
|
2014-04-28 06:32:05 +04:00
|
|
|
} else if (v.isUndefined()) {
|
2011-06-30 01:34:58 +04:00
|
|
|
*d = 0.0;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-02-01 23:47:59 +03:00
|
|
|
bool HasDrawWindowPrivilege(JSContext* aCx, JSObject* /* unused */) {
|
2017-09-04 04:51:02 +03:00
|
|
|
return nsContentUtils::CallerHasPermission(aCx,
|
|
|
|
nsGkAtoms::all_urlsPermission);
|
2017-02-01 23:47:59 +03:00
|
|
|
}
|
|
|
|
|
2019-06-04 09:32:37 +03:00
|
|
|
bool CheckWriteOnlySecurity(bool aCORSUsed, nsIPrincipal* aPrincipal,
|
|
|
|
bool aHadCrossOriginRedirects) {
|
2019-04-16 09:58:29 +03:00
|
|
|
if (!aPrincipal) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aCORSUsed) {
|
2019-06-04 09:32:37 +03:00
|
|
|
if (aHadCrossOriginRedirects) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-16 09:58:29 +03:00
|
|
|
nsIGlobalObject* incumbentSettingsObject = dom::GetIncumbentGlobal();
|
2019-06-07 13:35:42 +03:00
|
|
|
if (!incumbentSettingsObject) {
|
2019-04-16 09:58:29 +03:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIPrincipal* principal = incumbentSettingsObject->PrincipalOrNull();
|
|
|
|
if (NS_WARN_IF(!principal) || !(principal->Subsumes(aPrincipal))) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-06-30 01:34:58 +04:00
|
|
|
} // namespace CanvasUtils
|
|
|
|
} // namespace mozilla
|