Skip to content

Commit

Permalink
Some changes to help building ads. (#2908)
Browse files Browse the repository at this point in the history
- Add a startTime to the context given to ads.
- Move getAdCid to a separate function, rather than a method of AmpAd.
- Split out ad-cid tests from amp-ad tests.
- Fix a typo.
  • Loading branch information
bobcassels authored and cramforce committed Apr 19, 2016
1 parent 21c1272 commit 74b9daf
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 206 deletions.
1 change: 1 addition & 0 deletions ads/README.md
Expand Up @@ -38,6 +38,7 @@ We will provide the following information to the ad:
- Only available on pages that load `amp-analytics`. The clientId will be null if `amp-analytics` was not loaded on the given page.
- `window.context.pageViewId` contains a relatively low entropy id that is the same for all ads shown on a page.
- [ad viewability](#ad-viewability)
- `window.context.startTime` contains the time at which processing of the amp-ad element started.

More information can be provided in a similar fashion if needed (Please file an issue).

Expand Down
2 changes: 1 addition & 1 deletion build-system/tasks/presubmit-checks.js
Expand Up @@ -155,7 +155,7 @@ var forbiddenTerms = {
'cidFor': {
message: requiresReviewPrivacy,
whitelist: [
'builtins/amp-ad.js',
'src/ad-cid.js',
'src/cid.js',
'src/service/cid-impl.js',
'src/url-replacements.js',
Expand Down
37 changes: 3 additions & 34 deletions builtins/amp-ad.js
Expand Up @@ -16,17 +16,16 @@

import {BaseElement} from '../src/base-element';
import {IntersectionObserver} from '../src/intersection-observer';
import {cidForOrNull} from '../src/cid';
import {getAdCid} from '../src/ad-cid';
import {getIframe, prefetchBootstrap} from '../src/3p-frame';
import {isLayoutSizeDefined} from '../src/layout';
import {listen, listenOnce, postMessage} from '../src/iframe-helper';
import {loadPromise} from '../src/event-helper';
import {parseUrl} from '../src/url';
import {registerElement} from '../src/custom-element';
import {adPrefetch, adPreconnect, clientIdScope} from '../ads/_config';
import {adPrefetch, adPreconnect} from '../ads/_config';
import {timer} from '../src/timer';
import {user} from '../src/log';
import {userNotificationManagerFor} from '../src/user-notification';
import {viewerFor} from '../src/viewer';


Expand Down Expand Up @@ -234,7 +233,7 @@ export function installAd(win) {
// now.
loadingAdsCount--;
}, 1000);
return this.getAdCid_().then(cid => {
return getAdCid(this).then(cid => {
if (cid) {
this.element.setAttribute('ampcid', cid);
}
Expand Down Expand Up @@ -286,36 +285,6 @@ export function installAd(win) {
return loadPromise(this.iframe_);
}

/**
* @return {!Promise<string|undefined>} A promise for a CID or undefined if
* - the ad network does not request one or
* - `amp-analytics` which provides the CID service was not installed.
* @private
*/
getAdCid_() {
const scope = clientIdScope[this.element.getAttribute('type')];
const consentId = this.element.getAttribute(
'data-consent-notification-id');
if (!(scope || consentId)) {
return Promise.resolve();
}
return cidForOrNull(this.getWin()).then(cidService => {
if (!cidService) {
return;
}
let consent = Promise.resolve();
if (consentId) {
consent = userNotificationManagerFor(this.getWin()).then(service => {
return service.get(consentId);
});
if (!scope && consentId) {
return consent;
}
}
return cidService.get(scope, consent);
});
}

/** @override */
viewportCallback(inViewport) {
if (this.intersectionObserver_) {
Expand Down
2 changes: 2 additions & 0 deletions src/3p-frame.js
Expand Up @@ -45,6 +45,7 @@ const count = {};
* - A _context object for internal use.
*/
function getFrameAttributes(parentWindow, element, opt_type) {
const startTime = timer.now();
const width = element.getAttribute('width');
const height = element.getAttribute('height');
const type = opt_type || element.getAttribute('type');
Expand Down Expand Up @@ -79,6 +80,7 @@ function getFrameAttributes(parentWindow, element, opt_type) {
timer.now(),
viewportFor(parentWindow).getRect(),
element.getLayoutBox()),
startTime: startTime,
};
const adSrc = element.getAttribute('src');
if (adSrc) {
Expand Down
50 changes: 50 additions & 0 deletions src/ad-cid.js
@@ -0,0 +1,50 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {cidForOrNull} from './cid';
import {clientIdScope} from '../ads/_config';
import {userNotificationManagerFor} from './user-notification';


/**
* @param {BaseElement} adElement
* @return {!Promise<string|undefined>} A promise for a CID or undefined if
* - the ad network does not request one or
* - `amp-analytics` which provides the CID service was not installed.
*/
export function getAdCid(adElement) {
const scope = clientIdScope[adElement.element.getAttribute('type')];
const consentId = adElement.element.getAttribute(
'data-consent-notification-id');
if (!(scope || consentId)) {
return Promise.resolve();
}
return cidForOrNull(adElement.getWin()).then(cidService => {
if (!cidService) {
return;
}
let consent = Promise.resolve();
if (consentId) {
consent = userNotificationManagerFor(adElement.getWin()).then(service => {
return service.get(consentId);
});
if (!scope && consentId) {
return consent;
}
}
return cidService.get(scope, consent);
});
}
2 changes: 1 addition & 1 deletion src/custom-element.js
Expand Up @@ -1125,7 +1125,7 @@ export function createAmpElementProto(win, name, implementationClass) {
if (!this.overflowElement_) {
if (overflown) {
user.warn(TAG_,
'Cannot resize element and overlfow is not available', this);
'Cannot resize element and overflow is not available', this);
}
} else {
this.overflowElement_.classList.toggle('amp-visible', overflown);
Expand Down
2 changes: 1 addition & 1 deletion test/functional/test-3p-frame.js
Expand Up @@ -124,7 +124,7 @@ describe('3p-frame', () => {
',"x":0,"y":0},"boundingClientRect":' +
'{"width":100,"height":200},"intersectionRect":{' +
'"left":0,"top":0,"width":0,"height":0,"bottom":0,' +
'"right":0,"x":0,"y":0}}}}';
'"right":0,"x":0,"y":0}},"startTime":1234567888}}';
expect(src).to.equal(
'http://ads.localhost:9876/dist.3p/current/frame.max.html' +
fragment);
Expand Down
172 changes: 172 additions & 0 deletions test/functional/test-ad-cid.js
@@ -0,0 +1,172 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import {clientIdScope} from '../../ads/_config';
import {createAdPromise} from '../../testing/ad-iframe';
import {installAd} from '../../builtins/amp-ad';
import {installEmbed} from '../../builtins/amp-embed';
import {installCidService} from '../../src/service/cid-impl';
import {
installUserNotificationManager,
} from '../../extensions/amp-user-notification/0.1/amp-user-notification';
import {setCookie} from '../../src/cookies';
import * as sinon from 'sinon';


describe('ad-cid', tests('amp-ad', installAd));
describe('ad-cid-embed', tests('amp-embed', win => {
installAd(win);
installEmbed(win);
}));

function tests(name, installer) {
function getAd(attributes, canonical, opt_handleElement,
opt_beforeLayoutCallback) {
return createAdPromise(name, installer, attributes, canonical,
opt_handleElement, opt_beforeLayoutCallback);
}

return () => {
describe('cid-ad support', () => {
const cidScope = 'cid-in-ads-test';
let sandbox;

beforeEach(() => {
sandbox = sinon.sandbox.create();
});

afterEach(() => {
sandbox.restore();
delete clientIdScope['with_cid'];
setCookie(window, cidScope, '', new Date().getTime() - 5000);
});

it('provides cid to ad', () => {
clientIdScope['with_cid'] = cidScope;
return getAd({
width: 300,
height: 250,
type: 'with_cid',
src: 'testsrc',
}, 'https://schema.org', function(ad) {
const win = ad.ownerDocument.defaultView;
setCookie(window, cidScope, 'sentinel123',
new Date().getTime() + 5000);
installCidService(win);
return ad;
}).then(ad => {
expect(ad.getAttribute('ampcid')).to.equal('sentinel123');
});
});

it('waits for consent', () => {
clientIdScope['with_cid'] = cidScope;
return getAd({
width: 300,
height: 250,
type: 'with_cid',
src: 'testsrc',
'data-consent-notification-id': 'uid',
}, 'https://schema.org', function(ad) {
const win = ad.ownerDocument.defaultView;
const cidService = installCidService(win);
const uidService = installUserNotificationManager(win);
sandbox.stub(uidService, 'get', id => {
expect(id).to.equal('uid');
return Promise.resolve('consent');
});
sandbox.stub(cidService, 'get', (scope, consent) => {
expect(scope).to.equal(cidScope);
return consent.then(val => {
return val + '-cid';
});
});
return ad;
}).then(ad => {
expect(ad.getAttribute('ampcid')).to.equal('consent-cid');
});
});

it('waits for consent w/o cidScope', () => {
return getAd({
width: 300,
height: 250,
type: 'with_cid',
src: 'testsrc',
'data-consent-notification-id': 'uid',
}, 'https://schema.org', function(ad) {
const win = ad.ownerDocument.defaultView;
const cidService = installCidService(win);
const uidService = installUserNotificationManager(win);
sandbox.stub(uidService, 'get', id => {
expect(id).to.equal('uid');
return Promise.resolve('consent');
});
sandbox.stub(cidService, 'get', (scope, consent) => {
expect(scope).to.equal(cidScope);
return consent.then(val => {
return val + '-cid';
});
});
return ad;
}).then(ad => {
expect(ad.getAttribute('ampcid')).to.equal('consent');
});
});

it('provide null if notification and cid is not provided', () => {
let uidSpy = null;
return getAd({
width: 300,
height: 250,
type: 'with_cid',
src: 'testsrc',
}, 'https://schema.org', function(ad) {
const win = ad.ownerDocument.defaultView;
const cidService = installCidService(win);
const uidService = installUserNotificationManager(win);
uidSpy = sandbox.spy(uidService, 'get');
sandbox.stub(cidService, 'get', (scope, consent) => {
expect(scope).to.equal(cidScope);
return consent.then(val => {
return val + '-cid';
});
});
return ad;
}).then(ad => {
expect(uidSpy.callCount).to.equal(0);
expect(ad.getAttribute('ampcid')).to.be.null;
});
});

it('provides null if cid service not available', () => {
clientIdScope['with_cid'] = cidScope;
return getAd({
width: 300,
height: 250,
type: 'with_cid',
src: 'testsrc',
}, 'https://schema.org', function(ad) {
setCookie(window, cidScope, 'XXX',
new Date().getTime() + 5000);
return ad;
}).then(ad => {
expect(ad.getAttribute('ampcid')).to.be.null;
});
});
});
};
}

0 comments on commit 74b9daf

Please sign in to comment.