diff --git a/services/ad-server/src/public/js/ad-server-lib.js b/services/ad-server/src/public/js/ad-server-lib.js
index 2ca534fb..c6a396af 100644
--- a/services/ad-server/src/public/js/ad-server-lib.js
+++ b/services/ad-server/src/public/js/ad-server-lib.js
@@ -192,7 +192,7 @@ class AdServerLib {
paragraphEl.className = 'font-mono text-sm';
document.getElementById(divId).appendChild(paragraphEl);
- if (type === 'image') {
+ if (type === 'image' || type === 'multi-piece') {
[containerFrameEl.width, containerFrameEl.height] = size;
} else if (type === 'video') {
// The video creative does not actually render anything, and it only contains
diff --git a/services/ad-server/src/public/js/decision-logic.js b/services/ad-server/src/public/js/decision-logic.js
index 061080ae..d6f3f995 100644
--- a/services/ad-server/src/public/js/decision-logic.js
+++ b/services/ad-server/src/public/js/decision-logic.js
@@ -26,7 +26,10 @@ function scoreAd(
// If it's a video ad, there is no contextual video auction in this demo to compare
// the bid against, so we just return the desirability score as the bid itself,
// and the highest bid will win the auction
- if (auctionConfig.sellerSignals.adType === 'video') {
+ if (
+ auctionConfig.sellerSignals.adType === 'video' ||
+ auctionConfig.sellerSignals.adType === 'multi-piece'
+ ) {
desirability = bid;
} else {
// For an image ad, we compare the PA auction bid against the bid floor set by the
diff --git a/services/dsp-a/src/index.ts b/services/dsp-a/src/index.ts
index b48deb94..d7f9cfc6 100644
--- a/services/dsp-a/src/index.ts
+++ b/services/dsp-a/src/index.ts
@@ -80,6 +80,14 @@ app.use((req, res, next) => {
next();
});
+app.use((req, res, next) => {
+ // opt-in fencedframe
+ if (req.get('sec-fetch-dest') === 'fencedframe') {
+ res.setHeader('Supports-Loading-Mode', 'fenced-frame');
+ }
+ next();
+});
+
app.use(
express.static('src/public', {
setHeaders: (res: Response, path, stat) => {
@@ -94,14 +102,6 @@ app.use(
}),
);
-app.use((req, res, next) => {
- // opt-in fencedframe
- if (req.get('sec-fetch-dest') === 'fencedframe') {
- res.setHeader('Supports-Loading-Mode', 'fenced-frame');
- }
- next();
-});
-
app.set('view engine', 'ejs');
app.set('views', 'src/views');
@@ -144,6 +144,25 @@ app.get('/interest-group.json', async (req: Request, res: Response) => {
imageCreative.searchParams.append('advertiser', advertiser as string);
imageCreative.searchParams.append('id', id as string);
+ const multiPieceCreativeContainer = new URL(
+ `https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/container.html`,
+ );
+ const multiPieceCreativeComponentA = new URL(
+ `https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-a.html`,
+ );
+ const multiPieceCreativeComponentB = new URL(
+ `https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-b.html`,
+ );
+ const multiPieceCreativeComponentC = new URL(
+ `https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-c.html`,
+ );
+ const multiPieceCreativeComponentD = new URL(
+ `https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-d.html`,
+ );
+ const multiPieceCreativeComponentE = new URL(
+ `https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-e.html`,
+ );
+
const videoCreativeForSspA = new URL(
`https://${DSP_A_HOST}:${EXTERNAL_PORT}/html/video-ad-creative.html?sspVastUrl=${SSP_A_VAST_URL}`,
);
@@ -207,6 +226,19 @@ app.get('/interest-group.json', async (req: Request, res: Response) => {
seller: SSP_B,
},
},
+ {
+ renderUrl: multiPieceCreativeContainer,
+ metadata: {
+ adType: 'multi-piece',
+ },
+ },
+ ],
+ adComponents: [
+ {renderUrl: multiPieceCreativeComponentA},
+ {renderUrl: multiPieceCreativeComponentB},
+ {renderUrl: multiPieceCreativeComponentC},
+ {renderUrl: multiPieceCreativeComponentD},
+ {renderUrl: multiPieceCreativeComponentE},
],
});
});
diff --git a/services/dsp-a/src/public/html/multi-piece-ad/component-a.html b/services/dsp-a/src/public/html/multi-piece-ad/component-a.html
new file mode 100644
index 00000000..1843987c
--- /dev/null
+++ b/services/dsp-a/src/public/html/multi-piece-ad/component-a.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-A component ad A
+
+
+
+
+ A
+
+
diff --git a/services/dsp-a/src/public/html/multi-piece-ad/component-b.html b/services/dsp-a/src/public/html/multi-piece-ad/component-b.html
new file mode 100644
index 00000000..0eeb8559
--- /dev/null
+++ b/services/dsp-a/src/public/html/multi-piece-ad/component-b.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-A component ad B
+
+
+
+
+ B
+
+
diff --git a/services/dsp-a/src/public/html/multi-piece-ad/component-c.html b/services/dsp-a/src/public/html/multi-piece-ad/component-c.html
new file mode 100644
index 00000000..1b1b21c7
--- /dev/null
+++ b/services/dsp-a/src/public/html/multi-piece-ad/component-c.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-A component ad C
+
+
+
+
+ C
+
+
diff --git a/services/dsp-a/src/public/html/multi-piece-ad/component-d.html b/services/dsp-a/src/public/html/multi-piece-ad/component-d.html
new file mode 100644
index 00000000..d67fbc0a
--- /dev/null
+++ b/services/dsp-a/src/public/html/multi-piece-ad/component-d.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-A component ad D
+
+
+
+
+ D
+
+
diff --git a/services/dsp-a/src/public/html/multi-piece-ad/component-e.html b/services/dsp-a/src/public/html/multi-piece-ad/component-e.html
new file mode 100644
index 00000000..2e26ff53
--- /dev/null
+++ b/services/dsp-a/src/public/html/multi-piece-ad/component-e.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-A component ad E
+
+
+
+
+ E
+
+
diff --git a/services/dsp-a/src/public/html/multi-piece-ad/container.html b/services/dsp-a/src/public/html/multi-piece-ad/container.html
new file mode 100644
index 00000000..42fc3933
--- /dev/null
+++ b/services/dsp-a/src/public/html/multi-piece-ad/container.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+ DSP-A ad container
+
+
+
+
+
+
DSP-A multi-piece ad
+
+
Fenced frame components
+
+
+
+
+
+
diff --git a/services/dsp-a/src/public/js/bidding-logic.js b/services/dsp-a/src/public/js/bidding-logic.js
index 1a67b6a8..43dfead0 100644
--- a/services/dsp-a/src/public/js/bidding-logic.js
+++ b/services/dsp-a/src/public/js/bidding-logic.js
@@ -14,6 +14,10 @@
limitations under the License.
*/
+const IMAGE_AD_TYPE = 'image';
+const VIDEO_AD_TYPE = 'video';
+const MULTI_PIECE_AD_TYPE = 'multi-piece';
+
function generateBid(
interestGroup,
auctionSignals,
@@ -21,31 +25,40 @@ function generateBid(
trustedBiddingSignals,
browserSignals,
) {
- const {ads} = interestGroup;
+ const {ads, adComponents} = interestGroup;
const {adType} = auctionSignals;
const {seller, topLevelSeller} = browserSignals;
let render;
- if (adType === 'image') {
- render = ads.find((ad) => ad.metadata.adType === 'image')?.renderUrl;
- } else if (adType === 'video') {
- // We look through the video ads passed in from the interest group and
- // select the ad that matches the component seller's origin
- render = ads.find(
- (ad) =>
- ad.metadata.adType === 'video' && ad.metadata.seller.includes(seller),
- ).renderUrl;
+ switch (adType) {
+ case IMAGE_AD_TYPE:
+ render = ads.find(
+ ({metadata}) => metadata.adType === IMAGE_AD_TYPE,
+ )?.renderUrl;
+ break;
+ case VIDEO_AD_TYPE:
+ // We look through the video ads passed in from the interest group and
+ // select the ad that matches the component seller's origin
+ render = ads.find(
+ ({metadata}) =>
+ metadata.adType === VIDEO_AD_TYPE && metadata.seller.includes(seller),
+ ).renderUrl;
+ break;
+ case MULTI_PIECE_AD_TYPE:
+ render = ads.find(
+ ({metadata}) => metadata.adType === MULTI_PIECE_AD_TYPE,
+ ).renderUrl;
+ break;
}
- const response = {
+ return {
// We return a random bid of 0 to 100
bid: Math.floor(Math.random() * 100, 10),
render,
+ adComponents: adComponents.map(({renderUrl}) => ({url: renderUrl})),
allowComponentAuction: !!topLevelSeller,
};
-
- return response;
}
function reportWin(
diff --git a/services/dsp-b/src/index.ts b/services/dsp-b/src/index.ts
index c5bc829e..395f8d6c 100644
--- a/services/dsp-b/src/index.ts
+++ b/services/dsp-b/src/index.ts
@@ -80,6 +80,14 @@ app.use((req, res, next) => {
next();
});
+app.use((req, res, next) => {
+ // opt-in fencedframe
+ if (req.get('sec-fetch-dest') === 'fencedframe') {
+ res.setHeader('Supports-Loading-Mode', 'fenced-frame');
+ }
+ next();
+});
+
app.use(
express.static('src/public', {
setHeaders: (res: Response, path, stat) => {
@@ -94,14 +102,6 @@ app.use(
}),
);
-app.use((req, res, next) => {
- // opt-in fencedframe
- if (req.get('sec-fetch-dest') === 'fencedframe') {
- res.setHeader('Supports-Loading-Mode', 'fenced-frame');
- }
- next();
-});
-
app.set('view engine', 'ejs');
app.set('views', 'src/views');
@@ -144,6 +144,25 @@ app.get('/interest-group.json', async (req: Request, res: Response) => {
imageCreative.searchParams.append('advertiser', advertiser as string);
imageCreative.searchParams.append('id', id as string);
+ const multiPieceCreativeContainer = new URL(
+ `https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/container.html`,
+ );
+ const multiPieceCreativeComponentA = new URL(
+ `https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-1.html`,
+ );
+ const multiPieceCreativeComponentB = new URL(
+ `https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-2.html`,
+ );
+ const multiPieceCreativeComponentC = new URL(
+ `https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-3.html`,
+ );
+ const multiPieceCreativeComponentD = new URL(
+ `https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-4.html`,
+ );
+ const multiPieceCreativeComponentE = new URL(
+ `https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/multi-piece-ad/component-5.html`,
+ );
+
const videoCreativeForSspA = new URL(
`https://${DSP_B_HOST}:${EXTERNAL_PORT}/html/video-ad-creative.html?sspVastUrl=${SSP_A_VAST_URL}`,
);
@@ -207,6 +226,19 @@ app.get('/interest-group.json', async (req: Request, res: Response) => {
seller: SSP_B,
},
},
+ {
+ renderUrl: multiPieceCreativeContainer,
+ metadata: {
+ adType: 'multi-piece',
+ },
+ },
+ ],
+ adComponents: [
+ {renderUrl: multiPieceCreativeComponentA},
+ {renderUrl: multiPieceCreativeComponentB},
+ {renderUrl: multiPieceCreativeComponentC},
+ {renderUrl: multiPieceCreativeComponentD},
+ {renderUrl: multiPieceCreativeComponentE},
],
});
});
diff --git a/services/dsp-b/src/public/html/multi-piece-ad/component-1.html b/services/dsp-b/src/public/html/multi-piece-ad/component-1.html
new file mode 100644
index 00000000..471bca71
--- /dev/null
+++ b/services/dsp-b/src/public/html/multi-piece-ad/component-1.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-B component ad 1
+
+
+
+
+ 1
+
+
diff --git a/services/dsp-b/src/public/html/multi-piece-ad/component-2.html b/services/dsp-b/src/public/html/multi-piece-ad/component-2.html
new file mode 100644
index 00000000..3d8ba0e6
--- /dev/null
+++ b/services/dsp-b/src/public/html/multi-piece-ad/component-2.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-B component ad 2
+
+
+
+
+ 2
+
+
diff --git a/services/dsp-b/src/public/html/multi-piece-ad/component-3.html b/services/dsp-b/src/public/html/multi-piece-ad/component-3.html
new file mode 100644
index 00000000..cf077b93
--- /dev/null
+++ b/services/dsp-b/src/public/html/multi-piece-ad/component-3.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-B component ad 3
+
+
+
+
+ 3
+
+
diff --git a/services/dsp-b/src/public/html/multi-piece-ad/component-4.html b/services/dsp-b/src/public/html/multi-piece-ad/component-4.html
new file mode 100644
index 00000000..9a9f81d1
--- /dev/null
+++ b/services/dsp-b/src/public/html/multi-piece-ad/component-4.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-B component ad 4
+
+
+
+
+ 4
+
+
diff --git a/services/dsp-b/src/public/html/multi-piece-ad/component-5.html b/services/dsp-b/src/public/html/multi-piece-ad/component-5.html
new file mode 100644
index 00000000..85f8d4c0
--- /dev/null
+++ b/services/dsp-b/src/public/html/multi-piece-ad/component-5.html
@@ -0,0 +1,21 @@
+
+
+
+
+
+ DSP-B component ad 5
+
+
+
+
+ 5
+
+
diff --git a/services/dsp-b/src/public/html/multi-piece-ad/container.html b/services/dsp-b/src/public/html/multi-piece-ad/container.html
new file mode 100644
index 00000000..4a8cee56
--- /dev/null
+++ b/services/dsp-b/src/public/html/multi-piece-ad/container.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+ DSP-B ad container
+
+
+
+
+
+
DSP-B multi-piece ad
+
+
Fenced frame components
+
+
+
+
+
+
diff --git a/services/dsp-b/src/public/js/bidding-logic.js b/services/dsp-b/src/public/js/bidding-logic.js
index 1a67b6a8..43dfead0 100644
--- a/services/dsp-b/src/public/js/bidding-logic.js
+++ b/services/dsp-b/src/public/js/bidding-logic.js
@@ -14,6 +14,10 @@
limitations under the License.
*/
+const IMAGE_AD_TYPE = 'image';
+const VIDEO_AD_TYPE = 'video';
+const MULTI_PIECE_AD_TYPE = 'multi-piece';
+
function generateBid(
interestGroup,
auctionSignals,
@@ -21,31 +25,40 @@ function generateBid(
trustedBiddingSignals,
browserSignals,
) {
- const {ads} = interestGroup;
+ const {ads, adComponents} = interestGroup;
const {adType} = auctionSignals;
const {seller, topLevelSeller} = browserSignals;
let render;
- if (adType === 'image') {
- render = ads.find((ad) => ad.metadata.adType === 'image')?.renderUrl;
- } else if (adType === 'video') {
- // We look through the video ads passed in from the interest group and
- // select the ad that matches the component seller's origin
- render = ads.find(
- (ad) =>
- ad.metadata.adType === 'video' && ad.metadata.seller.includes(seller),
- ).renderUrl;
+ switch (adType) {
+ case IMAGE_AD_TYPE:
+ render = ads.find(
+ ({metadata}) => metadata.adType === IMAGE_AD_TYPE,
+ )?.renderUrl;
+ break;
+ case VIDEO_AD_TYPE:
+ // We look through the video ads passed in from the interest group and
+ // select the ad that matches the component seller's origin
+ render = ads.find(
+ ({metadata}) =>
+ metadata.adType === VIDEO_AD_TYPE && metadata.seller.includes(seller),
+ ).renderUrl;
+ break;
+ case MULTI_PIECE_AD_TYPE:
+ render = ads.find(
+ ({metadata}) => metadata.adType === MULTI_PIECE_AD_TYPE,
+ ).renderUrl;
+ break;
}
- const response = {
+ return {
// We return a random bid of 0 to 100
bid: Math.floor(Math.random() * 100, 10),
render,
+ adComponents: adComponents.map(({renderUrl}) => ({url: renderUrl})),
allowComponentAuction: !!topLevelSeller,
};
-
- return response;
}
function reportWin(
diff --git a/services/news/src/views/index.ejs b/services/news/src/views/index.ejs
index 1499a141..c790fd82 100644
--- a/services/news/src/views/index.ejs
+++ b/services/news/src/views/index.ejs
@@ -56,6 +56,10 @@
<%= lorem %>
+
<%= lorem %>
@@ -165,6 +169,7 @@
size: [300, 250],
isFencedFrame: true
}
+
const videoIframeAdUnit = {
divId: 'video-ad--iframe',
type: 'video',
@@ -172,10 +177,17 @@
isFencedFrame: false
}
+ const multiPieceFencedFrameAdUnit = {
+ divId: 'image-ad__multi-piece--fenced-frame',
+ type: 'multi-piece',
+ size: [300, 250],
+ isFencedFrame: true
+ }
// Start the auction for each ad unit
startSequentialAuction(imageIframeAdUnit, sellers)
startSequentialAuction(imageFencedFrameAdUnit, sellers)
startSequentialAuction(videoIframeAdUnit, sellers)
+ startSequentialAuction(multiPieceFencedFrameAdUnit, sellers)
}
async function setupDemo () {