diff --git a/.travis.yml b/.travis.yml index 42358b09003f..ec43c1c73af7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,10 +35,14 @@ script: - gulp test --saucelabs --integration --compiled # All unit tests with an old chrome (best we can do right now to pass tests # and not start relying on new features). - - gulp test --saucelabs --oldchrome + # Disabled because it regressed. Better to run the other saucelabs tests. + # - gulp test --saucelabs --oldchrome - gulp validator branches: only: - master - release - canary +env: + global: + - NPM_CONFIG_PROGRESS="false" diff --git a/3p/integration.js b/3p/integration.js index e1e40ffda759..ab39988915d0 100644 --- a/3p/integration.js +++ b/3p/integration.js @@ -27,6 +27,7 @@ import {a9} from '../ads/a9'; import {adreactor} from '../ads/adreactor'; import {adsense} from '../ads/adsense'; import {adtech} from '../ads/adtech'; +import {plista} from '../ads/plista'; import {doubleclick} from '../ads/doubleclick'; import {facebook} from './facebook'; import {manageWin} from './environment'; @@ -35,12 +36,23 @@ import {twitter} from './twitter'; import {register, run} from '../src/3p'; import {parseUrl} from '../src/url'; import {assert} from '../src/asserts'; +import {taboola} from '../ads/taboola'; + +/** + * Whether the embed type may be used with amp-embed tag. + * @const {!Object} + */ +const AMP_EMBED_ALLOWED = { + taboola: true +}; register('a9', a9); register('adreactor', adreactor); register('adsense', adsense); register('adtech', adtech); +register('plista', plista); register('doubleclick', doubleclick); +register('taboola', taboola); register('_ping_', function(win, data) { win.document.getElementById('c').textContent = data.ping; }); @@ -62,6 +74,9 @@ export function draw3p(win, data, configCallback) { const type = data.type; assert(win.context.location.originValidated != null, 'Origin should have been validated'); + + assert(isTagNameAllowed(data.type, win.context.tagName), + 'Embed type %s not allowed with tag %s', data.type, win.context.tagName); if (configCallback) { configCallback(data, data => { assert(data, 'Expected configuration to be passed as first argument'); @@ -257,3 +272,18 @@ export function parseFragment(fragment) { } return json ? JSON.parse(json) : {}; } + +/** + * Not all types of embeds are allowed to be used with all tag names on the + * AMP side. This function checks whether the current usage is permissible. + * @param {string} type + * @param {string|undefined} tagName The tagName that was used to embed this + * 3p-frame. + * @return {boolean} + */ +export function isTagNameAllowed(type, tagName) { + if (tagName == 'AMP-EMBED') { + return !!AMP_EMBED_ALLOWED[type]; + } + return true; +} diff --git a/README.md b/README.md index 09ec322bac3b..d2ce54bda2be 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ AMP HTML works by including the AMP JS library and adding a bit of boilerplate t - + Hello World! diff --git a/ads/README.md b/ads/README.md index 80fecad1f582..7d9d03c09f9e 100644 --- a/ads/README.md +++ b/ads/README.md @@ -83,23 +83,11 @@ Example usage: Ads can call the special API `window.context.requestResize(width, height)` to send a resize request. -Example of resize request: -```javascript -window.parent.postMessage({ - sentinel: 'amp-3p', - type: 'embed-size', - height: document.body.scrollHeight -}, '*'); -``` - -Once this message is received the AMP runtime will try to accommodate this request as soon as +Once the request is processed the AMP runtime will try to accommodate this request as soon as possible, but it will take into account where the reader is currently reading, whether the scrolling is ongoing and any other UX or performance factors. -Based one whether the AMP runtime was able to satisfy the resize request, -the runtime will also send out a `embed-size-changed` or `embed-size-denied` message accordingly. The ad can listen to these messages and have it's own overflow element within and repeat request on tap. In that case AMP runtime will definitely allow size change due to user action. - -Ads can call the special API `window.context.onResizeSuccess` to get a callback in case a resize request was successful. +Ads can observe wehther resize request were successful using the `window.context.onResizeSuccess` and `window.context.onResizeDenied` methods. Example ```javascript @@ -107,12 +95,7 @@ var unlisten = window.context.onResizeSuccess(function(requestedHeight) { // Hide any overflow elements that were shown. // The requestedHeight argument may be used to check which height change the request corresponds to. }); -``` - -Ads can call the special API `window.context.onResizeDenied` to get a callback in case a resize request was denied. -Example -```javascript var unlisten = window.context.onResizeDenied(function(requestedHeight) { // Show the overflow element and send a window.context.requestResize(width, height) when the overflow element is clicked. // You may use the requestedHeight to check which height change the request corresponds to. @@ -127,10 +110,10 @@ Here are some factors that affect how fast the resize will be executed: - Whether the resize is requested for an ad below the viewport or above the viewport. -### Minimizing HTTP requests +### Optimizing ad performance #### JS reuse across iframes -To allow ads to bundle HTTP requests across multiple ad units on the same page the object `window.context.master` will contain the window object of the iframe being elected master iframe for the current page. +To allow ads to bundle HTTP requests across multiple ad units on the same page the object `window.context.master` will contain the window object of the iframe being elected master iframe for the current page. The `window.context.isMaster` property is `true` when the current frame is the master frame. #### Preconnect and prefetch Add the JS URLs that an ad **always** fetches or always connects to (if you know the origin but not the path) to [_config.js](_config.js). diff --git a/ads/_config.js b/ads/_config.js index 1f3c8eef1074..5fa606c3c567 100644 --- a/ads/_config.js +++ b/ads/_config.js @@ -37,6 +37,7 @@ export const adPrefetch = { export const adPreconnect = { adreactor: 'https://adserver.adreactor.com', adsense: 'https://googleads.g.doubleclick.net', + taboola: 'https://cdn.taboola.com', doubleclick: [ 'https://partner.googleadservices.com', 'https://securepubads.g.doubleclick.net', diff --git a/ads/plista.js b/ads/plista.js new file mode 100644 index 000000000000..a23f27139e6c --- /dev/null +++ b/ads/plista.js @@ -0,0 +1,48 @@ +/** + * Copyright 2015 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 {loadScript, checkData} from '../src/3p'; + +/** + * @param {!Window} global + * @param {!Object} data + */ +export function plista(global, data) { + checkData(data, [ + 'publickey', 'widgetname', 'urlprefix', + 'item', 'geo', 'categories', 'countrycode' + ]); + const div = global.document.createElement('div'); + div.setAttribute('data-display', 'plista_widget_' + data.widgetname); + // container with id "c" is provided by amphtml + global.document.getElementById('c').appendChild(div); + window.PLISTA = { + publickey: data.publickey, + widgets: [{ + name: data.widgetname, + pre: data.urlprefix + }], + item: data.item, + geo: data.geo, + categories: data.categories, + noCache: true, + useDocumentReady: false, + dataMode: 'data-display' + }; + + // load the plista modules asynchronously + loadScript(global, 'https://static' + (data.countrycode ? '-' + encodeURIComponent(data.countrycode) : '') + '.plista.com/async.js'); +} diff --git a/ads/plista.md b/ads/plista.md new file mode 100644 index 000000000000..e05d1b9ee91f --- /dev/null +++ b/ads/plista.md @@ -0,0 +1,67 @@ + + +# plista + +## Example + +### Basic + +```html + +``` + +### With article information + +```html + +``` + +## Configuration + +For semantics of configuration, please see [ad network documentation](https://goo.gl/nm9f41). + +Supported parameters: + +- data-countrycode +- data-publickey +- data-widgetname +- data-geo +- data-urlprefix +- data-categories + +Supported via `json` attribute: + +- item + +## Layout + +Width and height are optional. You can use different layout types with plista. diff --git a/ads/taboola.js b/ads/taboola.js new file mode 100644 index 000000000000..333a1829f485 --- /dev/null +++ b/ads/taboola.js @@ -0,0 +1,74 @@ +/** + * Copyright 2015 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 {loadScript, validateDataExists, validateExactlyOne} from '../src/3p'; + +/** + * @param {!Window} global + * @param {!Object} data + */ +export function taboola(global, data) { + // do not copy the following attributes from the 'data' object + // to _tablloa global object + const blackList = ['height', 'initialWindowHeight', 'initialWindowWidth', + 'type', 'width', 'placement', 'mode']; + + // ensure we have vlid publisher, placement and mode + // and exactly one page-type + validateDataExists(data, ['publisher', 'placement', 'mode']); + validateExactlyOne(data, ['article', 'video', 'photo', 'search', 'category', + 'homepage', 'others']); + + // setup default values for referrer and url + const params = { + referrer: data.referrer || global.context.referrer, + url: data.url || global.context.canonicalUrl + }; + + // copy none blacklisted attribute to the 'params' map + Object.keys(data).forEach(k => { + if (blackList.indexOf(k) === -1) { + params[k] = data[k]; + } + }); + + // push the two object into the '_taboola' global + (global._taboola = global._taboola || []).push([{ + viewId: global.context.pageViewId, + publisher: data.publisher, + placement: data.placement, + mode: data.mode, + framework: 'amp', + container: 'c' + }, + params]); + + // install observation on entering/leaving the view + global.context.observeIntersection(function(changes) { + changes.forEach(function(c) { + if (c.intersectionRect.height) { + global._taboola.push({ + visible: true, + rects: c, + placement: data.placement + }); + } + }); + }); + + // load the taboola loader asynchronously + loadScript(global, `https://cdn.taboola.com/libtrc/${encodeURIComponent(data.publisher)}/loader.js`); +} diff --git a/ads/taboola.md b/ads/taboola.md new file mode 100644 index 000000000000..c325381b00d0 --- /dev/null +++ b/ads/taboola.md @@ -0,0 +1,51 @@ + + +# Taboola + +## Example + +### Basic + +```html + + +``` + +## Configuration + +For semantics of configuration, please see ad network documentation. + +Supported parameters: + +- data-publisher +- data-placement +- data-mode +- data-article +- data-video +- data-photo +- data-home +- data-category +- data-others +- data-url +- data-referrer diff --git a/build-system/config.js b/build-system/config.js index 17d60ffd4f7c..84acda9b6d81 100644 --- a/build-system/config.js +++ b/build-system/config.js @@ -62,9 +62,6 @@ var karma = { singleRun: true, client: { captureConsole: false, - amp: { - useCompiledJs: false - } } }, firefox: { @@ -99,10 +96,10 @@ var karma = { 'SL_Firefox_latest', 'SL_Safari_8', 'SL_Safari_9', + 'SL_Edge_latest', // TODO(#895) Enable these. //'SL_iOS_9_1', //'SL_IE_11', - //'SL_Edge_latest', ], singleRun: true, client: { diff --git a/build-system/tasks/presubmit-checks.js b/build-system/tasks/presubmit-checks.js index e383d4ac2ed9..6e9c50156e4d 100644 --- a/build-system/tasks/presubmit-checks.js +++ b/build-system/tasks/presubmit-checks.js @@ -183,7 +183,9 @@ var forbiddenTerms = { message: requiresReviewPrivacy, whitelist: [ 'extensions/amp-access/0.1/amp-access.js', + 'extensions/amp-user-notification/0.1/amp-user-notification.js', 'src/experiments.js', + 'src/service/storage-impl.js', 'tools/experiments/experiments.js', ] }, diff --git a/build-system/tasks/test.js b/build-system/tasks/test.js index 553f7160eaf5..a0a93fb6f5c5 100644 --- a/build-system/tasks/test.js +++ b/build-system/tasks/test.js @@ -91,10 +91,9 @@ gulp.task('test', 'Runs tests', prerequisites, function(done) { c.files = config.testPaths; } - if (argv.compiled) { - // Only applies to integration tests. - c.client.amp.useCompiledJs = true; - } + c.client.amp = { + useCompiledJs: !!argv.compiled + }; karma.start(c, done); }, { diff --git a/builtins/amp-embed.md b/builtins/amp-embed.md index 2d84c80ebd17..3e75f6a3998e 100644 --- a/builtins/amp-embed.md +++ b/builtins/amp-embed.md @@ -19,8 +19,8 @@ The `amp-embed` element is used to allow embedding elements in to the AMP page. #### Implementation -The `` is actually an alias to the [``](amp-ad.md) tag, deriving all of it's functionality with a different tag name. -Can be used instead of `` when that would be semanitcally more accurate. +The `` is actually an alias to the [``](amp-ad.md) tag, deriving all of it's functionality with a different tag name. +Can be used instead of `` when that would be semantically more accurate. ```html -

A9 as amp-embed

- +

plista

+ + + +

Taboola responsive widget

+ diff --git a/examples/analytics-notification.amp.html b/examples/analytics-notification.amp.html index 0a81cbf0bc19..ae98b30c0197 100644 --- a/examples/analytics-notification.amp.html +++ b/examples/analytics-notification.amp.html @@ -58,7 +58,7 @@ - + diff --git a/examples/analytics.amp.html b/examples/analytics.amp.html index 5e2e4e81aa4d..53227a23d1dd 100644 --- a/examples/analytics.amp.html +++ b/examples/analytics.amp.html @@ -14,7 +14,7 @@ } - + @@ -83,6 +83,18 @@ + + + + + +

AMP Analytics

diff --git a/examples/article-access.amp.html b/examples/article-access.amp.html index 65beb37517a3..255aaedebeaa 100644 --- a/examples/article-access.amp.html +++ b/examples/article-access.amp.html @@ -142,7 +142,7 @@ - + diff --git a/examples/article.amp.html b/examples/article.amp.html index 7634acd3cfc8..57ee52934de8 100644 --- a/examples/article.amp.html +++ b/examples/article.amp.html @@ -145,7 +145,7 @@ } - + diff --git a/examples/brightcove.amp.html b/examples/brightcove.amp.html index 57e7ff138dfc..a67541bcc461 100644 --- a/examples/brightcove.amp.html +++ b/examples/brightcove.amp.html @@ -7,7 +7,7 @@ - + diff --git a/examples/csp.amp.html b/examples/csp.amp.html index 908de21bf43f..4731f0d3ddd4 100644 --- a/examples/csp.amp.html +++ b/examples/csp.amp.html @@ -10,7 +10,7 @@ - + + diff --git a/examples/facebook.amp.html b/examples/facebook.amp.html index 35561c716ddf..eeec1b057450 100644 --- a/examples/facebook.amp.html +++ b/examples/facebook.amp.html @@ -7,7 +7,7 @@ - + diff --git a/examples/font.amp.html b/examples/font.amp.html index 78292b28df43..9e0184363f4f 100644 --- a/examples/font.amp.html +++ b/examples/font.amp.html @@ -53,7 +53,7 @@ } - + diff --git a/examples/instagram.amp.html b/examples/instagram.amp.html index a1b411a59e21..a78fe03f38fb 100644 --- a/examples/instagram.amp.html +++ b/examples/instagram.amp.html @@ -12,7 +12,7 @@ background-color: blue; } - + diff --git a/examples/metadata-examples/article-json-ld.amp.html b/examples/metadata-examples/article-json-ld.amp.html index 6a9dbb21a4b5..54c4e4b68ec6 100644 --- a/examples/metadata-examples/article-json-ld.amp.html +++ b/examples/metadata-examples/article-json-ld.amp.html @@ -59,6 +59,8 @@ "mainEntityOfPage": "http://cdn.ampproject.org/article-metadata.html", "headline": "Lorem Ipsum", "datePublished": "1907-05-05T12:02:41Z", + "dateModified": "1907-05-05T12:02:41Z", + "description": "The Catiline Orations continue to begule engineers and designers alike -- but can it stand the test of time?", "author": { "@type": "Person", "name": "Jordan M Adler" @@ -82,7 +84,7 @@ } - + diff --git a/examples/metadata-examples/article-microdata.amp.html b/examples/metadata-examples/article-microdata.amp.html index 4c57332caf8c..8d75244e5a69 100644 --- a/examples/metadata-examples/article-microdata.amp.html +++ b/examples/metadata-examples/article-microdata.amp.html @@ -42,7 +42,7 @@ } - + @@ -51,6 +51,8 @@ + +
@@ -72,7 +74,7 @@

AMP boilerplate

+

Testing if the old validator is still accepted.

+

+ "Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit..." + "There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain..." +

+ + diff --git a/examples/pinterest.amp.html b/examples/pinterest.amp.html index 8bea3bebdafc..bf725cc6e214 100644 --- a/examples/pinterest.amp.html +++ b/examples/pinterest.amp.html @@ -7,7 +7,7 @@ - + diff --git a/examples/released.amp.html b/examples/released.amp.html index 06fb4b6e6776..3bf885c64281 100644 --- a/examples/released.amp.html +++ b/examples/released.amp.html @@ -25,7 +25,7 @@ - + diff --git a/examples/responsive.amp.html b/examples/responsive.amp.html new file mode 100644 index 000000000000..2ddf1e3a8545 --- /dev/null +++ b/examples/responsive.amp.html @@ -0,0 +1,336 @@ + + + + + Lorem Ipsum | PublisherName + + + + + + + + + +
+ +
+
+
+
+ + +
+ + + + + + +
+
+

Lorem Ipsum

+ +

+ Fusce pretium tempor justo, vitae consequat dolor maximus eget. +

+
+ +
+ + + +
+
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Curabitur ullamcorper turpis vel commodo scelerisque. Phasellus + luctus nunc ut elit cursus, et imperdiet diam vehicula. + Duis et nisi sed urna blandit bibendum et sit amet erat. + Suspendisse potenti. Curabitur consequat volutpat arcu nec + elementum. Etiam a turpis ac libero varius condimentum. + Maecenas sollicitudin felis aliquam tortor vulputate, + ac posuere velit semper. +

+

+ Fusce pretium tempor justo, vitae consequat dolor maximus eget. + Aliquam iaculis tincidunt quam sed maximus. Suspendisse faucibus + ornare sodales. Nullam id dolor vitae arcu consequat ornare a + et lectus. Sed tempus eget enim eget lobortis. + Mauris sem est, accumsan sed tincidunt ut, sagittis vel arcu. + Nullam in libero nisi. +

+ +
+ + +
+ +

+ Sed pharetra semper fringilla. Nulla fringilla, neque eget + varius suscipit, mi turpis congue odio, quis dignissim nisi + nulla at erat. Duis non nibh vel erat vehicula hendrerit eget + vel velit. Donec congue augue magna, nec eleifend dui porttitor + sed. Cras orci quam, dignissim nec elementum ac, bibendum et purus. + Ut elementum mi eget felis ultrices tempus. Maecenas nec sodales + ex. Phasellus ultrices, purus non egestas ullamcorper, felis + lorem ultrices nibh, in tristique mauris justo sed ante. + Nunc commodo purus feugiat metus bibendum consequat. Duis + finibus urna ut ligula auctor, sed vehicula ex aliquam. + Sed sed augue auctor, porta turpis ultrices, cursus diam. + In venenatis aliquet porta. Sed volutpat fermentum quam, + ac molestie nulla porttitor ac. Donec porta risus ut enim + pellentesque, id placerat elit ornare. +

+

+ Curabitur convallis, urna quis pulvinar feugiat, purus diam + posuere turpis, sit amet tincidunt purus justo et mi. Donec + sapien urna, aliquam ut lacinia quis, varius vitae ex. + Maecenas efficitur iaculis lorem, at imperdiet orci viverra + in. Nullam eu erat eu metus ultrices viverra a sit amet leo. + Pellentesque est felis, pulvinar mollis sollicitudin et, + suscipit eget massa. Nunc bibendum non nunc et consequat. + Quisque auctor est vel leo faucibus, non faucibus magna ultricies. + Vestibulum ante ipsum primis in faucibus orci luctus et ultrices + posuere cubilia Curae; Vestibulum tortor lacus, bibendum et + enim eu, vehicula placerat erat. Nullam gravida rhoncus accumsan. + Integer suscipit iaculis elit nec mollis. Vestibulum eget arcu + nec lectus finibus rutrum vel sed orci. +

+ +
+ + +
+ Fusce pretium tempor justo, vitae consequat dolor maximus eget. +
+
+
+ +

+ Cum sociis natoque penatibus et magnis dis parturient montes, + nascetur ridiculus mus. Nulla et viverra turpis. Fusce + viverra enim eget elit blandit, in finibus enim blandit. Integer + fermentum eleifend felis non posuere. In vulputate et metus at + aliquam. Praesent a varius est. Quisque et tincidunt nisi. + Nam porta urna at turpis lacinia, sit amet mattis eros elementum. + Etiam vel mauris mattis, dignissim tortor in, pulvinar arcu. + In molestie sem elit, tincidunt venenatis tortor aliquet sodales. + Ut elementum velit fermentum felis volutpat sodales in non libero. + Aliquam erat volutpat. +

+ +
+ + +
+ +

+ Morbi at velit vitae eros congue congue venenatis non dui. + Sed lacus sem, feugiat sed elementum sed, maximus sed lacus. + Integer accumsan magna in sagittis pharetra. Class aptent taciti + sociosqu ad litora torquent per conubia nostra, per inceptos + himenaeos. Suspendisse ac nisl efficitur ligula aliquam lacinia + eu in magna. Vestibulum non felis odio. Ut consectetur venenatis + felis aliquet maximus. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. +

+ + + + + +
+
+
+
+ +
+ +
+ + diff --git a/examples/twitter.amp.html b/examples/twitter.amp.html index 9214235dfb51..0d17ff94ebeb 100644 --- a/examples/twitter.amp.html +++ b/examples/twitter.amp.html @@ -7,7 +7,7 @@ - + diff --git a/examples/user-notification.amp.html b/examples/user-notification.amp.html index 79b10a63c14e..cd8dead22538 100644 --- a/examples/user-notification.amp.html +++ b/examples/user-notification.amp.html @@ -197,7 +197,7 @@ - + @@ -406,7 +406,7 @@

Lorem Ipsum

+ + + + +

Vimeo

+ + + + + + + + + + diff --git a/examples/vine.amp.html b/examples/vine.amp.html index 5eb35003328e..9d97ff375414 100644 --- a/examples/vine.amp.html +++ b/examples/vine.amp.html @@ -7,7 +7,7 @@ - + diff --git a/extensions/amp-access/0.1/login-dialog.js b/extensions/amp-access/0.1/login-dialog.js index 4fa574d88ade..c8f8de11ef01 100644 --- a/extensions/amp-access/0.1/login-dialog.js +++ b/extensions/amp-access/0.1/login-dialog.js @@ -25,6 +25,9 @@ const TAG = 'AmpAccessLogin'; /** @const {!Function} */ const assert = AMP.assert; +/** @const {!RegExp} */ +const RETURN_URL_REGEX = new RegExp('RETURN_URL'); + /** * Opens the login dialog for the specified URL. If the login dialog succeeds, @@ -221,6 +224,12 @@ class LoginDialog { * @private */ buildLoginUrl_(url, returnUrl) { + // RETURN_URL has to arrive here unreplaced by UrlReplacements for two + // reasons: (1) sync replacement and (2) if we need to propagate this + // replacement to the viewer. + if (RETURN_URL_REGEX.test(url)) { + return url.replace(RETURN_URL_REGEX, encodeURIComponent(returnUrl)); + } return url + (url.indexOf('?') == -1 ? '?' : '&') + 'return=' + encodeURIComponent(returnUrl); diff --git a/extensions/amp-access/0.1/test/test-amp-access.js b/extensions/amp-access/0.1/test/test-amp-access.js index fff8edcebc0e..f4cd5e0c4241 100644 --- a/extensions/amp-access/0.1/test/test-amp-access.js +++ b/extensions/amp-access/0.1/test/test-amp-access.js @@ -873,6 +873,20 @@ describe('AccessService login', () => { }); }); + it('should build login url with RETURN_URL', () => { + service.config_.login = 'https://acme.com/l?rid=READER_ID&ret=RETURN_URL'; + cidMock.expects('get') + .withExactArgs( + {scope: 'amp-access', createCookieIfNotPresent: true}, + sinon.match(() => true)) + .returns(Promise.resolve('reader1')) + .once(); + return service.buildLoginUrl_().then(url => { + expect(url).to.equal('https://acme.com/l?rid=reader1&ret=RETURN_URL'); + expect(service.loginUrl_).to.equal(url); + }); + }); + it('should open dialog in the same microtask', () => { service.openLoginDialog_ = sandbox.stub(); service.openLoginDialog_.returns(Promise.resolve()); diff --git a/extensions/amp-access/0.1/test/test-login-dialog.js b/extensions/amp-access/0.1/test/test-login-dialog.js index 4e50fbdf13bb..b350865f49c8 100644 --- a/extensions/amp-access/0.1/test/test-login-dialog.js +++ b/extensions/amp-access/0.1/test/test-login-dialog.js @@ -195,6 +195,26 @@ describe('LoginDialog', () => { }); }); + it('should substitute return URL', () => { + windowMock.expects('open') + .withExactArgs( + 'http://acme.com/login?a=1&ret1=' + RETURN_URL_ESC, + '_blank', + 'height=450,width=700,left=150,top=275') + .returns(dialog) + .once(); + const promise = openLoginDialog(windowApi, + 'http://acme.com/login?a=1&ret1=RETURN_URL'); + return Promise.resolve() + .then(() => { + succeed(); + return promise; + }) + .then(result => { + expect(result).to.equal('#success=true'); + }); + }); + it('should respond with empty string when dialog is closed', () => { windowMock.expects('open') .returns(dialog) diff --git a/extensions/amp-access/amp-access-spec.md b/extensions/amp-access/amp-access-spec.md index 2dc8339df996..7d456955daa8 100644 --- a/extensions/amp-access/amp-access-spec.md +++ b/extensions/amp-access/amp-access-spec.md @@ -23,6 +23,7 @@ limitations under the License. AMP Access or “AMP paywall and subscription support” provides control to publishers over what content can be accessed by a reader and with what restrictions,  based on the reader’s subscription status, number of views and so on. #Solution + The proposed solution gives control to the Publisher over the following decisions and flows: - Create and maintain users - Control of metering @@ -45,7 +46,9 @@ The solution also allows the Publisher to place in the AMP document a Login Lin In its basic form, this solution sends the complete (though obscured) document to the Reader and simply shows/hides restricted sections based on the Authorization response. However, the solution also provides the “server” option, where the restricted sections can be excluded from the initial document delivery and downloaded only after the authorization has been confirmed. Supporting AMP Access requires Publisher to implement the components described above. Access Content Markup, Authorization endpoint, Pingback endpoint and Login Page are required. + ##AMP Reader ID + To assist access services and use cases, AMP Access introduces the concept of the *Reader ID*. The Reader ID is an anonymous and unique ID created by the AMP ecosystem. It is unique for each reader/publisher pair. i.e., a Reader is identified differently to two different publishers. It is a non-reversible ID. The Reader ID is included in all AMP/Publisher communications. Publishers can use the Reader ID to identify the Reader and map it to their own identity systems. @@ -67,11 +70,13 @@ Authorization is an endpoint provided by the publisher and called by AMP Runtime Pingback is an endpoint provided by the publisher and called by AMP Runtime or AMP Cache. It is a credentialed CORS endpoint. AMP Runtime calls this endpoint automatically when the Reader has started viewing the document. On of the main goals of the Pingback is for the Publisher to update metering information. ##Login Page and Login Link + Login Page is implemented and served by the Publisher and called by the AMP Runtime. It is normally shown as a browser dialog. Login Page is triggered when the Reader taps on the Login Link which can be placed by the Publisher anywhere in the document. #Specification v0.2 + ##Configuration All of the endpoints are configured in the AMP document as a JSON object in the HEAD of the document: @@ -110,6 +115,7 @@ Here’s an example of the AMP Access configuration: } ``` + ##Access URL Variables When configuring the URLs for various endpoints, the Publisher can use substitution variables. These variables are a subset of the variables defined in the [AMP Var Spec](https://github.com/ampproject/amphtml/blob/master/spec/amp-var-substitutions.md). The set of allowed variables is defined in the table below: @@ -118,6 +124,7 @@ Var | Description ----------------- | ----------- READER_ID | The AMP Reader ID. AUTHDATA(field) | The value of the field in the authorization response. +RETURN_URL | The placeholder for the return URL specified by the AMP runtime for a Login Dialog to return to. AMPDOC_URL | The URL of this AMP Document. CANONICAL_URL | The canonical URL of this AMP Document. DOCUMENT_REFERRER | The Referrer URL. @@ -190,6 +197,7 @@ And here’s an example that shows additional content to the premium subscribers Shhh… No one but you can read this content. ``` + ##Authorization Endpoint Authorization is configured via ```authorization``` property in the [AMP Access Configuration][8] section. It is a credentialed CORS endpoint. See [CORS Origin Security][9] for a list of origins that should be allowed. @@ -236,8 +244,8 @@ This RPC may be called in the prerendering phase and thus it should not be used Another important consideration is that in some cases AMP runtime may need to call Authorization endpoint multiple times per document impression. This can happen when AMP Runtime believes that the access parameters for the Reader have changed significantly, e.g. after a successful Login Flow. The authorization response may be used by AMP Runtime and extensions for two different purposes: - 1. When evaluating ```amp-access``` expressions - 2. When evaluating ```