Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AMP Ad Refresh #9535

Merged
merged 92 commits into from Jul 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
92 commits
Select commit Hold shift + click to select a range
b1f409f
Initial commit.
glevitzky May 4, 2017
f6ccd65
Work on refresher.
glevitzky May 5, 2017
7b0bba6
RefreshManager v0 complete.
glevitzky May 8, 2017
dc2c128
Tests for refresh-manager.
glevitzky May 8, 2017
bab9dd8
Modified RefreshManager to support multiple IOs, each configured with…
glevitzky May 17, 2017
10290d3
Removed test file.
glevitzky May 17, 2017
a0eb2b1
Comment update.
glevitzky May 17, 2017
b2dd090
Added refresh logic to amp-a4a.
glevitzky May 18, 2017
f52b639
Removed test page.
glevitzky May 18, 2017
f74863e
Removed test page (for real this time).
glevitzky May 18, 2017
7e91124
Fixed issue with loader persisting; no-fills continue to show previou…
glevitzky May 18, 2017
f524990
Addressing PR feedback: mostly annotation fixes.
glevitzky May 25, 2017
5289054
Drastically simplified RefreshManager with analytics' visibility mana…
glevitzky May 25, 2017
23b08da
Added config parsing + example page.
glevitzky May 31, 2017
7d0a3d8
No longer calling lifecycle methods directly.
glevitzky May 31, 2017
6ce28f4
Made adPomise_ private again. Fixed comment.
glevitzky May 31, 2017
56a4ab7
No longer calling layoutCallback directly.
glevitzky Jun 1, 2017
e1b4d2f
Removed loop from getRefreshConfiguration().
glevitzky Jun 1, 2017
fba1588
Removed backup file.
glevitzky Jun 1, 2017
fc6facd
Shuffled code around from AmpA4A/doubleclick-impl to refresh-manager.
glevitzky Jun 1, 2017
9a21ca3
Comment.
glevitzky Jun 1, 2017
f286d41
Changed how refresh cycle is restarted.
glevitzky Jun 2, 2017
e57a1d0
Fixed example page.
glevitzky Jun 2, 2017
2f7ca32
Minor tweaks + moved refresh manager initialization into layoutCallback.
glevitzky Jun 2, 2017
bef809f
Restructured getRefreshConfiguration.
glevitzky Jun 2, 2017
9eb4268
Removed unneeded type declaration.
glevitzky Jun 2, 2017
ba10f35
Refactored how configs and eligibility is computed.
glevitzky Jun 2, 2017
0cc1f14
Added logic to handle multi-size slots + refresh correctly.
glevitzky Jun 5, 2017
aff6548
Fixed doubleclick.js + passing 'strict' to dimension validator.
glevitzky Jun 5, 2017
a320baf
refresh-manager refactor + amp-ad container type changes.
glevitzky Jun 6, 2017
38e10c2
Refactors + test coverage expansion.
glevitzky Jun 7, 2017
b16be0c
Added test for AmpA4A.refresh().
glevitzky Jun 7, 2017
e75ad4a
Added back eligibility check.
glevitzky Jun 7, 2017
4d32f12
Added refresh counter for doubleclick.
glevitzky Jun 7, 2017
00dbb67
Removed dead code + fixed user().warn.
glevitzky Jun 8, 2017
78fe1a0
Added public-facing documentation for refresh feature.
glevitzky Jun 8, 2017
499866f
Minor refactor + more tests.
glevitzky Jun 8, 2017
ae7f212
Merges with SRA.
glevitzky Jun 8, 2017
3ccf39b
Minor refresh-manager refactor.
glevitzky Jun 8, 2017
a19ded0
Stash.
glevitzky Jun 9, 2017
15e9b2b
Stashing changes while debugging.
glevitzky Jun 9, 2017
9efc75a
Skipping potentially problematic test.
glevitzky Jun 9, 2017
c048035
Lint.
glevitzky Jun 9, 2017
29706d9
Isolating another test.
glevitzky Jun 9, 2017
7c1af0b
lint
glevitzky Jun 9, 2017
a05e12a
Comment toggle.
glevitzky Jun 9, 2017
05775b5
Fixed test.
glevitzky Jun 13, 2017
dcc0a66
Manually triggering scheduler to call layoutcallback.
glevitzky Jun 14, 2017
c384695
Fixed test.
glevitzky Jun 14, 2017
0124d03
Uncommented asserts.
glevitzky Jun 14, 2017
a1beaa0
Moved line to more relevant spot.
glevitzky Jun 14, 2017
36c9fd3
Moved all DOM-changing code into layoutCallback.
glevitzky Jun 15, 2017
22b81a9
Added REVIEW tag.
glevitzky Jun 15, 2017
2199b45
lint fixes
glevitzky Jun 15, 2017
280be2b
Removed changes from _a4a-config.
glevitzky Jun 15, 2017
4701b08
Fixed test.
glevitzky Jun 15, 2017
804d5ea
schedulePass -> requireLayout
glevitzky Jun 16, 2017
c3b8ba3
Duplicate imports from merge.
glevitzky Jun 19, 2017
8438c14
Fixed doubleclick.js test.
glevitzky Jun 19, 2017
3b764ac
Lint fixes.
glevitzky Jun 19, 2017
eb3e4ba
Moved more logic from amp-a4a to doubleclick impl.
glevitzky Jun 21, 2017
32da8eb
Reverting changes to iframe-handler.
glevitzky Jun 21, 2017
070ffb7
Missed stray space.
glevitzky Jun 21, 2017
19b965d
Refactors + PR feedback.
glevitzky Jun 21, 2017
9e3afa7
Fixed tests.
glevitzky Jun 22, 2017
b67eaee
Skipping problematic test.
glevitzky Jun 22, 2017
d8bcbbb
Refactored refresh promise.
glevitzky Jun 23, 2017
ad23e2b
Moved doc + using this.mutateElement.
glevitzky Jun 23, 2017
6276560
Using this.mutateElement.
glevitzky Jun 23, 2017
ad9f99d
Fixed 1 test, added another.
glevitzky Jun 26, 2017
acf6eed
Cleaned up test.
glevitzky Jun 26, 2017
6358656
GH issues.
glevitzky Jun 26, 2017
66c76be
More test fixes + new test.
glevitzky Jun 26, 2017
725abc5
Should have 3 new, green, tests.
glevitzky Jun 26, 2017
1fb489d
Unskipping now passing test for adsense-impl (for ifi/pv).
glevitzky Jun 26, 2017
d0b9704
Guarding refresh feature behind experiment flag.
glevitzky Jun 27, 2017
034b7e7
Minor cosmetic changes.
glevitzky Jun 27, 2017
b904124
Merges.
glevitzky Jun 28, 2017
448f07b
Reverting RefreshManager to use initial implementation of view manage…
glevitzky Jun 28, 2017
dff0d73
min refresh interval 3 -> 30.
glevitzky Jun 28, 2017
16de942
Minor fixes, PR feedback, SRA carveout.
glevitzky Jul 6, 2017
e7653bf
Removed duplicate file.
glevitzky Jul 6, 2017
8b2499e
Fixed user().assert
glevitzky Jul 6, 2017
ce9e829
Fixed tests.
glevitzky Jul 6, 2017
26d6afe
PR feedback.
glevitzky Jul 7, 2017
2250818
Refresh + SRA test, minor misc fixes.
glevitzky Jul 11, 2017
6220374
Moved container-requirement into doubleclick impl + docs update.
glevitzky Jul 11, 2017
54ce29c
Merges.
glevitzky Jul 17, 2017
03ee809
Post-merge fixes.
glevitzky Jul 17, 2017
932760f
PR feedback.
glevitzky Jul 17, 2017
afc635d
Removed superfluous 'REFRESHED' state.
glevitzky Jul 18, 2017
de17663
Fixing comments.
glevitzky Jul 18, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 0 additions & 30 deletions ads/google/a4a/test/test-utils.js
Expand Up @@ -234,36 +234,6 @@ describe('Google A4A utils', () => {
sandbox = sinon.sandbox.create();
});

it('should have the correct ifi numbers', function() {
// When ran locally, this test tends to exceed 2000ms timeout.
this.timeout(5000);
// Reset counter for purpose of this test.
delete window['ampAdGoogleIfiCounter'];
return createIframePromise().then(fixture => {
setupForAdTesting(fixture);
const doc = fixture.doc;
doc.win = window;
const elem = createElementWithAttributes(doc, 'amp-a4a', {
'type': 'adsense',
'width': '320',
'height': '50',
});
const impl = new MockA4AImpl(elem);
noopMethods(impl, doc, sandbox);
return fixture.addElement(elem).then(() => {
return googleAdUrl(impl, '', 0, [], [], []).then(url1 => {
expect(url1).to.match(/ifi=1/);
return googleAdUrl(impl, '', 0, [], [], []).then(url2 => {
expect(url2).to.match(/ifi=2/);
return googleAdUrl(impl, '', 0, [], [], []).then(url3 => {
expect(url3).to.match(/ifi=3/);
});
});
});
});
});
});

it('should set ad position', function() {
// When ran locally, this test tends to exceed 2000ms timeout.
this.timeout(5000);
Expand Down
44 changes: 25 additions & 19 deletions ads/google/a4a/utils.js
Expand Up @@ -130,38 +130,25 @@ export function isReportingEnabled(ampElement) {
export function googleBlockParameters(a4a, opt_experimentIds) {
const adElement = a4a.element;
const win = a4a.win;
win['ampAdGoogleIfiCounter'] = win['ampAdGoogleIfiCounter'] || 1;
const slotRect = a4a.getPageLayoutBox();
const iframeDepth = iframeNestingDepth(win);
// Detect container types.
const containerTypeSet = {};
for (let el = adElement.parentElement, counter = 0;
el && counter < 20; el = el.parentElement, counter++) {
const tagName = el.tagName.toUpperCase();
if (ValidAdContainerTypes[tagName]) {
containerTypeSet[ValidAdContainerTypes[tagName]] = true;
}
}
const pfx =
(containerTypeSet[ValidAdContainerTypes['AMP-FX-FLYING-CARPET']]
|| containerTypeSet[ValidAdContainerTypes['AMP-STICKY-AD']])
? '1' : '0';
const enclosingContainers = getEnclosingContainerTypes(adElement);
const pfx = enclosingContainers.includes(
ValidAdContainerTypes['AMP-FX-FLYING-CARPET']) ||
enclosingContainers.includes(ValidAdContainerTypes['AMP-STICKY-AD']);
let eids = adElement.getAttribute('data-experiment-id');
if (opt_experimentIds) {
eids = mergeExperimentIds(opt_experimentIds, eids);
}
const containerTypeArray = Object.keys(containerTypeSet);
return {
'ifi': win['ampAdGoogleIfiCounter']++,
'adf': domFingerprint(adElement),
'nhd': iframeDepth,
'eid': eids,
'adx': slotRect.left,
'ady': slotRect.top,
'oid': '2',
pfx,
'rc': a4a.fromResumeCallback ? 1 : null,
'act': containerTypeArray.length ? containerTypeArray.join() : null,
'pfx': pfx ? '1' : '0',
'act': enclosingContainers.length ? enclosingContainers.join() : null,
};
}

Expand Down Expand Up @@ -535,3 +522,22 @@ export function addCsiSignalsToAmpAnalyticsConfig(win, element, config,
config['triggers']['continuousVisible']['request'].push('visibilityCsi');
return config;
}

/**
* Returns an array of two-letter codes representing the amp-ad containers
* enclosing the given ad element.
*
* @param {!Element} adElement
* @return {!Array<string>}
*/
export function getEnclosingContainerTypes(adElement) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we have a counter 20 here? Is checking el.tagName == 'BODY' better, like what we do in #isAdPositionAllowed function

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is considered a best practice in the context of ads tagging. Apparently, even though it should by all rights be impossible, there have been reports of JavaScript tags encountering cyclic DOM trees in the wild. If code that traversed the DOM without a loop counter encountered such a cycle, the browser would hang.

That said, I'm not sure AMP really needs to worry about this.

const containerTypeSet = {};
for (let el = adElement.parentElement, counter = 0;
el && counter < 20; el = el.parentElement, counter++) {
const tagName = el.tagName.toUpperCase();
if (ValidAdContainerTypes[tagName]) {
containerTypeSet[ValidAdContainerTypes[tagName]] = true;
}
}
return Object.keys(containerTypeSet);
}
28 changes: 12 additions & 16 deletions ads/google/doubleclick.js
Expand Up @@ -67,24 +67,20 @@ export function doubleclick(global, data) {
* @param {!string} url
*/
function doubleClickWithGpt(global, data, gladeExperiment, url) {
const dimensions = [[
parseInt(data.overrideWidth || data.width, 10),
parseInt(data.overrideHeight || data.height, 10),
]];

// Handle multi-size data parsing, validation, and inclusion into dimensions.
const multiSizeDataStr = data.multiSize || null;
if (multiSizeDataStr) {
const primarySize = dimensions[0];
const primaryWidth = primarySize[0];
const primaryHeight = primarySize[1];

getMultiSizeDimensions(
multiSizeDataStr,
primaryWidth,
primaryHeight,
(data.multiSizeValidation || 'true') == 'true',
dimensions);
const primaryWidth = parseInt(data.overrideWidth || data.width, 10);
const primaryHeight = parseInt(data.overrideHeight || data.height, 10);
let dimensions;
if (multiSizeDataStr && (dimensions = getMultiSizeDimensions(
multiSizeDataStr,
primaryWidth,
primaryHeight,
(data.multiSizeValidation || 'true') == 'true',
true))) {
dimensions.unshift([primaryWidth, primaryHeight]);
} else {
dimensions = [[primaryWidth, primaryHeight]];
}

loadScript(global, url, () => {
Expand Down
94 changes: 94 additions & 0 deletions ads/google/test/test-utils.js
@@ -0,0 +1,94 @@
/**
* Copyright 2017 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 {getMultiSizeDimensions} from '../utils';

describe('#getMultiSizeDimensions', () => {

const multiSizes = [
[300, 300],
[300, 250],
[250, 250],
[250, 200],
[150, 50],
];

const multiSizeDataStr = '300x300,300x250,250x250,250x200,150x50';

function verifyArray(actual, lower, upper) {
expect(multiSizes.filter((size, index) => index < upper && index >= lower))
.to.deep.equal(actual);
}

it('should return all sizes', () => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 300, 300,
/* Ignore lowerbound */ false,
/* Don't drop entire string on error */ false);
verifyArray(actual, 0, multiSizes.length);
});

it('should return a smaller array', () => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 300, 250,
/* Ignore lowerbound */ false,
/* Don't drop entire string on error */ false);
verifyArray(actual, 1, multiSizes.length);
});

it('should return an even smaller array', () => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 250, 250,
/* Ignore lowerbound */ false,
/* Don't drop entire string on error */ false);
verifyArray(actual, 2, multiSizes.length);
});

it('should return an empty array', () => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 100, 50,
/* Ignore lowerbound */ false,
/* Don't drop entire string on error */ false);
verifyArray(actual, 0, 0);
});

it('should return a smaller array due to lowerbound', () => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 300, 300,
/* Use lowerbound */ true,
/* Don't drop entire string on error */ false);

verifyArray(actual, 0, multiSizes.length - 1);
});

it('should return a smaller array due to lowerbound + smaller primary size',
() => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 300, 250,
/* Use lowerbound */ true,
/* Don't drop entire string on error */ false);
verifyArray(actual, 1, multiSizes.length - 1);
});

it('should return null', () => {
const actual = getMultiSizeDimensions(multiSizeDataStr, 300, 250,
/* Ignore lowerbound */ false,
/* Drop entire string on error */ true);
expect(actual).to.be.null;
});

it('should return null due to non-positive argument', () => {
const actual = getMultiSizeDimensions(
'-1x300,' + multiSizeDataStr, 300, 300,
/* Ignore lowerbound */ false,
/* Drop entire string on error */ true);
expect(actual).to.be.null;
});
});