mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 20:30:41 +00:00
Bug 1621433 - In RFP mode, turn canvas image extraction into a random 'poison pill' for fingerprinters r=tjr,jrmuizel
In RFP mode, canvas image extraction leads to an all-white image, replace that with a random (sample 32 bytes of randomness and fill the buffer with that) 'poison pill'. This helps defeat naive fingerprinters by producing a random image on every try. This feature is toggled using a new, default on, pref `privacy.resistFingerprinting.randomDataOnCanvasExtract`. Updated `browser_canvas_fingerprinting_resistance.js` to test this new feature as well. Updates and replaces D66308. Differential Revision: https://phabricator.services.mozilla.com/D72716
This commit is contained in:
parent
2271eb5b20
commit
d66dab82a7
@ -3,6 +3,9 @@
|
||||
* required for canvas data extraction.
|
||||
* This tests whether the site permission prompt for canvas data extraction
|
||||
* works properly.
|
||||
* When "privacy.resistFingerprinting.randomDataOnCanvasExtract" is true,
|
||||
* canvas data extraction results in random data, and when it is false, canvas
|
||||
* data extraction results in all-white data.
|
||||
*/
|
||||
"use strict";
|
||||
|
||||
@ -48,6 +51,12 @@ function initTab() {
|
||||
let placeholder = drawCanvas("white");
|
||||
contentWindow.kPlaceholderData = placeholder.toDataURL();
|
||||
let canvas = drawCanvas("cyan", "canvas-id-canvas");
|
||||
contentWindow.kPlacedData = canvas.toDataURL();
|
||||
is(
|
||||
canvas.toDataURL(),
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = false, canvas data == placed data"
|
||||
);
|
||||
isnot(
|
||||
canvas.toDataURL(),
|
||||
contentWindow.kPlaceholderData,
|
||||
@ -55,10 +64,17 @@ function initTab() {
|
||||
);
|
||||
}
|
||||
|
||||
function enableResistFingerprinting(autoDeclineNoInput) {
|
||||
function enableResistFingerprinting(
|
||||
randomDataOnCanvasExtract,
|
||||
autoDeclineNoInput
|
||||
) {
|
||||
return SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["privacy.resistFingerprinting", true],
|
||||
[
|
||||
"privacy.resistFingerprinting.randomDataOnCanvasExtract",
|
||||
randomDataOnCanvasExtract,
|
||||
],
|
||||
[
|
||||
"privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts",
|
||||
autoDeclineNoInput,
|
||||
@ -75,28 +91,61 @@ function promisePopupHidden() {
|
||||
return BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
|
||||
}
|
||||
|
||||
function extractCanvasData(grantPermission) {
|
||||
function extractCanvasData(randomDataOnCanvasExtract, grantPermission) {
|
||||
let contentWindow = content.wrappedJSObject;
|
||||
let canvas = contentWindow.document.getElementById("canvas-id-canvas");
|
||||
let canvasData = canvas.toDataURL();
|
||||
if (grantPermission) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = true, permission granted, canvas data == placed data"
|
||||
);
|
||||
if (!randomDataOnCanvasExtract) {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission granted, canvas data != placeholderdata"
|
||||
);
|
||||
}
|
||||
} else if (grantPermission === false) {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true, permission granted, canvas data != placeholderdata"
|
||||
);
|
||||
} else if (grantPermission === false) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true, permission denied, canvas data == placeholderdata"
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = true, permission denied, canvas data != placed data"
|
||||
);
|
||||
if (!randomDataOnCanvasExtract) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission denied, canvas data == placeholderdata"
|
||||
);
|
||||
} else {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, permission denied, canvas data != placeholderdata"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
is(
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true, requesting permission, canvas data == placeholderdata"
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = true, requesting permission, canvas data != placed data"
|
||||
);
|
||||
if (!randomDataOnCanvasExtract) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, requesting permission, canvas data == placeholderdata"
|
||||
);
|
||||
} else {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, requesting permission, canvas data != placeholderdata"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,11 +167,19 @@ function testPermission() {
|
||||
return Services.perms.testPermissionFromPrincipal(kPrincipal, kPermission);
|
||||
}
|
||||
|
||||
async function withNewTabNoInput(grantPermission, browser) {
|
||||
async function withNewTabNoInput(
|
||||
randomDataOnCanvasExtract,
|
||||
grantPermission,
|
||||
browser
|
||||
) {
|
||||
await SpecialPowers.spawn(browser, [], initTab);
|
||||
await enableResistFingerprinting(false);
|
||||
await enableResistFingerprinting(randomDataOnCanvasExtract, false);
|
||||
let popupShown = promisePopupShown();
|
||||
await SpecialPowers.spawn(browser, [], extractCanvasData);
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[randomDataOnCanvasExtract],
|
||||
extractCanvasData
|
||||
);
|
||||
await popupShown;
|
||||
let popupHidden = promisePopupHidden();
|
||||
if (grantPermission) {
|
||||
@ -134,28 +191,34 @@ async function withNewTabNoInput(grantPermission, browser) {
|
||||
await popupHidden;
|
||||
is(testPermission(), Services.perms.DENY_ACTION, "permission denied");
|
||||
}
|
||||
await SpecialPowers.spawn(browser, [grantPermission], extractCanvasData);
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[randomDataOnCanvasExtract, grantPermission],
|
||||
extractCanvasData
|
||||
);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
async function doTestNoInput(grantPermission) {
|
||||
async function doTestNoInput(randomDataOnCanvasExtract, grantPermission) {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
kUrl,
|
||||
withNewTabNoInput.bind(null, grantPermission)
|
||||
withNewTabNoInput.bind(null, randomDataOnCanvasExtract, grantPermission)
|
||||
);
|
||||
Services.perms.removeFromPrincipal(kPrincipal, kPermission);
|
||||
}
|
||||
|
||||
// With auto-declining disabled (not the default)
|
||||
// Tests clicking "Don't Allow" button of the permission prompt.
|
||||
add_task(doTestNoInput.bind(null, false));
|
||||
add_task(doTestNoInput.bind(null, true, false));
|
||||
add_task(doTestNoInput.bind(null, false, false));
|
||||
|
||||
// Tests clicking "Allow" button of the permission prompt.
|
||||
add_task(doTestNoInput.bind(null, true));
|
||||
add_task(doTestNoInput.bind(null, true, true));
|
||||
add_task(doTestNoInput.bind(null, false, true));
|
||||
|
||||
async function withNewTabAutoBlockNoInput(browser) {
|
||||
async function withNewTabAutoBlockNoInput(randomDataOnCanvasExtract, browser) {
|
||||
await SpecialPowers.spawn(browser, [], initTab);
|
||||
await enableResistFingerprinting(true);
|
||||
await enableResistFingerprinting(randomDataOnCanvasExtract, true);
|
||||
|
||||
let noShowHandler = () => {
|
||||
ok(false, "The popup notification should not show in this case.");
|
||||
@ -169,7 +232,11 @@ async function withNewTabAutoBlockNoInput(browser) {
|
||||
);
|
||||
|
||||
// Try to extract canvas data without user inputs.
|
||||
await SpecialPowers.spawn(browser, [], extractCanvasData);
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[randomDataOnCanvasExtract],
|
||||
extractCanvasData
|
||||
);
|
||||
|
||||
await promisePopupObserver;
|
||||
info("There should be no popup shown on the panel.");
|
||||
@ -190,38 +257,84 @@ async function withNewTabAutoBlockNoInput(browser) {
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
add_task(async function doTestAutoBlockNoInput() {
|
||||
await BrowserTestUtils.withNewTab(kUrl, withNewTabAutoBlockNoInput);
|
||||
});
|
||||
async function doTestAutoBlockNoInput(randomDataOnCanvasExtract) {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
kUrl,
|
||||
withNewTabAutoBlockNoInput.bind(null, randomDataOnCanvasExtract)
|
||||
);
|
||||
}
|
||||
|
||||
function extractCanvasDataUserInput(grantPermission) {
|
||||
add_task(doTestAutoBlockNoInput.bind(null, true));
|
||||
add_task(doTestAutoBlockNoInput.bind(null, false));
|
||||
|
||||
function extractCanvasDataUserInput(
|
||||
randomDataOnCanvasExtract,
|
||||
grantPermission
|
||||
) {
|
||||
let contentWindow = content.wrappedJSObject;
|
||||
let canvas = contentWindow.document.getElementById("canvas-id-canvas");
|
||||
let canvasData = canvas.toDataURL();
|
||||
if (grantPermission) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = true, permission granted, canvas data == placed data"
|
||||
);
|
||||
if (!randomDataOnCanvasExtract) {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission granted, canvas data != placeholderdata"
|
||||
);
|
||||
}
|
||||
} else if (grantPermission === false) {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true, permission granted, canvas data != placeholderdata"
|
||||
);
|
||||
} else if (grantPermission === false) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true, permission denied, canvas data == placeholderdata"
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = true, permission denied, canvas data != placed data"
|
||||
);
|
||||
if (!randomDataOnCanvasExtract) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, permission denied, canvas data == placeholderdata"
|
||||
);
|
||||
} else {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, permission denied, canvas data != placeholderdata"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
is(
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true, requesting permission, canvas data == placeholderdata"
|
||||
contentWindow.kPlacedData,
|
||||
"privacy.resistFingerprinting = true, requesting permission, canvas data != placed data"
|
||||
);
|
||||
if (!randomDataOnCanvasExtract) {
|
||||
is(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = false, requesting permission, canvas data == placeholderdata"
|
||||
);
|
||||
} else {
|
||||
isnot(
|
||||
canvasData,
|
||||
contentWindow.kPlaceholderData,
|
||||
"privacy.resistFingerprinting = true and randomDataOnCanvasExtract = true, requesting permission, canvas data != placeholderdata"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function withNewTabInput(grantPermission, browser) {
|
||||
async function withNewTabInput(
|
||||
randomDataOnCanvasExtract,
|
||||
grantPermission,
|
||||
browser
|
||||
) {
|
||||
await SpecialPowers.spawn(browser, [], initTab);
|
||||
await enableResistFingerprinting(true);
|
||||
await enableResistFingerprinting(randomDataOnCanvasExtract, true);
|
||||
let popupShown = promisePopupShown();
|
||||
await SpecialPowers.spawn(browser, [], function(host) {
|
||||
E10SUtils.wrapHandlingUserInput(content, true, function() {
|
||||
@ -242,23 +355,29 @@ async function withNewTabInput(grantPermission, browser) {
|
||||
}
|
||||
await SpecialPowers.spawn(
|
||||
browser,
|
||||
[grantPermission],
|
||||
[randomDataOnCanvasExtract, grantPermission],
|
||||
extractCanvasDataUserInput
|
||||
);
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
async function doTestInput(grantPermission, autoDeclineNoInput) {
|
||||
async function doTestInput(
|
||||
randomDataOnCanvasExtract,
|
||||
grantPermission,
|
||||
autoDeclineNoInput
|
||||
) {
|
||||
await BrowserTestUtils.withNewTab(
|
||||
kUrl,
|
||||
withNewTabInput.bind(null, grantPermission)
|
||||
withNewTabInput.bind(null, randomDataOnCanvasExtract, grantPermission)
|
||||
);
|
||||
Services.perms.removeFromPrincipal(kPrincipal, kPermission);
|
||||
}
|
||||
|
||||
// With auto-declining enabled (the default)
|
||||
// Tests clicking "Don't Allow" button of the permission prompt.
|
||||
add_task(doTestInput.bind(null, false));
|
||||
add_task(doTestInput.bind(null, true, false));
|
||||
add_task(doTestInput.bind(null, false, false));
|
||||
|
||||
// Tests clicking "Allow" button of the permission prompt.
|
||||
add_task(doTestInput.bind(null, true));
|
||||
add_task(doTestInput.bind(null, true, true));
|
||||
add_task(doTestInput.bind(null, false, true));
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "ImageEncoder.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
|
||||
#include "mozilla/dom/MemoryBlobImpl.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
@ -383,8 +384,9 @@ nsresult ImageEncoder::ExtractDataInternal(
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
if (aUsePlaceholder) {
|
||||
// If placeholder data was requested, return all-white, opaque image data.
|
||||
memset(map.mData, 0xFF, 4 * aSize.width * aSize.height);
|
||||
auto size = 4 * aSize.width * aSize.height;
|
||||
auto* data = map.mData;
|
||||
GeneratePlaceholderCanvasData(size, data);
|
||||
}
|
||||
rv = aEncoder->InitFromData(map.mData, aSize.width * aSize.height * 4,
|
||||
aSize.width, aSize.height, aSize.width * 4,
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "mozilla/PresShellInlines.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
|
||||
#include "SVGObserverUtils.h"
|
||||
#include "nsPresContext.h"
|
||||
|
||||
@ -5095,21 +5096,28 @@ nsresult CanvasRenderingContext2D::GetImageDataArray(
|
||||
}
|
||||
|
||||
do {
|
||||
uint8_t* randomData;
|
||||
if (usePlaceholder) {
|
||||
// Since we cannot call any GC-able functions (like requesting the RNG
|
||||
// service) after we call JS_GetUint8ClampedArrayData, we will
|
||||
// pre-generate the randomness required for GeneratePlaceholderCanvasData.
|
||||
randomData = TryToGenerateRandomDataForPlaceholderCanvasData();
|
||||
}
|
||||
|
||||
JS::AutoCheckCannotGC nogc;
|
||||
bool isShared;
|
||||
uint8_t* data = JS_GetUint8ClampedArrayData(darray, &isShared, nogc);
|
||||
MOZ_ASSERT(!isShared); // Should not happen, data was created above
|
||||
|
||||
if (usePlaceholder) {
|
||||
FillPlaceholderCanvas(randomData, len.value(), data);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t srcStride = rawData.mStride;
|
||||
uint8_t* src =
|
||||
rawData.mData + srcReadRect.y * srcStride + srcReadRect.x * 4;
|
||||
|
||||
// Return all-white, opaque pixel data if no permission.
|
||||
if (usePlaceholder) {
|
||||
memset(data, 0xFF, len.value());
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t* dst = data + dstWriteRect.y * (aWidth * 4) + dstWriteRect.x * 4;
|
||||
|
||||
if (mOpaque) {
|
||||
|
99
dom/canvas/GeneratePlaceholderCanvasData.h
Normal file
99
dom/canvas/GeneratePlaceholderCanvasData.h
Normal file
@ -0,0 +1,99 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_dom_GeneratePlaceholderCanvasData_h
|
||||
#define mozilla_dom_GeneratePlaceholderCanvasData_h
|
||||
|
||||
#include "mozilla/StaticPrefs_privacy.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIRandomGenerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#define RANDOM_BYTES_TO_SAMPLE 32
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
/**
|
||||
* When privacy.resistFingerprinting.randomDataOnCanvasExtract is true, tries
|
||||
* to generate random data for placeholder canvas by sampling
|
||||
* RANDOM_BYTES_TO_SAMPLE bytes and then returning it. If this fails or if
|
||||
* privacy.resistFingerprinting.randomDataOnCanvasExtract is false, returns
|
||||
* nullptr.
|
||||
*
|
||||
* @return uint8_t* output buffer
|
||||
*/
|
||||
inline uint8_t* TryToGenerateRandomDataForPlaceholderCanvasData() {
|
||||
if (!StaticPrefs::privacy_resistFingerprinting_randomDataOnCanvasExtract()) {
|
||||
return nullptr;
|
||||
}
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIRandomGenerator> rg =
|
||||
do_GetService("@mozilla.org/security/random-generator;1", &rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
uint8_t* randomData;
|
||||
rv = rg->GenerateRandomBytes(RANDOM_BYTES_TO_SAMPLE, &randomData);
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
return randomData;
|
||||
}
|
||||
|
||||
/**
|
||||
* If randomData not nullptr, repeats those bytes many times to fill buffer. If
|
||||
* randomData is nullptr, returns all-white pixel data.
|
||||
*
|
||||
* @param[in] randomData Buffer of RANDOM_BYTES_TO_SAMPLE bytes of random
|
||||
* data, or nullptr
|
||||
* @param[in] size Size of output buffer
|
||||
* @param[out] buffer Output buffer
|
||||
*/
|
||||
inline void FillPlaceholderCanvas(uint8_t* randomData, uint32_t size,
|
||||
uint8_t* buffer) {
|
||||
if (!randomData) {
|
||||
memset(buffer, 0xFF, size);
|
||||
return;
|
||||
}
|
||||
auto remaining_to_fill = size;
|
||||
auto index = 0;
|
||||
while (remaining_to_fill > 0) {
|
||||
auto bytes_to_write = (remaining_to_fill > RANDOM_BYTES_TO_SAMPLE)
|
||||
? RANDOM_BYTES_TO_SAMPLE
|
||||
: remaining_to_fill;
|
||||
memcpy(buffer + (index * RANDOM_BYTES_TO_SAMPLE), randomData,
|
||||
bytes_to_write);
|
||||
remaining_to_fill -= bytes_to_write;
|
||||
index++;
|
||||
}
|
||||
free(randomData);
|
||||
}
|
||||
|
||||
/**
|
||||
* When privacy.resistFingerprinting.randomDataOnCanvasExtract is true, tries
|
||||
* to generate random canvas data by sampling RANDOM_BYTES_TO_SAMPLE bytes and
|
||||
* then repeating those bytes many times to fill the buffer. If this fails or
|
||||
* if privacy.resistFingerprinting.randomDataOnCanvasExtract is false, returns
|
||||
* all-white, opaque pixel data.
|
||||
*
|
||||
* It is recommended that callers call this function instead of individually
|
||||
* calling TryToGenerateRandomDataForPlaceholderCanvasData and
|
||||
* FillPlaceholderCanvas unless there are requirements, like NoGC, that prevent
|
||||
* them from calling the RNG service inside this function.
|
||||
*
|
||||
* @param[in] size Size of output buffer
|
||||
* @param[out] buffer Output buffer
|
||||
*/
|
||||
inline void GeneratePlaceholderCanvasData(uint32_t size, uint8_t* buffer) {
|
||||
uint8_t* randomData = TryToGenerateRandomDataForPlaceholderCanvasData();
|
||||
FillPlaceholderCanvas(randomData, size, buffer);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_GeneratePlaceholderCanvasData_h
|
@ -53,6 +53,7 @@ EXPORTS.mozilla.dom += [
|
||||
'CanvasRenderingContext2D.h',
|
||||
'CanvasRenderingContextHelper.h',
|
||||
'CanvasUtils.h',
|
||||
'GeneratePlaceholderCanvasData.h',
|
||||
'ImageBitmap.h',
|
||||
'ImageBitmapRenderingContext.h',
|
||||
'ImageBitmapSource.h',
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/dom/CanvasCaptureMediaStream.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/dom/GeneratePlaceholderCanvasData.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/HTMLCanvasElementBinding.h"
|
||||
@ -100,8 +101,9 @@ class RequestedFrameRefreshObserver : public nsARefreshObserver {
|
||||
MOZ_ASSERT(data->GetFormat() == copy->GetFormat());
|
||||
|
||||
if (aReturnPlaceholderData) {
|
||||
// If returning placeholder data, fill the frame copy with white pixels.
|
||||
memset(write.GetData(), 0xFF, write.GetStride() * copy->GetSize().height);
|
||||
auto size = write.GetStride() * copy->GetSize().height;
|
||||
auto* data = write.GetData();
|
||||
GeneratePlaceholderCanvasData(size, data);
|
||||
} else {
|
||||
memcpy(write.GetData(), read.GetData(),
|
||||
write.GetStride() * copy->GetSize().height);
|
||||
|
@ -8176,6 +8176,13 @@
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# Whether canvas extraction should result in random data. If false, canvas
|
||||
# extraction results in all-white, opaque pixel data.
|
||||
- name: privacy.resistFingerprinting.randomDataOnCanvasExtract
|
||||
type: bool
|
||||
value: true
|
||||
mirror: always
|
||||
|
||||
# The log level for browser console messages logged in RFPHelper.jsm. Change to
|
||||
# 'All' and restart to see the messages.
|
||||
- name: privacy.resistFingerprinting.jsmloglevel
|
||||
|
Loading…
x
Reference in New Issue
Block a user