Bug 1901060 - Privacy Preserving Attribution origin trial and API. r=bbirdsong,mt,webidl,smaug

Differential Revision: https://phabricator.services.mozilla.com/D211093
This commit is contained in:
Emilio Cobos Álvarez 2024-06-06 16:43:41 +00:00
parent c650eff9ee
commit c9f45e36b2
22 changed files with 416 additions and 2 deletions

View file

@ -21,6 +21,7 @@
#include "nsIClassOfService.h" #include "nsIClassOfService.h"
#include "nsIHttpProtocolHandler.h" #include "nsIHttpProtocolHandler.h"
#include "nsIContentPolicy.h" #include "nsIContentPolicy.h"
#include "nsIPrivateAttributionService.h"
#include "nsContentPolicyUtils.h" #include "nsContentPolicyUtils.h"
#include "nsISupportsPriority.h" #include "nsISupportsPriority.h"
#include "nsIWebProtocolHandlerRegistrar.h" #include "nsIWebProtocolHandlerRegistrar.h"
@ -41,11 +42,13 @@
#include "BatteryManager.h" #include "BatteryManager.h"
#include "mozilla/dom/CredentialsContainer.h" #include "mozilla/dom/CredentialsContainer.h"
#include "mozilla/dom/Clipboard.h" #include "mozilla/dom/Clipboard.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/FeaturePolicyUtils.h" #include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/GamepadServiceTest.h" #include "mozilla/dom/GamepadServiceTest.h"
#include "mozilla/dom/MediaCapabilities.h" #include "mozilla/dom/MediaCapabilities.h"
#include "mozilla/dom/MediaSession.h" #include "mozilla/dom/MediaSession.h"
#include "mozilla/dom/power/PowerManagerService.h" #include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/PrivateAttribution.h"
#include "mozilla/dom/LockManager.h" #include "mozilla/dom/LockManager.h"
#include "mozilla/dom/MIDIAccessManager.h" #include "mozilla/dom/MIDIAccessManager.h"
#include "mozilla/dom/MIDIOptionsBinding.h" #include "mozilla/dom/MIDIOptionsBinding.h"
@ -161,6 +164,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddonManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddonManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrivateAttribution)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserActivation) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserActivation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock)
@ -250,6 +254,8 @@ void Navigator::Invalidate() {
mLocks = nullptr; mLocks = nullptr;
} }
mPrivateAttribution = nullptr;
mUserActivation = nullptr; mUserActivation = nullptr;
mSharePromise = nullptr; mSharePromise = nullptr;
@ -2273,6 +2279,13 @@ dom::LockManager* Navigator::Locks() {
return mLocks; return mLocks;
} }
dom::PrivateAttribution* Navigator::PrivateAttribution() {
if (!mPrivateAttribution) {
mPrivateAttribution = new dom::PrivateAttribution(GetWindow()->AsGlobal());
}
return mPrivateAttribution;
}
/* static */ /* static */
bool Navigator::Webdriver() { bool Navigator::Webdriver() {
#ifdef ENABLE_WEBDRIVER #ifdef ENABLE_WEBDRIVER

View file

@ -42,6 +42,7 @@ class ServiceWorkerContainer;
class CredentialsContainer; class CredentialsContainer;
class Clipboard; class Clipboard;
class LockManager; class LockManager;
class PrivateAttribution;
class HTMLMediaElement; class HTMLMediaElement;
class AudioContext; class AudioContext;
class WakeLockJS; class WakeLockJS;
@ -209,6 +210,7 @@ class Navigator final : public nsISupports, public nsWrapperCache {
dom::Clipboard* Clipboard(); dom::Clipboard* Clipboard();
webgpu::Instance* Gpu(); webgpu::Instance* Gpu();
dom::LockManager* Locks(); dom::LockManager* Locks();
dom::PrivateAttribution* PrivateAttribution();
static bool Webdriver(); static bool Webdriver();
@ -300,7 +302,8 @@ class Navigator final : public nsISupports, public nsWrapperCache {
RefPtr<AddonManager> mAddonManager; RefPtr<AddonManager> mAddonManager;
RefPtr<webgpu::Instance> mWebGpu; RefPtr<webgpu::Instance> mWebGpu;
RefPtr<Promise> mSharePromise; // Web Share API related RefPtr<Promise> mSharePromise; // Web Share API related
RefPtr<dom::LockManager> mLocks; RefPtr<LockManager> mLocks;
RefPtr<dom::PrivateAttribution> mPrivateAttribution;
RefPtr<dom::UserActivation> mUserActivation; RefPtr<dom::UserActivation> mUserActivation;
RefPtr<dom::WakeLockJS> mWakeLock; RefPtr<dom::WakeLockJS> mWakeLock;
}; };

View file

@ -17,9 +17,11 @@
#include "chrome/common/process_watcher.h" #include "chrome/common/process_watcher.h"
#include "mozilla/Result.h" #include "mozilla/Result.h"
#include "mozilla/Services.h"
#include "mozilla/XREAppData.h" #include "mozilla/XREAppData.h"
#include "nsComponentManagerUtils.h" #include "nsComponentManagerUtils.h"
#include "nsIBrowserDOMWindow.h" #include "nsIBrowserDOMWindow.h"
#include "nsIPrivateAttributionService.h"
#include "GMPServiceParent.h" #include "GMPServiceParent.h"
#include "HandlerServiceParent.h" #include "HandlerServiceParent.h"
@ -1244,6 +1246,35 @@ mozilla::ipc::IPCResult ContentParent::RecvCreateGMPService() {
return IPC_OK(); return IPC_OK();
} }
IPCResult ContentParent::RecvAttributionEvent(
const nsACString& aHost, PrivateAttributionImpressionType aType,
uint32_t aIndex, const nsAString& aAd, const nsACString& aTargetHost) {
nsCOMPtr<nsIPrivateAttributionService> pa =
components::PrivateAttribution::Service();
if (NS_WARN_IF(!pa)) {
return IPC_OK();
}
pa->OnAttributionEvent(aHost, GetEnumString(aType), aIndex, aAd, aTargetHost);
return IPC_OK();
}
IPCResult ContentParent::RecvAttributionConversion(
const nsACString& aHost, const nsAString& aTask, uint32_t aHistogramSize,
const Maybe<uint32_t>& aLoopbackDays,
const Maybe<PrivateAttributionImpressionType>& aImpressionType,
const nsTArray<nsString>& aAds, const nsTArray<nsCString>& aSourceHosts) {
nsCOMPtr<nsIPrivateAttributionService> pa =
components::PrivateAttribution::Service();
if (NS_WARN_IF(!pa)) {
return IPC_OK();
}
pa->OnAttributionConversion(
aHost, aTask, aHistogramSize, aLoopbackDays.valueOr(0),
aImpressionType ? GetEnumString(*aImpressionType) : EmptyCString(), aAds,
aSourceHosts);
return IPC_OK();
}
Atomic<bool, mozilla::Relaxed> sContentParentTelemetryEventEnabled(false); Atomic<bool, mozilla::Relaxed> sContentParentTelemetryEventEnabled(false);
/*static*/ /*static*/

View file

@ -443,6 +443,15 @@ class ContentParent final : public PContentParent,
return PContentParent::RecvPHalConstructor(aActor); return PContentParent::RecvPHalConstructor(aActor);
} }
mozilla::ipc::IPCResult RecvAttributionEvent(
const nsACString& aHost, PrivateAttributionImpressionType aType,
uint32_t aIndex, const nsAString& aAd, const nsACString& aTargetHost);
mozilla::ipc::IPCResult RecvAttributionConversion(
const nsACString& aHost, const nsAString& aTask, uint32_t aHistogramSize,
const Maybe<uint32_t>& aLoopbackDays,
const Maybe<PrivateAttributionImpressionType>& aImpressionType,
const nsTArray<nsString>& aAds, const nsTArray<nsCString>& aSourceHosts);
PHeapSnapshotTempFileHelperParent* AllocPHeapSnapshotTempFileHelperParent(); PHeapSnapshotTempFileHelperParent* AllocPHeapSnapshotTempFileHelperParent();
PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent(); PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent();

View file

@ -160,6 +160,7 @@ using mozilla::PerfStats::MetricMask from "mozilla/PerfStats.h";
[RefCounted] using class nsIX509Cert from "nsIX509Cert.h"; [RefCounted] using class nsIX509Cert from "nsIX509Cert.h";
using nsIDNSService::ResolverMode from "nsIDNSService.h"; using nsIDNSService::ResolverMode from "nsIDNSService.h";
using mozilla::dom::UserActivation::Modifiers from "mozilla/dom/UserActivation.h"; using mozilla::dom::UserActivation::Modifiers from "mozilla/dom/UserActivation.h";
using mozilla::dom::PrivateAttributionImpressionType from "mozilla/dom/PrivateAttributionIPCUtils.h";
union ChromeRegistryItem union ChromeRegistryItem
{ {
@ -1970,6 +1971,17 @@ parent:
// test document before taking the snapshot and starting e.g. IPC fuzzing. // test document before taking the snapshot and starting e.g. IPC fuzzing.
async SignalFuzzingReady(); async SignalFuzzingReady();
#endif #endif
async AttributionEvent(nsCString aSourceHost,
PrivateAttributionImpressionType aType,
uint32_t aIndex, nsString aAd, nsCString aTargetHost);
async AttributionConversion(nsCString aSourceHost, nsString aTask,
uint32_t aHistogramSize,
uint32_t? aLoopbackDays,
PrivateAttributionImpressionType? aImpressionType,
nsString[] aAds,
nsCString[] aImpressionSourceHosts);
}; };
} }

View file

@ -74,6 +74,7 @@ DIRS += [
"audiochannel", "audiochannel",
"broadcastchannel", "broadcastchannel",
"messagechannel", "messagechannel",
"privateattribution",
"promise", "promise",
"smil", "smil",
"streams", "streams",

View file

@ -212,6 +212,8 @@ static int32_t PrefState(OriginTrial aTrial) {
return StaticPrefs::dom_origin_trials_test_trial_state(); return StaticPrefs::dom_origin_trials_test_trial_state();
case OriginTrial::CoepCredentialless: case OriginTrial::CoepCredentialless:
return StaticPrefs::dom_origin_trials_coep_credentialless_state(); return StaticPrefs::dom_origin_trials_coep_credentialless_state();
case OriginTrial::PrivateAttribution:
return StaticPrefs::dom_origin_trials_private_attribution_state();
case OriginTrial::MAX: case OriginTrial::MAX:
MOZ_ASSERT_UNREACHABLE("Unknown trial!"); MOZ_ASSERT_UNREACHABLE("Unknown trial!");
break; break;

View file

@ -10,6 +10,7 @@ pub enum OriginTrial {
// NOTE(emilio): 0 is reserved for WebIDL usage. // NOTE(emilio): 0 is reserved for WebIDL usage.
TestTrial = 1, TestTrial = 1,
CoepCredentialless = 2, CoepCredentialless = 2,
PrivateAttribution = 3,
MAX, MAX,
} }
@ -19,6 +20,7 @@ impl OriginTrial {
Some(match s { Some(match s {
"TestTrial" => Self::TestTrial, "TestTrial" => Self::TestTrial,
"CoepCredentialless" => Self::CoepCredentialless, "CoepCredentialless" => Self::CoepCredentialless,
"PrivateAttribution" => Self::PrivateAttribution,
_ => return None, _ => return None,
}) })
} }

View file

@ -13,9 +13,11 @@ support-files = [
# verification fails, expectedly. # verification fails, expectedly.
skip-if = [ skip-if = [
"!debug", "!debug",
"xorigin", # AudioWorklet requires secure context "xorigin",
] ]
# We want to test that trials are exposed in worklets, and AudioWorklet
# requires secure context.
scheme = "https" scheme = "https"
["test_expired_token.html"] ["test_expired_token.html"]

View file

@ -3,11 +3,15 @@
<meta http-equiv="origin-trial" content="AyGdETIKWLLqe+chG57f74gZcjYSfbdYAapEq7DA49E6CmaYaPmaoXh/4tAe5XJJJdwwpFVal7hz/irC+Wvp1HgAAABLeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IlRlc3RUcmlhbCIsImV4cGlyeSI6MzI1MDM2ODAwMDB9"> <meta http-equiv="origin-trial" content="AyGdETIKWLLqe+chG57f74gZcjYSfbdYAapEq7DA49E6CmaYaPmaoXh/4tAe5XJJJdwwpFVal7hz/irC+Wvp1HgAAABLeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IlRlc3RUcmlhbCIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
<!-- Created with: mktoken --origin 'https://example.com' --feature CoepCredentialless --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 --> <!-- Created with: mktoken --origin 'https://example.com' --feature CoepCredentialless --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 -->
<meta http-equiv="origin-trial" content="Az+DK2Kczk8Xz1cAlD+TkvPZmuM2uJZ2CFefbp2hLuCU9FbUqxWTyQ2tEYr50r0syKELcOZLAPaABw8aYTLHn5YAAABUeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IkNvZXBDcmVkZW50aWFsbGVzcyIsImV4cGlyeSI6MzI1MDM2ODAwMDB9"> <meta http-equiv="origin-trial" content="Az+DK2Kczk8Xz1cAlD+TkvPZmuM2uJZ2CFefbp2hLuCU9FbUqxWTyQ2tEYr50r0syKELcOZLAPaABw8aYTLHn5YAAABUeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IkNvZXBDcmVkZW50aWFsbGVzcyIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
<!-- Created with: mktoken --origin 'https://example.com' --feature PrivateAttribution --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 -->
<meta http-equiv="origin-trial" content="A39jvTkkSoAueJadvVP6xeapNZVQLJgqbI6hwQc3kIYm0O96hc8WjpRwFVOnI+gN9zAQbQLEJEcibsRVAxQJ0bIAAABUeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IlByaXZhdGVBdHRyaWJ1dGlvbiIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
<script src="/tests/SimpleTest/SimpleTest.js"></script> <script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="common.js"></script> <script src="common.js"></script>
<script> <script>
assertTestTrialActive(true); assertTestTrialActive(true);
add_task(function() { add_task(function() {
ok(!!SpecialPowers.DOMWindowUtils.isCoepCredentialless(), "CoepCredentialless trial works."); ok(!!SpecialPowers.DOMWindowUtils.isCoepCredentialless(), "CoepCredentialless trial works.");
ok(navigator.privateAttribution, "PrivateAttribution trial works.");
ok('PrivateAttribution' in window, "PrivateAttribution trial works.");
}); });
</script> </script>

View file

@ -3,4 +3,8 @@
<script src="common.js"></script> <script src="common.js"></script>
<script> <script>
assertTestTrialActive(false); assertTestTrialActive(false);
add_task(function() {
ok(!navigator.privateAttribution, "PrivateAttribution trial disabled.");
ok(!('PrivateAttribution' in window), "PrivateAttribution trial disabled.");
});
</script> </script>

View file

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "PrivateAttribution.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/PrivateAttributionBinding.h"
#include "mozilla/Components.h"
#include "nsIGlobalObject.h"
#include "nsIPrivateAttributionService.h"
#include "nsXULAppAPI.h"
#include "nsURLHelper.h"
namespace mozilla::dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PrivateAttribution, mOwner)
PrivateAttribution::PrivateAttribution(nsIGlobalObject* aGlobal)
: mOwner(aGlobal) {
MOZ_ASSERT(aGlobal);
}
JSObject* PrivateAttribution::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return PrivateAttribution_Binding::Wrap(aCx, this, aGivenProto);
}
PrivateAttribution::~PrivateAttribution() = default;
bool PrivateAttribution::GetSourceHost(nsACString& aSourceHost,
ErrorResult& aRv) {
MOZ_ASSERT(mOwner);
nsIPrincipal* prin = mOwner->PrincipalOrNull();
if (!prin || NS_FAILED(prin->GetHost(aSourceHost))) {
aRv.ThrowInvalidStateError("Couldn't get source host");
return false;
}
return true;
}
[[nodiscard]] static bool ValidateHost(const nsACString& aHost,
ErrorResult& aRv) {
if (!net_IsValidHostName(aHost)) {
aRv.ThrowSyntaxError(aHost + " is not a valid host name"_ns);
return false;
}
return true;
}
void PrivateAttribution::SaveImpression(
const PrivateAttributionImpressionOptions& aOptions, ErrorResult& aRv) {
nsAutoCString source;
if (!GetSourceHost(source, aRv)) {
return;
}
if (!ValidateHost(aOptions.mTarget, aRv)) {
return;
}
if (XRE_IsParentProcess()) {
nsCOMPtr<nsIPrivateAttributionService> pa =
components::PrivateAttribution::Service();
if (NS_WARN_IF(!pa)) {
return;
}
pa->OnAttributionEvent(source, GetEnumString(aOptions.mType),
aOptions.mIndex, aOptions.mAd, aOptions.mTarget);
return;
}
auto* content = ContentChild::GetSingleton();
if (NS_WARN_IF(!content)) {
return;
}
content->SendAttributionEvent(source, aOptions.mType, aOptions.mIndex,
aOptions.mAd, aOptions.mTarget);
}
void PrivateAttribution::MeasureConversion(
const PrivateAttributionConversionOptions& aOptions, ErrorResult& aRv) {
nsAutoCString source;
if (!GetSourceHost(source, aRv)) {
return;
}
for (const nsACString& host : aOptions.mSources) {
if (!ValidateHost(host, aRv)) {
return;
}
}
if (XRE_IsParentProcess()) {
nsCOMPtr<nsIPrivateAttributionService> pa =
components::PrivateAttribution::Service();
if (NS_WARN_IF(!pa)) {
return;
}
pa->OnAttributionConversion(
source, aOptions.mTask, aOptions.mHistogramSize,
aOptions.mLookbackDays.WasPassed() ? aOptions.mLookbackDays.Value() : 0,
aOptions.mImpression.WasPassed()
? GetEnumString(aOptions.mImpression.Value())
: EmptyCString(),
aOptions.mAds, aOptions.mSources);
return;
}
auto* content = ContentChild::GetSingleton();
if (NS_WARN_IF(!content)) {
return;
}
content->SendAttributionConversion(
source, aOptions.mTask, aOptions.mHistogramSize,
aOptions.mLookbackDays.WasPassed() ? Some(aOptions.mLookbackDays.Value())
: Nothing(),
aOptions.mImpression.WasPassed() ? Some(aOptions.mImpression.Value())
: Nothing(),
aOptions.mAds, aOptions.mSources);
}
} // namespace mozilla::dom

View file

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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_PrivateAttribution_h
#define mozilla_dom_PrivateAttribution_h
#include "nsWrapperCache.h"
#include "nsCOMPtr.h"
class nsIGlobalObject;
namespace mozilla {
class ErrorResult;
}
namespace mozilla::dom {
struct PrivateAttributionImpressionOptions;
struct PrivateAttributionConversionOptions;
class PrivateAttribution final : public nsWrapperCache {
public:
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PrivateAttribution)
NS_DECL_CYCLE_COLLECTION_NATIVE_WRAPPERCACHE_CLASS(PrivateAttribution)
explicit PrivateAttribution(nsIGlobalObject* aGlobal);
static already_AddRefed<PrivateAttribution> Create(nsIGlobalObject& aGlobal);
nsIGlobalObject* GetParentObject() const { return mOwner; }
JSObject* WrapObject(JSContext*, JS::Handle<JSObject*> aGivenProto) override;
void SaveImpression(const PrivateAttributionImpressionOptions&, ErrorResult&);
void MeasureConversion(const PrivateAttributionConversionOptions&,
ErrorResult&);
private:
[[nodiscard]] bool GetSourceHost(nsACString&, ErrorResult&);
~PrivateAttribution();
nsCOMPtr<nsIGlobalObject> mOwner;
};
} // namespace mozilla::dom
#endif // mozilla_dom_PrivateAttribution_h

View file

@ -0,0 +1,22 @@
/* -*- 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_PrivateAttributionIPCUtils_h
#define mozilla_dom_PrivateAttributionIPCUtils_h
#include "mozilla/dom/PrivateAttributionBinding.h"
#include "mozilla/dom/BindingIPCUtils.h"
namespace IPC {
template <>
struct ParamTraits<mozilla::dom::PrivateAttributionImpressionType>
: public mozilla::dom::WebIDLEnumSerializer<
mozilla::dom::PrivateAttributionImpressionType> {};
} // namespace IPC
#endif

View file

@ -0,0 +1,31 @@
/* vim: set ts=2 sw=2 sts=2 et 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/. */
/**
*
*/
export class PrivateAttributionService {
onAttributionEvent(sourceHost, type, index, ad, targetHost) {
dump(
`onAttributionEvent(${sourceHost}, ${type}, ${index}, ${ad}, ${targetHost})\n`
);
}
onAttributionConversion(
sourceHost,
task,
histogramSize,
loopbackDays,
impressionType,
ads,
sourceHosts
) {
dump(
`onAttributionConversion(${sourceHost}, ${task}, ${histogramSize}, ${loopbackDays}, ${impressionType}, ${ads}, ${sourceHosts})\n`
);
}
QueryInterface = ChromeUtils.generateQI([Ci.nsIPrivateAttributionService]);
}

View file

@ -0,0 +1,15 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
Classes = [
{
'cid': '{57d16147-1deb-46ac-8f1c-1140b5e1ddfd}',
'name': 'PrivateAttribution',
'contract_ids': ['@mozilla.org/private-attribution;1'],
'esModule': 'resource://gre/modules/PrivateAttributionService.sys.mjs',
'constructor': 'PrivateAttributionService',
},
]

View file

@ -0,0 +1,35 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "DOM: Core & HTML")
EXPORTS.mozilla.dom += [
"PrivateAttribution.h",
"PrivateAttributionIPCUtils.h",
]
UNIFIED_SOURCES += [
"PrivateAttribution.cpp",
]
XPIDL_SOURCES += [
"nsIPrivateAttributionService.idl",
]
XPIDL_MODULE = "privateattribution"
XPCOM_MANIFESTS += [
"components.conf",
]
EXTRA_JS_MODULES += [
"PrivateAttributionService.sys.mjs",
]
include("/ipc/chromium/chromium-config.mozbuild")
FINAL_LIBRARY = "xul"

View file

@ -0,0 +1,14 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* 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/. */
#include "nsISupports.idl"
[scriptable, uuid(c7e7fc54-4133-4191-bd40-cc2b77fd21bc)]
interface nsIPrivateAttributionService : nsISupports
{
void onAttributionEvent(in ACString sourceHost, in ACString type, in uint32_t index, in AString ad, in ACString targetHost);
void onAttributionConversion(in ACString sourceHost, in AString task, in uint32_t histogramSize, in uint32_t loopbackDays, in ACString impressionType, in Array<AString> ads, in Array<ACString> sourceHosts);
};

View file

@ -394,3 +394,9 @@ partial interface Navigator {
[SameObject, Pref="dom.screenwakelock.enabled"] [SameObject, Pref="dom.screenwakelock.enabled"]
readonly attribute WakeLock wakeLock; readonly attribute WakeLock wakeLock;
}; };
[SecureContext]
partial interface Navigator {
[SameObject, Trial="PrivateAttribution"]
readonly attribute PrivateAttribution privateAttribution;
};

View file

@ -0,0 +1,29 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/. */
enum PrivateAttributionImpressionType { "view", "click" };
dictionary PrivateAttributionImpressionOptions {
PrivateAttributionImpressionType type = "view";
required unsigned long index;
required DOMString ad;
required UTF8String target;
};
dictionary PrivateAttributionConversionOptions {
required DOMString task;
required unsigned long histogramSize;
unsigned long lookbackDays;
PrivateAttributionImpressionType impression;
sequence<DOMString> ads = [];
sequence<UTF8String> sources = [];
};
[Trial="PrivateAttribution", SecureContext, Exposed=Window]
interface PrivateAttribution {
[Throws] undefined saveImpression(PrivateAttributionImpressionOptions options);
[Throws] undefined measureConversion(PrivateAttributionConversionOptions options);
};

View file

@ -804,6 +804,7 @@ WEBIDL_FILES = [
"PluginArray.webidl", "PluginArray.webidl",
"PointerEvent.webidl", "PointerEvent.webidl",
"PopoverInvokerElement.webidl", "PopoverInvokerElement.webidl",
"PrivateAttribution.webidl",
"ProcessingInstruction.webidl", "ProcessingInstruction.webidl",
"Promise.webidl", "Promise.webidl",
"PushEvent.webidl", "PushEvent.webidl",

View file

@ -3216,6 +3216,13 @@
value: 0 value: 0
mirror: always mirror: always
# Origin trial state for Private Attribution
# 0: normal, 1: always-enabled, 2: always-disabled
- name: dom.origin-trials.private-attribution.state
type: RelaxedAtomicInt32
value: 0
mirror: always
# Is support for Window.paintWorklet enabled? # Is support for Window.paintWorklet enabled?
- name: dom.paintWorklet.enabled - name: dom.paintWorklet.enabled
type: bool type: bool