129 | You may want to measure the demographics of the users who has seen some content you have embedded across
130 | different sites.
131 |
132 |
133 | For this demo, visit the survey page to enter your demographic data where the information you enter is added
134 | to Shared Storage. Then visit either Publisher A or B sites which will trigger the aggregatable report to be
135 | sent once across both sites. The content ID, age group ID, and the geography ID dimensions are encoded into
136 | the aggregation key (bucket), and the count is used as the aggregatable value.
137 |
138 |
139 | The summary report generated will provide information such as "Approximately 391 users who have seen the
140 | content ID 743 are between the age of 18-25 and are from Europe."
141 |
142 |
Code
143 |
144 |
145 | Iframe logic
149 | (embedded into the publisher page)
150 |
151 |
152 | Worklet
156 | (loaded and executed by the iframe logic)
157 |
158 |
159 |
160 |
161 |
162 |
163 | Shared Storage API is not enabled in your browser. Follow the
164 | instructions, and enable the the
168 | Privacy Sandbox Ads APIs
169 | experiment flag at
170 | chrome://flags/#privacy-sandbox-ads-apis
171 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
172 |
20 | To see if an experiment has the desired effect, you can conduct A/B testing across multiple sites. As an
21 | advertiser, you can choose to render a different ad based on what group the user is assigned to. This group
22 | assignment is stored into Shared Storage and can be used cross-site.
23 |
24 |
25 | In this demo, the user can be assigned to
26 | "Control",
27 | "Experiment A", or
28 | "Experiment B" groups. The same ad will be rendered across different sites based on what group the user
29 | is in. The initial assignment is random, and the demo contains a set of butons to manually add the user to a
30 | group.
31 |
32 |
Code
33 |
34 |
Iframe logic
38 | (embedded into the publisher page)
39 |
Worklet
43 | (loaded and executed by the iframe logic)
44 |
45 |
46 |
47 |
48 |
49 | Shared Storage API is not enabled in your browser. Follow the
50 | instructions, and enable the the
54 | Privacy Sandbox Ads APIs
55 | experiment flag at
56 | chrome://flags/#privacy-sandbox-ads-apis
57 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
58 |
20 | An advertiser may want to show different ads of the same campaign to the user to increase effectiveness of the
21 | ads.
22 |
23 |
24 | In this demo, the creative can be rotated with different strategies. In sequential rotation, creatives A, B and
25 | C are shown one after another. In even distribution, the creative is selected at random where each creative has
26 | an equal chance of being chosen. In weighted distribution, some creatives can be weighted to be chosen more
27 | often than another creative.
28 |
29 |
Code
30 |
31 |
Iframe logic
35 | (embedded into the publisher page)
36 |
Worklet
40 | (loaded and executed by the iframe logic)
41 |
42 |
43 |
44 |
45 |
46 | Shared Storage API is not enabled in your browser. Follow the
47 | instructions, and enable the the
51 | Privacy Sandbox Ads APIs
52 | experiment flag at
53 | chrome://flags/#privacy-sandbox-ads-apis
54 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
55 |
Shared storage - Creative selection by frequency demo
14 |
15 |
16 |
17 |
18 |
Description
19 |
20 | If an ad creative has been shown to the user too many times, select a different ad.
21 |
22 |
23 | In this demo, when the user sees the same example ad 5 times across both
24 | Publisher A
25 | and
26 | Publisher B
27 | sites, the default ad will be selected. The demo contains a button to reset the frequency back to 5.
28 |
29 |
Code
30 |
31 |
Iframe logic
35 | (embedded into the publisher page)
36 |
Worklet
40 | (loaded and executed by the iframe logic)
41 |
42 |
43 |
44 |
45 |
46 | Shared Storage API is not enabled in your browser. Follow the
47 | instructions, and enable the the
51 | Privacy Sandbox Ads APIs
52 | experiment flag at
53 | chrome://flags/#privacy-sandbox-ads-apis
54 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
55 |
23 | You may want to measure the demographics of the users who has seen some content you have embedded across
24 | different sites.
25 |
26 |
27 | For this demo, visit the survey page to enter your demographic data where the information you enter is added to
28 | Shared Storage. Then visit either Publisher A or B sites which will trigger the aggregatable report to be sent
29 | once across both sites. The content ID, age group ID, and the geography ID dimensions are encoded into the
30 | aggregation key (bucket), and the count is used as the aggregatable value.
31 |
32 |
33 | The summary report generated will provide information such as "Approximately 391 users who have seen the content
34 | ID 743 are between the age of 18-25 and are from Europe."
35 |
36 |
Code
37 |
38 |
Iframe logic
42 | (embedded into the publisher page)
43 |
Worklet
47 | (loaded and executed by the iframe logic)
48 |
49 |
50 |
51 |
52 |
53 | Shared Storage API is not enabled in your browser. Follow the
54 | instructions, and enable the the
58 | Privacy Sandbox Ads APIs
59 | experiment flag at
60 | chrome://flags/#privacy-sandbox-ads-apis
61 | with Chrome Canary and Dev 107.0.5292.0 and above to test this Shared Storage API demo.
62 |
16 | Whenever your mouse enters the demo fenced frame, an event-level report is submitted. Open up DevTools and check the network requests or the console log to see the submitted report.
17 |
Iframe logic
29 | (embedded into the publisher page)
30 |
Worklet
34 | (loaded and executed by the iframe logic)
35 |
36 |
37 |
38 |
39 |
40 | Shared Storage API is not enabled in your browser. Follow the
41 | instructions, and enable the the
45 | Privacy Sandbox Ads APIs
46 | experiment flag at
47 | chrome://flags/#privacy-sandbox-ads-apis
48 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
49 |
23 | You may want to measure the number of users who have seen your content K or more times a given client across
24 | different sites.
25 |
26 | In this demo, the impression count is added to Shared Storage where it increments by 1 whenever the content is
27 | loaded for both publisher A and B. When the impression count has reached 3, the Private Aggregation API is called.
28 | The content ID dimension is encoded as the aggregation key, and the count is used as the aggregatable value.
29 |
30 | The summary report generated will provide information such as "Approximately 391 users have seen the ad campaign
31 | ID 743 at least 3 times."
32 |
33 |
Code
34 |
35 |
Iframe logic
39 | (embedded into the publisher page)
40 |
Worklet
44 | (loaded and executed by the iframe logic)
45 |
46 |
47 |
48 |
49 |
50 | Shared Storage API is not enabled in your browser. Follow the
51 | instructions, and enable the the
55 | Privacy Sandbox Ads APIs
56 | experiment flag at
57 | chrome://flags/#privacy-sandbox-ads-apis
58 | with Chrome Canary and Dev 107.0.5292.0 and above to test this Shared Storage API demo.
59 |
19 | There may be cases where you want to render a different element based on whether the user has been seen before
20 | at a different site. A payment provider may want to render a
21 | "Register"
22 | or
23 | "Buy now"
24 | button based on whether the user has registered before at the payment provider's site.
25 |
26 |
27 | In this demo, a different button will be rendered for the publishers based on the user status set from the
28 | payment provider page.
29 |
30 |
Code
31 |
32 |
Iframe logic
36 | (embedded into the publisher page)
37 |
Worklet
41 | (loaded and executed by the iframe logic)
42 |
43 |
44 |
45 |
46 |
47 | Shared Storage API is not enabled in your browser. Follow the
48 | instructions, and enable the the
52 | Privacy Sandbox Ads APIs
53 | experiment flag at
54 | chrome://flags/#privacy-sandbox-ads-apis
55 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
56 |
23 | You may want to keep track of how many unique impressions your content has across different sites. In this demo,
24 | visiting either Publisher A or B sites will send the aggregatable report, but will not send it on subsequent
25 | visits. The ad camaign ID dimension is encoded into the aggregation key (bucket), and the count is used as the
26 | aggregatable value. To reset the demo, hover the "Reset impression flag" button on the demo page.
27 |
28 |
29 | The summary report will contain information such as "Approximately 391 users have seen the ad campaign ID 743."
30 |
31 |
Code
32 |
33 |
Iframe logic
37 | (embedded into the publisher page)
38 |
Worklet
42 | (loaded and executed by the iframe logic)
43 |
44 |
45 |
46 |
47 |
48 | Shared Storage API is not enabled in your browser. Follow the
49 | instructions, and enable the the
53 | Privacy Sandbox Ads APIs
54 | experiment flag at
55 | chrome://flags/#privacy-sandbox-ads-apis
56 | with Chrome Canary and Dev 107.0.5292.0 and above to test this Shared Storage API demo.
57 |
13 |
14 | {{!-- This third-party script writes data to the publisher's shared storage --}}
15 |
16 |
17 | {{!-- This third-party iframe's code writes data to its own shared storage --}}
18 |
19 |
20 |
21 |
Description
22 |
23 | When a third-party code is added to the publisher's page, the third-party can write to the shared storage of their own origin, or write to the publisher's origin.
24 |
25 |
26 | Using a script tag allows the third-party code to be injected in the publisher's context, and using an iframe to load third-party code allows third-party to write to its own shared storage.
27 |
45 | Shared Storage API is not enabled in your browser. Follow the
46 | instructions, and enable the the
50 | Privacy Sandbox Ads APIs
51 | experiment flag at
52 | chrome://flags/#privacy-sandbox-ads-apis
53 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
54 |
16 | Whenever the fenced frame navigates the top-level to another page, the remaining entropy budget is decreased for the origin the fenced frame is served from. Clicking on the fenced frame will navigate this demo to another page. Press the back button to return to the page. Check the DevTools / Applications tab and select the origin to see the remaining entropy budget.
17 |
Iframe logic
29 | (embedded into the publisher page)
30 |
Worklet
34 | (loaded and executed by the iframe logic)
35 |
36 |
37 |
38 |
39 |
40 | Shared Storage API is not enabled in your browser. Follow the
41 | instructions, and enable the the
45 | Privacy Sandbox Ads APIs
46 | experiment flag at
47 | chrome://flags/#privacy-sandbox-ads-apis
48 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
49 |
21 | To see if an experiment has the desired effect, you can conduct A/B testing across multiple sites. As an
22 | advertiser, you can choose to render a different ad based on what group the user is assigned to. This group
23 | assignment is stored into Shared Storage and can be used cross-site.
24 |
25 |
26 | In this demo, the user can be assigned to
27 | "Control",
28 | "Experiment A", or
29 | "Experiment B" groups. The same ad will be rendered across different sites based on what group the user
30 | is in. The initial assignment is random, and the demo contains a set of butons to manually add the user to a
31 | group.
32 |
33 |
Code
34 |
35 |
Iframe logic
39 | (embedded into the publisher page)
40 |
Worklet
44 | (loaded and executed by the iframe logic)
45 |
46 |
47 |
48 |
49 |
50 | Shared Storage API is not enabled in your browser. Follow the
51 | instructions, and enable the the
55 | Privacy Sandbox Ads APIs
56 | experiment flag at
57 | chrome://flags/#privacy-sandbox-ads-apis
58 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
59 |
20 | An advertiser may want to show different ads of the same campaign to the user to increase effectiveness of the
21 | ads.
22 |
23 |
24 | In this demo, the creative can be rotated with different strategies. In sequential rotation, creatives A, B and
25 | C are shown one after another. In even distribution, the creative is selected at random where each creative has
26 | an equal chance of being chosen. In weighted distribution, some creatives can be weighted to be chosen more
27 | often than another creative.
28 |
29 |
Code
30 |
31 |
Iframe logic
35 | (embedded into the publisher page)
36 |
Worklet
40 | (loaded and executed by the iframe logic)
41 |
42 |
43 |
44 |
45 |
46 | Shared Storage API is not enabled in your browser. Follow the
47 | instructions, and enable the the
51 | Privacy Sandbox Ads APIs
52 | experiment flag at
53 | chrome://flags/#privacy-sandbox-ads-apis
54 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
55 |
Shared storage - Creative selection by frequency demo
14 |
15 |
16 |
17 |
18 |
Description
19 |
20 | If an ad creative has been shown to the user too many times, select a different ad.
21 |
22 |
23 | In this demo, when the user sees the same example ad 5 times across both
24 | Publisher A
25 | and
26 | Publisher B
27 | sites, the default ad will be selected. The demo contains a button to reset the frequency back to 5.
28 |
29 |
Code
30 |
31 |
Iframe logic
35 | (embedded into the publisher page)
36 |
Worklet
40 | (loaded and executed by the iframe logic)
41 |
42 |
43 |
44 |
45 |
46 | Shared Storage API is not enabled in your browser. Follow the
47 | instructions, and enable the the
51 | Privacy Sandbox Ads APIs
52 | experiment flag at
53 | chrome://flags/#privacy-sandbox-ads-apis
54 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
55 |
23 | You may want to measure the number of users who have seen your content K or more times a given client across
24 | different sites.
25 |
26 | In this demo, the impression count is added to Shared Storage where it increments by 1 whenever the content is
27 | loaded for both publisher A and B. When the impression count has reached 3, the Private Aggregation API is called.
28 | The content ID dimension is encoded as the aggregation key, and the count is used as the aggregatable value.
29 |
30 | The summary report generated will provide information such as "Approximately 391 users have seen the ad campaign
31 | ID 743 at least 3 times."
32 |
33 |
Code
34 |
35 |
Iframe logic
39 | (embedded into the publisher page)
40 |
Worklet
44 | (loaded and executed by the iframe logic)
45 |
46 |
47 |
48 |
49 |
50 | Shared Storage API is not enabled in your browser. Follow the
51 | instructions, and enable the the
55 | Privacy Sandbox Ads APIs
56 | experiment flag at
57 | chrome://flags/#privacy-sandbox-ads-apis
58 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
59 |
19 | There may be cases where you want to render a different element based on whether the user has been seen before
20 | at a different site. A payment provider may want to render a
21 | "Register"
22 | or
23 | "Buy now"
24 | button based on whether the user has registered before at the payment provider's site.
25 |
26 |
27 | In this demo, a different button will be rendered for the publishers based on the user status set from the
28 | payment provider page.
29 |
30 |
Code
31 |
32 |
Iframe logic
36 | (embedded into the publisher page)
37 |
Worklet
41 | (loaded and executed by the iframe logic)
42 |
43 |
44 |
45 |
46 |
47 | Shared Storage API is not enabled in your browser. Follow the
48 | instructions, and enable the the
52 | Privacy Sandbox Ads APIs
53 | experiment flag at
54 | chrome://flags/#privacy-sandbox-ads-apis
55 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
56 |
23 | You may want to keep track of how many unique impressions your content has across different sites. In this demo,
24 | visiting either Publisher A or B sites will send the aggregatable report, but will not send it on subsequent
25 | visits. The ad camaign ID dimension is encoded into the aggregation key (bucket), and the count is used as the
26 | aggregatable value. To reset the demo, hover the "Reset impression flag" button on the demo page.
27 |
28 |
29 | The summary report will contain information such as "Approximately 391 users have seen the ad campaign ID 743."
30 |
31 |
Code
32 |
33 |
Iframe logic
37 | (embedded into the publisher page)
38 |
Worklet
42 | (loaded and executed by the iframe logic)
43 |
44 |
45 |
46 |
47 |
48 | Shared Storage API is not enabled in your browser. Follow the
49 | instructions, and enable the the
53 | Privacy Sandbox Ads APIs
54 | experiment flag at
55 | chrome://flags/#privacy-sandbox-ads-apis
56 | with Chrome 104.0.5086.0 and above to test this Shared Storage API demo.
57 |
The specified file was not found on this website. Check the URL for mistakes and try again.
99 |
Why am I seeing this?
100 |
101 | This page was generated by the Firebase Command-Line Interface. To modify it, edit the
102 | 404.html file in your project's configured public directory.
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/sites/content-producer/.well-known/privacy-sandbox-attestations.json:
--------------------------------------------------------------------------------
1 | {
2 | "privacy_sandbox_api_attestations": [
3 | {
4 | "attestation_parser_version": "2",
5 | "attestation_version": "1",
6 | "privacy_policy": [
7 | "https://policies.google.com/privacy"
8 | ],
9 | "ownership_token": "nLv7az7CCLlmraqX5BBX9yiahJpjE1rdRlRXPAmZ44wCXf4hGS60Y0bsbxI5U09z",
10 | "issued_seconds_since_epoch": 1727275829,
11 | "enrollment_id": "NHT7X",
12 | "enrollment_site": "https://shared-storage-demo-content-producer.web.app/",
13 | "platform_attestations": [
14 | {
15 | "platform": "chrome",
16 | "attestations": {
17 | "shared_storage_api": {
18 | "ServiceNotUsedForIdentifyingUserAcrossSites": true
19 | },
20 | "private_aggregation_api": {
21 | "ServiceNotUsedForIdentifyingUserAcrossSites": true
22 | }
23 | }
24 | },
25 | {
26 | "platform": "android",
27 | "attestations": {}
28 | }
29 | ]
30 | }
31 | ]
32 | }
--------------------------------------------------------------------------------
/sites/content-producer/ads/ad-1.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/ad-2.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/ad-3.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/ad.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | body {
18 | margin: 0;
19 | padding: 0;
20 | }
21 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/buy-now-button.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/default-ad.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/example-ad.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/experiment-ad-a.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/experiment-ad-b.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/hover-ad.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/nav-ad-1.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/nav-ad-2.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/nav-ad-3.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/sites/content-producer/ads/register-button.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/sites/content-producer/assets/ad-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/ad-1.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/ad-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/ad-2.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/ad-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/ad-3.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/buy-now-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/buy-now-button.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/default-ad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/default-ad.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/example-ad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/example-ad.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/experiment-a.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/experiment-a.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/experiment-b.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/experiment-b.png
--------------------------------------------------------------------------------
/sites/content-producer/assets/register-button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/assets/register-button.png
--------------------------------------------------------------------------------
/sites/content-producer/demo.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | body {
18 | width: 300px;
19 | }
20 |
21 | :where(h1, h2, h3, h4, h5, h6) {
22 | font-weight: var(--font-weight-6);
23 | }
24 |
25 | #ad-slot {
26 | height: 250px;
27 | border: none;
28 | }
29 |
30 | #button-slot {
31 | height: 100px;
32 | border: none;
33 | }
34 |
35 | .demo__card,
36 | .demo__controls {
37 | display: flex;
38 | flex-direction: column;
39 | align-items: center;
40 | gap: var(--size-2);
41 | background: var(--surface-3);
42 | border: 1px solid var(--surface-1);
43 | padding: var(--size-3);
44 | margin: var(--size-3) 0;
45 | border-radius: var(--radius-3);
46 | box-shadow: var(--shadow-2);
47 | margin-top: 0;
48 | }
49 |
50 | .demo__controls h5 {
51 | margin-bottom: var(--size-2);
52 | }
53 |
54 | .demo__buttons-container {
55 | display: flex;
56 | flex-direction: column;
57 | width: fit-content;
58 | }
59 |
60 | .demo__button {
61 | zoom: 90%;
62 | /* max-width: var(--size-13); */
63 | --_bg: linear-gradient(var(--indigo-5), var(--indigo-7));
64 | --_border: var(--indigo-6);
65 | --_text: var(--indigo-0);
66 | --_ink-shadow: 0 1px 0 var(--indigo-9);
67 | margin: 5px;
68 | }
69 |
--------------------------------------------------------------------------------
/sites/content-producer/index.css:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/content-producer/index.css
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/demographics-measurement-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * The scale factor is multiplied by the aggregatable value to maximize
19 | * the signal-to-noise ratio. See “Noise and scaling” section in the
20 | * Aggregation Fundamentals document to learn more:
21 | * - https://developer.chrome.com/en/docs/privacy-sandbox/aggregation-fundamentals
22 | *
23 | * The scale factor used here has been intentionally reduced to an arbitrary number.
24 | * The maximum aggregatable value used in this demo is 1. The scale factor
25 | * of 65536 would have maximized the signal-to-noise ratio. However, that also
26 | * uses the entire contribution budget for the rolling 24-hour period.
27 | *
28 | * Therefore, the scale factor has been significantly reduced to allow the demo
29 | * to be used repeatedly for testing throughout without hitting the contribution
30 | * budget limit.
31 | */
32 | const SCALE_FACTOR = 128;
33 |
34 | /**
35 | * The bucket key must be a number, and in this case, it is simply the ad campaign
36 | * ID itself. For more complex buckey key construction, see other use cases in
37 | * this demo.
38 | */
39 | const AGGREGATION_KEY_MAP = {
40 | ageGroupId: {
41 | '18-39': '1',
42 | '40-64': '2',
43 | '65+': '3',
44 | },
45 | continentId: {
46 | africa: '1',
47 | antarctica: '2',
48 | asia: '3',
49 | australia: '4',
50 | europe: '5',
51 | 'north-america': '6',
52 | 'south-america': '7',
53 | },
54 | };
55 |
56 | /**
57 | * The aggregation key will be in the format of:
58 | * contentId | ageGroupId | continentId
59 | *
60 | * For example, a user from Australia between the age of 40-64, who has
61 | * seen the Content ID 321 will be represented by the key:
62 | * 321 | 2 | 4 or 32124
63 | */
64 | function generateAggregationKey(contentId, ageGroup, continent) {
65 | const ageGroupId = AGGREGATION_KEY_MAP.ageGroupId[ageGroup];
66 | const continentId = AGGREGATION_KEY_MAP.continentId[continent];
67 | const aggregationKey = BigInt(`${contentId}${ageGroupId}${continentId}`);
68 |
69 | console.log(`[PAA] Content ID is ${contentId}`);
70 | console.log(`[PAA] Continent is ${continent}, and the corresponding ID is ${continentId}`);
71 | console.log(`[PAA] Age group is ${ageGroup}, and the corresponding ID is ${ageGroupId}`);
72 | console.log(
73 | `[PAA] The generated aggregation key is ${aggregationKey} which is composed of "${contentId} | ${ageGroupId} | ${continentId}"`
74 | );
75 |
76 | return aggregationKey;
77 | }
78 |
79 | class DemographicsMeasurementOperation {
80 | async run(data) {
81 | try {
82 | const { contentId, debugKey } = data;
83 |
84 | // Read from Shared Storage
85 | const key = 'has-reported-content';
86 | const hasReportedContent = (await sharedStorage.get(key)) === 'true';
87 | const ageGroup = await sharedStorage.get('age-group');
88 | const continent = await sharedStorage.get('continent');
89 |
90 | // Do not report if a report has been sent already
91 | if (hasReportedContent) {
92 | console.log(
93 | `[PAA] Demographics measurement report has been submitted already for Content ID ${contentId}. Reset the demo to start over.`
94 | );
95 | return;
96 | }
97 |
98 | if (!ageGroup || !continent) {
99 | console.log('[PAA] Demographics data is missing. Visit the survey data to enter your data.');
100 | return;
101 | }
102 |
103 | // Generate the aggregation key and the aggregatable value
104 | const bucket = generateAggregationKey(data.contentId, ageGroup, continent);
105 | const value = 1 * SCALE_FACTOR;
106 |
107 | // Send an aggregatable report via the Private Aggregation API
108 | privateAggregation.enableDebugMode({ debugKey });
109 | privateAggregation.contributeToHistogram({ bucket, value });
110 |
111 | // Set the report submission status flag
112 | await sharedStorage.set(key, true);
113 |
114 | console.log(`[PAA] Unique reach measurement report will be submitted for Content ID ${data.contentId}`);
115 | console.log(`[PAA] The aggregation key is ${bucket} and the aggregatable value is ${value}`);
116 | } catch (e) {
117 | console.error(e);
118 | }
119 | }
120 | }
121 |
122 | // Register the operation
123 | register('demographics-measurement', DemographicsMeasurementOperation);
124 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/demographics-measurement.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
41 |
42 |
43 |
44 |
45 |
Demo control
46 |
47 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/demographics-measurement.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | async function measureDemographics() {
17 | // Load the Shared Storage worklet
18 | await window.sharedStorage.worklet.addModule('demographics-measurement-worklet.js');
19 |
20 | // Run the demographics measurement operation
21 | await window.sharedStorage.run('demographics-measurement', { data: { contentId: '123', debugKey: 888n } });
22 | }
23 |
24 | measureDemographics();
25 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/demographics-survey.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | :where(h1, h2, h3, h4, h5, h6) {
17 | font-weight: var(--font-weight-6);
18 | }
19 |
20 | main {
21 | margin: 0 auto;
22 | padding: var(--size-5);
23 | }
24 |
25 | p {
26 | margin-bottom: var(--size-3);
27 | max-inline-size: var(--size-content-4);
28 | }
29 |
30 | card {
31 | display: flex;
32 | flex-direction: column;
33 | gap: var(--size-2);
34 | background: var(--surface-3);
35 | border: 1px solid var(--surface-1);
36 | padding: var(--size-4);
37 | border-radius: var(--radius-3);
38 | box-shadow: var(--shadow-2);
39 | }
40 |
41 | .demo__instructions {
42 | display: none;
43 | }
44 |
45 | .demo__header {
46 | padding: var(--size-5) 0;
47 | display: flex;
48 | align-items: center;
49 | }
50 |
51 | .demo__publisher-badge {
52 | display: flex;
53 | margin-right: var(--size-3);
54 | justify-content: center;
55 | align-items: center;
56 | padding: var(--size-3);
57 | }
58 |
59 | .demo__publisher-badge--publisher-a {
60 | background-color: var(--green-5);
61 | }
62 |
63 | .demo__publisher-badge--publisher-b {
64 | background-color: var(--orange-4);
65 | }
66 |
67 | .demo__content {
68 | display: flex;
69 | flex-direction: column;
70 | align-items: center;
71 | }
72 |
73 | .demo__description {
74 | height: fit-content;
75 | }
76 |
77 | .demo__radio-label {
78 | display: block;
79 | }
80 |
81 | .demo__survey-container {
82 | width: 100%;
83 | }
84 |
85 | @media screen and (min-width: 481px) {
86 | main {
87 | max-width: 1000px;
88 | }
89 |
90 | .demo__content {
91 | flex-direction: row;
92 | align-items: flex-start;
93 | }
94 | .demo__description {
95 | margin-left: var(--size-3);
96 | }
97 | }
98 |
99 | @media (prefers-color-scheme: dark) {
100 | .demo__publisher-badge--publisher-a {
101 | background-color: var(--green-9);
102 | }
103 |
104 | .demo__publisher-badge--publisher-b {
105 | background-color: var(--orange-9);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/demographics-survey.js:
--------------------------------------------------------------------------------
1 | const form = document.querySelector('.demo__survey-form');
2 |
3 | form.addEventListener('submit', async (event) => {
4 | event.preventDefault();
5 | const ageGroup = form.querySelector('[name=age-group]:checked').value;
6 | const continent = form.querySelector('[name=continent]:checked').value;
7 |
8 | await window.sharedStorage.set('age-group', ageGroup);
9 | await window.sharedStorage.set('continent', continent);
10 |
11 | console.log('The following information has been set in Shared Storage:');
12 | console.log(`ageGroup = ${ageGroup}`);
13 | console.log(`continent = ${continent}`);
14 | console.log('Visit the publisher page to continue the demo.');
15 |
16 | const submitButtonEl = form.querySelector('.demo__submit-button');
17 | const publisherLinkButtonEl = form.querySelector('.demo__continue-button');
18 | submitButtonEl.style.display = 'none';
19 | publisherLinkButtonEl.style.display = 'initial';
20 | });
21 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/k-freq-measurement-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * The scale factor is multiplied by the aggregatable value to maximize
19 | * the signal-to-noise ratio. See “Noise and scaling” section in the
20 | * Aggregation Fundamentals document to learn more:
21 | * - https://developer.chrome.com/en/docs/privacy-sandbox/aggregation-fundamentals
22 | *
23 | * The scale factor used here has been intentionally reduced to an arbitrary number.
24 | * The maximum aggregatable value used in this demo is 1. The scale factor
25 | * of 65536 would have maximized the signal-to-noise ratio. However, that also
26 | * uses the entire contribution budget for the rolling 24-hour period.
27 | *
28 | * Therefore, the scale factor has been significantly reduced to allow the demo
29 | * to be used repeatedly for testing throughout without hitting the contribution
30 | * budget limit.
31 | */
32 | const SCALE_FACTOR = 128;
33 |
34 | /**
35 | * The bucket key must be a number, and in this case, it is simply the content
36 | * ID itself. For more complex buckey key construction, see other use cases in
37 | * this demo.
38 | */
39 | function convertContentIdToBucket(contentId) {
40 | return BigInt(contentId);
41 | }
42 |
43 | class KFreqMeasurementOperation {
44 | async run(data) {
45 | try {
46 | const { kFreq, contentId, debugKey } = data;
47 |
48 | // Read from Shared Storage
49 | const hasReportedContentKey = 'has-reported-content';
50 | const impressionCountKey = 'impression-count';
51 | const hasReportedContent = (await sharedStorage.get(hasReportedContentKey)) === 'true';
52 | const impressionCount = parseInt((await sharedStorage.get(impressionCountKey)) || 0);
53 |
54 | // Do not report if a report has been sent already
55 | if (hasReportedContent) {
56 | console.log(
57 | `[PAA] K-frequency report has been submitted already for Content ID ${contentId}. Reset the demo to start over.`
58 | );
59 | return;
60 | }
61 |
62 | // Check ipmression count against frequency limit
63 | if (impressionCount < kFreq) {
64 | console.log(
65 | `[PAA] Current impression count is ${impressionCount} and has not met the K threashold of ${kFreq}`
66 | );
67 | await sharedStorage.set(impressionCountKey, impressionCount + 1);
68 | return;
69 | }
70 |
71 | // Generate the aggregation key and the aggregatable value
72 | const bucket = convertContentIdToBucket(contentId);
73 | const value = 1 * SCALE_FACTOR;
74 |
75 | // Send an aggregatable report via the Private Aggregation API
76 | privateAggregation.enableDebugMode({ debugKey });
77 | privateAggregation.contributeToHistogram({ bucket, value });
78 |
79 | // Set the report submission status flag
80 | await sharedStorage.set(hasReportedContentKey, 'true');
81 |
82 | console.log(`[PAA] Current impression count is ${impressionCount} which meets the K threashold of ${kFreq}`);
83 | console.log('[PAA] K-frequency measurement report will be submitted');
84 | console.log(`[PAA] The aggregation key is ${bucket} and the aggregatable value is ${value}`);
85 | } catch (e) {
86 | console.log(e);
87 | }
88 | }
89 | }
90 |
91 | // Register the operation
92 | register('k-freq-measurement', KFreqMeasurementOperation);
93 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/k-freq-measurement.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
40 |
41 |
42 |
43 |
44 |
Demo control
45 |
46 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/k-freq-measurement.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | async function injectAd() {
18 | // Load the Shared Storage worklet
19 | await window.sharedStorage.worklet.addModule('k-freq-measurement-worklet.js');
20 |
21 | // Run the K-frequency measurement operation
22 | await window.sharedStorage.run('k-freq-measurement', { data: { kFreq: 3, contentId: 123, debugKey: 999n } });
23 | }
24 |
25 | injectAd();
26 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/reach-measurement-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | /**
18 | * The scale factor is multiplied by the aggregatable value to maximize
19 | * the signal-to-noise ratio. See “Noise and scaling” section in the
20 | * Aggregation Fundamentals document to learn more:
21 | * - https://developer.chrome.com/en/docs/privacy-sandbox/aggregation-fundamentals
22 | *
23 | * The scale factor used here has been intentionally reduced to an arbitrary number.
24 | * The maximum aggregatable value used in this demo is 1. The scale factor
25 | * of 65536 would have maximized the signal-to-noise ratio. However, that also
26 | * uses the entire contribution budget for the rolling 24-hour period.
27 | *
28 | * Therefore, the scale factor has been significantly reduced to allow the demo
29 | * to be used repeatedly for testing throughout without hitting the contribution
30 | * budget limit.
31 | */
32 | const SCALE_FACTOR = 128;
33 |
34 | /**
35 | * The bucket key must be a number, and in this case, it is simply the ad campaign
36 | * ID itself. For more complex buckey key construction, see other use cases in this demo.
37 | */
38 | function convertContentIdToBucket(contentId) {
39 | return BigInt(contentId);
40 | }
41 |
42 | class ReachMeasurementOperation {
43 | async run(data) {
44 | try {
45 | const { contentId, debugKey } = data;
46 |
47 | // Read from Shared Storage
48 | const key = 'has-reported-content';
49 | const hasReportedContent = (await sharedStorage.get(key)) === 'true';
50 |
51 | // Do not report if a report has been sent already
52 | if (hasReportedContent) {
53 | console.log(
54 | `[PAA] Unique reach measurement report has been submitted already for Content ID ${data.contentId}. Reset the demo to start over.`
55 | );
56 | return;
57 | }
58 |
59 | // Generate the aggregation key and the aggregatable value
60 | const bucket = convertContentIdToBucket(contentId);
61 | const value = 1 * SCALE_FACTOR;
62 |
63 | // Send an aggregatable report via the Private Aggregation API
64 | privateAggregation.enableDebugMode({ debugKey });
65 | privateAggregation.contributeToHistogram({ bucket, value });
66 |
67 | // Set the report submission status flag
68 | await sharedStorage.set(key, true);
69 |
70 | console.log(`[PAA] Unique reach measurement report will be submitted for Content ID ${contentId}`);
71 | console.log(`[PAA] The aggregation key is ${bucket} and the aggregatable value is ${value}`);
72 | } catch (e) {
73 | console.log(e);
74 | }
75 | }
76 | }
77 |
78 | // Register the operation
79 | register('reach-measurement', ReachMeasurementOperation);
80 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/reach-measurement.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
Demo control
44 |
45 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/sites/content-producer/private-aggregation/reach-measurement.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | async function measureUniqueReach() {
17 | // Load the Shared Storage worklet
18 | await window.sharedStorage.worklet.addModule('reach-measurement-worklet.js');
19 |
20 | // Run the reach measurement operation
21 | await window.sharedStorage.run('reach-measurement', { data: { contentId: '1234', debugKey: 777n } });
22 | }
23 |
24 | measureUniqueReach();
25 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/ab-testing-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // This code is loaded as a Shared Storage worklet
18 | class SelectURLOperation {
19 | async run(urls, data) {
20 | // Read the user's group from shared storage
21 | const experimentGroup = await sharedStorage.get('ab-testing-group');
22 |
23 | // Log to console for the demo
24 | console.log(`urls = ${JSON.stringify(urls)}`);
25 | console.log(`data = ${JSON.stringify(data)}`);
26 | console.log(`ab-testing-group in shared storage is ${experimentGroup}`);
27 |
28 | // Return the index of the group
29 | return data.indexOf(experimentGroup);
30 | }
31 | }
32 |
33 | function getBucketForTestingGroup(testingGroup) {
34 | switch (testingGroup) {
35 | case 'control':
36 | return 0;
37 | case 'experiment-a':
38 | return 1;
39 | case 'experiment-b':
40 | return 2;
41 | }
42 | }
43 |
44 | class ExperimentGroupReportingOperation {
45 | async run() {
46 | const experimentGroup = await sharedStorage.get('ab-testing-group');
47 |
48 | const bucket = BigInt(getBucketForTestingGroup(experimentGroup));
49 | privateAggregation.contributeToHistogram({ bucket, value: 1 });
50 | }
51 | }
52 |
53 | // Register the operation as 'ab-testing'
54 | register('ab-testing', SelectURLOperation);
55 | register('experiment-group-reporting', ExperimentGroupReportingOperation);
56 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/ab-testing.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
40 |
41 |
42 |
43 |
44 |
45 |
Demo control
46 |
47 |
53 |
59 |
65 |
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/ab-testing.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // For demo purposes. The hostname is used to determine the usage of
18 | // development localhost URL vs production URL
19 | const contentProducerUrl = window.location.host;
20 |
21 | // Map the experiment groups to the URLs
22 | const EXPERIMENT_MAP = [
23 | {
24 | group: 'control',
25 | url: `https://${contentProducerUrl}/ads/default-ad.html`,
26 | },
27 | {
28 | group: 'experiment-a',
29 | url: `https://${contentProducerUrl}/ads/experiment-ad-a.html`,
30 | },
31 | {
32 | group: 'experiment-b',
33 | url: `https://${contentProducerUrl}/ads/experiment-ad-b.html`,
34 | },
35 | ];
36 |
37 | // Choose a random group for the initial experiment
38 | function getRandomExperiment() {
39 | const randomIndex = Math.floor(Math.random() * EXPERIMENT_MAP.length);
40 | return EXPERIMENT_MAP[randomIndex].group;
41 | }
42 |
43 | async function injectAd() {
44 | // Load the worklet module
45 | const abTestingWorklet = await window.sharedStorage.createWorklet(
46 | 'ab-testing-worklet.js'
47 | );
48 |
49 | // Set the initial value in the storage to a random experiment group
50 | window.sharedStorage.set('ab-testing-group', getRandomExperiment(), {
51 | ignoreIfPresent: true,
52 | });
53 |
54 | const urls = EXPERIMENT_MAP.map(({ url }) => ({ url }));
55 | const groups = EXPERIMENT_MAP.map(({ group }) => group);
56 |
57 | // Resolve the selectURL call to a fenced frame config only when it exists on the page
58 | const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
59 |
60 | // Run the URL selection operation to select an ad based on the experiment group in shared storage
61 | const selectedUrl = await abTestingWorklet.selectURL('ab-testing', urls, {
62 | data: groups,
63 | resolveToConfig,
64 | keepAlive: true,
65 | });
66 |
67 | const adSlot = document.getElementById('ad-slot');
68 |
69 | if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
70 | adSlot.config = selectedUrl;
71 | } else {
72 | adSlot.src = selectedUrl;
73 | }
74 |
75 | // Run the reporting operation
76 | await abTestingWorklet.run('experiment-group-reporting')
77 | }
78 |
79 | injectAd();
80 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/creative-rotation-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class SelectURLOperation {
18 | async run(urls, data) {
19 | // Initially set the storage to sequential mode for the demo
20 | await SelectURLOperation.seedStorage();
21 |
22 | // Read the rotation mode from Shared Storage
23 | const rotationMode = await sharedStorage.get('creative-rotation-mode');
24 |
25 | // Generate a random number to be used for rotation
26 | const randomNumber = Math.random();
27 |
28 | let index;
29 |
30 | switch (rotationMode) {
31 | /**
32 | * Sequential rotation
33 | * - Rotates the creatives in order
34 | * - Example: A -> B -> C -> A ...
35 | */
36 | case 'sequential':
37 | const currentIndex = await sharedStorage.get('creative-rotation-index');
38 | index = parseInt(currentIndex, 10);
39 | const nextIndex = (index + 1) % urls.length;
40 |
41 | console.log(`index = ${index} / next index = ${nextIndex}`);
42 |
43 | await sharedStorage.set('creative-rotation-index', nextIndex.toString());
44 | break;
45 |
46 | /**
47 | * Evenly-distributed rotation
48 | * - Rotates the creatives with equal probability
49 | * - Example: A=33% / B=33% / C=33%
50 | */
51 | case 'even-distribution':
52 | index = Math.floor(randomNumber * urls.length);
53 | break;
54 |
55 | /**
56 | * Weighted rotation
57 | * - Rotates the creatives with weighted probability
58 | * - Example: A=70% / B=20% / C=10%
59 | */
60 | case 'weighted-distribution':
61 | console.log('data = ', JSON.stringify(data));
62 | // Find the first URL where the cumnulative sum of the weights
63 | // exceed the random number. The array is sorted by the weight
64 | // in descending order.
65 | let weightSum = 0;
66 | const { url } = data
67 | .sort((a, b) => b.weight - a.weight)
68 | .find(({ weight }) => {
69 | weightSum += weight;
70 | return weightSum > randomNumber;
71 | });
72 |
73 | index = urls.indexOf(url);
74 | break;
75 |
76 | default:
77 | index = 0;
78 | }
79 |
80 | console.log(JSON.stringify({ index, randomNumber, rotationMode }));
81 | return index;
82 | }
83 |
84 | // Set the mode to sequential and set the starting index to 0.
85 | static async seedStorage() {
86 | await sharedStorage.set('creative-rotation-mode', 'sequential', {
87 | ignoreIfPresent: true,
88 | });
89 |
90 | await sharedStorage.set('creative-rotation-index', 0, {
91 | ignoreIfPresent: true,
92 | });
93 | }
94 | }
95 |
96 | class SetRotationModeOperation {
97 | async run({ rotationMode }) {
98 | await sharedStorage.set('creative-rotation-mode', rotationMode);
99 | }
100 | }
101 |
102 | // Register the operation as 'creative-rotation'
103 | register('creative-rotation', SelectURLOperation);
104 | register('set-rotation-mode', SetRotationModeOperation);
105 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/creative-rotation.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // For demo purposes. The hostname is used to determine the usage of
18 | // development localhost URL vs production URL
19 | const contentProducerUrl = window.getContentProducerUrl();
20 |
21 | // Ad confg with the URL of the ad, a probability weight for rotation, and the clickthrough rate.
22 | const DEMO_AD_CONFIG = [
23 | {
24 | url: `${contentProducerUrl}/ads/ad-1.html`,
25 | weight: 0.7,
26 | },
27 | {
28 | url: `${contentProducerUrl}/ads/ad-2.html`,
29 | weight: 0.2,
30 | },
31 | {
32 | url: `${contentProducerUrl}/ads/ad-3.html`,
33 | weight: 0.1,
34 | },
35 | ];
36 |
37 | async function setRotationMode(rotationMode) {
38 | // Load the worklet module
39 | const creativeRotationWorklet = await window.sharedStorage.createWorklet(
40 | `${contentProducerUrl}/url-selection/creative-rotation-worklet.js`,
41 | { dataOrigin: 'script-origin' }
42 | );
43 |
44 | await creativeRotationWorklet.run('set-rotation-mode', {
45 | data: { rotationMode }
46 | });
47 | console.log(`creative rotation mode set to ${rotationMode}`);
48 | }
49 |
50 | async function injectAd() {
51 | // Load the worklet module
52 | const creativeRotationWorklet = await window.sharedStorage.createWorklet(
53 | `${contentProducerUrl}/url-selection/creative-rotation-worklet.js`,
54 | { dataOrigin: 'script-origin' }
55 | );
56 |
57 | const urls = DEMO_AD_CONFIG.map(({ url }) => ({ url }));
58 |
59 | // Resolve the selectURL call to a fenced frame config only when it exists on the page
60 | const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
61 |
62 | // Run the URL selection operation to determine the next ad that should be rendered
63 | const selectedUrl = await creativeRotationWorklet.selectURL('creative-rotation', urls, {
64 | data: DEMO_AD_CONFIG,
65 | resolveToConfig
66 | });
67 |
68 | const adSlot = document.getElementById('ad-slot');
69 |
70 | if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
71 | adSlot.config = selectedUrl;
72 | } else {
73 | adSlot.src = selectedUrl;
74 | }
75 | }
76 |
77 | injectAd();
78 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/creative-selection-by-frequency-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | const FREQUENCY_LIMIT = 5;
18 |
19 | class CreativeSelectionByFrequencyOperation {
20 | async run(urls, data) {
21 | // Read the current frequency cap in shared storage
22 | const count = parseInt(await sharedStorage.get('frequency-count'));
23 |
24 | // Log to console for the demo
25 | console.log(`urls = ${JSON.stringify(urls)}`);
26 | console.log(`frequency-count in shared storage is ${count}`);
27 |
28 | // If the count is 0, the frequency cap has been reached
29 | if (count === FREQUENCY_LIMIT) {
30 | console.log('frequency limit has been reached, and the default ad will be rendered');
31 | return 0;
32 | }
33 |
34 | // Increment the frequency
35 | await sharedStorage.set('frequency-count', count + 1);
36 | return 1;
37 | }
38 | }
39 |
40 | // Register the operation
41 | register('creative-selection-by-frequency', CreativeSelectionByFrequencyOperation);
42 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/creative-selection-by-frequency.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
Demo control
45 |
46 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/creative-selection-by-frequency.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // For demo purposes. The hostname is used to determine the usage of
18 | // development localhost URL vs production URL
19 | const contentProducerUrl = window.location.host;
20 |
21 | const AD_URLS = [
22 | { url: `https://${contentProducerUrl}/ads/default-ad.html` },
23 | { url: `https://${contentProducerUrl}/ads/example-ad.html` },
24 | ];
25 |
26 | async function injectAd() {
27 | // Load the worklet module
28 | await window.sharedStorage.worklet.addModule('creative-selection-by-frequency-worklet.js');
29 |
30 | // Set the initial frequency cap to 5
31 | window.sharedStorage.set('frequency-count', 0, {
32 | ignoreIfPresent: true,
33 | });
34 |
35 | // Resolve the selectURL call to a fenced frame config only when it exists on the page
36 | const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
37 |
38 | // Run the URL selection operation to choose an ad based on the frequency cap in shared storage
39 | const selectedUrl = await window.sharedStorage.selectURL('creative-selection-by-frequency', AD_URLS, {
40 | resolveToConfig
41 | });
42 |
43 | const adSlot = document.getElementById('ad-slot');
44 |
45 | if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
46 | adSlot.config = selectedUrl;
47 | } else {
48 | adSlot.src = selectedUrl;
49 | }
50 | }
51 |
52 | injectAd();
53 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/hover-event-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // This code is loaded as a Shared Storage worklet
18 | class SelectURLOperation {
19 | async run() {
20 | return 0;
21 | }
22 | }
23 |
24 | // Register the operation as 'hover-event'
25 | register('hover-event', SelectURLOperation);
26 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/hover-event.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/hover-event.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // For demo purposes. The hostname is used to determine the usage of
18 | // development localhost URL vs production URL
19 | const CONTENT_ID = 1234;
20 | const contentProducerUrl = window.location.host;
21 | const AD_URLS = [
22 | {
23 | url: `https://${contentProducerUrl}/ads/hover-ad.html`,
24 | reportingMetadata: {
25 | hover: `https://${contentProducerUrl}/report/hover?contentId=${CONTENT_ID}`
26 | }
27 | }
28 | ];
29 |
30 | async function injectAd() {
31 | // Load the worklet module
32 | await window.sharedStorage.worklet.addModule('hover-event-worklet.js');
33 |
34 | // Resolve the selectURL call to a fenced frame config only when it exists on the page
35 | const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
36 |
37 | // Run the URL selection operation to select an ad based on the experiment group in shared storage
38 | const selectedUrl = await window.sharedStorage.selectURL('hover-event', AD_URLS, {
39 | resolveToConfig,
40 | });
41 |
42 | const adSlot = document.getElementById('ad-slot');
43 |
44 | if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
45 | adSlot.config = selectedUrl;
46 | } else {
47 | adSlot.src = selectedUrl;
48 | }
49 | }
50 |
51 | injectAd();
52 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/known-customer-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | class SelectURLOperation {
18 | async run(urls) {
19 | const knownCustomer = await sharedStorage.get('known-customer');
20 |
21 | console.log(`urls = ${JSON.stringify(urls)}`);
22 | console.log(`known-customer value is ${knownCustomer}`);
23 |
24 | // '0' is unknown and '1' is known
25 | return parseInt(knownCustomer);
26 | }
27 | }
28 |
29 | // Register the operation as 'known-customer'
30 | register('known-customer', SelectURLOperation);
31 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/known-customer.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Shared storage demo
24 |
25 |
26 |
27 |
28 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/known-customer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // For demo purposes. The hostname is used to determine the usage of
18 | // development localhost URL vs production URL
19 | const contentProducerUrl = window.location.host;
20 |
21 | // The first URL is the "register" button to be rendered if the user is not known
22 | const AD_URLS = [
23 | { url: `https://${contentProducerUrl}/ads/register-button.html` },
24 | { url: `https://${contentProducerUrl}/ads/buy-now-button.html` },
25 | ];
26 |
27 | async function injectAd() {
28 | // Load the worklet module
29 | await window.sharedStorage.worklet.addModule('known-customer-worklet.js');
30 |
31 | // Set the initial status to unknown ('0' is unknown and '1' is known)
32 | window.sharedStorage.set('known-customer', 0, {
33 | ignoreIfPresent: true,
34 | });
35 |
36 | // Resolve the selectURL call to a fenced frame config only when it exists on the page
37 | const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
38 |
39 | // Run the URL selection operation to choose the button based on the user status
40 | const selectedUrl = await window.sharedStorage.selectURL('known-customer', AD_URLS, {
41 | resolveToConfig,
42 | });
43 |
44 | const adSlot = document.getElementById('button-slot');
45 |
46 | if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
47 | adSlot.config = selectedUrl;
48 | } else {
49 | adSlot.src = selectedUrl;
50 | }
51 | }
52 |
53 | injectAd();
54 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/top-level-nav-worklet.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // This code is loaded as a Shared Storage worklet
18 | class SelectURLOperation {
19 | async run() {
20 | return 0;
21 | }
22 | }
23 |
24 | register('top-level-nav', SelectURLOperation);
25 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/top-level-nav.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/sites/content-producer/url-selection/top-level-nav.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // For demo purposes. The hostname is used to determine the usage of
18 | // development localhost URL vs production URL
19 | const contentProducerUrl = window.location.host;
20 | const AD_URLS = [
21 | { url: `https://${contentProducerUrl}/ads/nav-ad-1.html` },
22 | { url: `https://${contentProducerUrl}/ads/nav-ad-2.html` },
23 | { url: `https://${contentProducerUrl}/ads/nav-ad-3.html` },
24 | ];
25 |
26 | async function injectAd() {
27 | // Load the worklet module
28 | await window.sharedStorage.worklet.addModule('top-level-nav-worklet.js');
29 |
30 | // Resolve the selectURL call to a fenced frame config only when it exists on the page
31 | const resolveToConfig = typeof window.FencedFrameConfig !== 'undefined';
32 |
33 | // Run the URL selection operation to select an ad based on the experiment group in shared storage
34 | const selectedUrl = await window.sharedStorage.selectURL('top-level-nav', AD_URLS, {
35 | resolveToConfig,
36 | });
37 |
38 | const adSlot = document.getElementById('ad-slot');
39 |
40 | if (resolveToConfig && selectedUrl instanceof FencedFrameConfig) {
41 | adSlot.config = selectedUrl;
42 | } else {
43 | adSlot.src = selectedUrl;
44 | }
45 | }
46 |
47 | injectAd();
48 |
--------------------------------------------------------------------------------
/sites/content-producer/use-cases/third-party-write.html:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | Shared storage demo
23 |
24 |
25 |
26 |
27 |
31 |
32 |
43 |
44 |
45 |
46 |
The specified file was not found on this website. Check the URL for mistakes and try again.
99 |
Why am I seeing this?
100 |
101 | This page was generated by the Firebase Command-Line Interface. To modify it, edit the
102 | 404.html file in your project's configured public directory.
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/sites/home/assets/privacy-sandbox-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GoogleChromeLabs/shared-storage-demo/3f194de67c956ee421237cc8a402f575993c13de/sites/home/assets/privacy-sandbox-logo.png
--------------------------------------------------------------------------------
/sites/home/decoder/decoder.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | .decoder__input {
17 | width: 100%;
18 | }
19 |
20 | .decoder__run {
21 | margin-top: var(--size-4);
22 | }
23 |
24 | .decoder__output-container {
25 | margin-top: var(--size-4);
26 | }
27 |
28 | .decoder__output-table th {
29 | padding: var(--size-2);
30 | /* width: 40%; */
31 | }
32 |
33 | .decoder__output-table td {
34 | text-align: center;
35 | /* max-width: 150px; */
36 | overflow-wrap: anywhere;
37 | padding: 0 20px;
38 | }
39 |
--------------------------------------------------------------------------------
/sites/home/decoder/decoder.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2022 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Placeholder for 8-bits to be used for padding
18 | const ZEROES_FOR_PADDING = '00000000';
19 |
20 | function renderOutput({ bucketInBinary, valueInBinary }) {
21 | document.querySelector('.decoder__bucket--binary').innerHTML = bucketInBinary;
22 | document.querySelector('.decoder__bucket--decimal').innerHTML = BigInt(`0b${bucketInBinary}`);
23 | document.querySelector('.decoder__value--binary').innerHTML = valueInBinary;
24 | document.querySelector('.decoder__value--decimal').innerHTML = BigInt(`0b${valueInBinary}`);
25 | }
26 |
27 | // Converts the number to a binary string
28 | function covertToBinary(input) {
29 | return input
30 | // Filter out 0s
31 | .filter((n) => n)
32 | // Converts number to binary, and moves it to an array from a typed array to an array
33 | .reduce((acc, n) => [...acc, n.toString(2)], [])
34 | // Pad the left of the value and make it a length of 8
35 | .map((n) => ZEROES_FOR_PADDING.slice(0, 8 - n.length) + n)
36 | .join('');
37 | }
38 |
39 | async function decodePayload(payload) {
40 | // Base64 decode
41 | const arr = new Uint8Array([...atob(payload)]
42 | .map((c) => c.charCodeAt(0)));
43 |
44 | // CBOR decode
45 | const {
46 | data: [{ bucket, value }],
47 | } = await cbor.decodeFirst(arr);
48 |
49 | return {
50 | bucketInBinary: covertToBinary(bucket),
51 | valueInBinary: covertToBinary(value),
52 | };
53 | }
54 |
55 | async function runDecoder() {
56 | const payload = document.querySelector('.decoder__input').value;
57 |
58 | if (!payload) {
59 | return;
60 | }
61 |
62 | const output = await decodePayload(payload);
63 | renderOutput(output);
64 | }
65 |
--------------------------------------------------------------------------------
/sites/home/decoder/index.html:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | Debug payload decoder
25 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
Debug payload decoder for the Private Aggregation API