From 001680a9a44c8bdbeb390a1bdb1b053c730f25d4 Mon Sep 17 00:00:00 2001 From: Matt Mower Date: Wed, 6 May 2020 06:52:28 -0700 Subject: [PATCH 01/62] spec: Add AMP framework hosting guide (#27100) * spec: Add AMP framework hosting guide * Fix URLs * Prettier * Partially address reviews - Add another warning about mixing origins - Add cache-control header advice This commit does not address all review items. * Update version flag, minor reorg * Minor updates * Fix heading * Fix serving framework links and update flags presentation * Rename tool download-runtime --> runtime-download * Update guide with latest on self-hosting - Expect absolute URLs in meta and scripts - Version format (13/15-digits) is required - Pages will not yet validate - Shrink some tl;dr sections * Minor tweaks * Remove redundant info * Minorestest tweak * Lint fixes * Revisions from review * Revisions from review * Subdivision part of ISO 3166-2 is alphanumeric * Subdivision part of ISO 3166-2 has variable length * Revisions from review * Update build instructions * Update for changes in versioning --- spec/amp-framework-hosting.md | 324 ++++++++++++++++++++++++++++++++++ 1 file changed, 324 insertions(+) create mode 100644 spec/amp-framework-hosting.md diff --git a/spec/amp-framework-hosting.md b/spec/amp-framework-hosting.md new file mode 100644 index 000000000000..740100eb84ea --- /dev/null +++ b/spec/amp-framework-hosting.md @@ -0,0 +1,324 @@ +# Hosting the AMP framework + +You can host the AMP framework and components from your own server or CDN. This feature has a number of applications. For example, you can... + +- set a release cadence that matches your development cycle. +- deliver the AMP framework in regions where `cdn.ampproject.org` may not be available. +- serve AMP pages and the framework from the same host, potentially improving content delivery times. +- test and demonstrate changes to the framework or components. + +The AMP Project is looking into options for [validation](https://amp.dev/documentation/guides-and-tutorials/learn/validation-workflow/validate_amp/) of AMP pages that use an AMP framework hosted outside of `cdn.ampproject.org` ([#27546](https://github.com/ampproject/amphtml/issues/27546)). As of April 2020, these AMP pages do not pass validation. + +## Versioning + +This document makes frequent use of the terms "version" and "runtime version (rtv)". These two versions are slightly different. When the AMP framework is built, it is assigned a 13-digit _version_. When the AMP framework is served, a config number prefixes the version, resulting in a 15-digit _runtime version_. The build system and runtime enforce these version formats. + +Note: The _version_ (sometimes referred to as the "AMP version number") and _runtime version_ are often confused with each other. When it matters, be explicit about the version to which you're referring by indicating 13- or 15-digits. + +When the AMP framework is built (either by you or by the AMP Project), a 13-digit date-based version is automatically assigned. You can find this version in `version.txt` at the root of the AMP framework distribution. Then, the easiest route to hosting the AMP framework is to prefix this version with `01` to create a 15-digit runtime version, where `01` indicates a stable release. + +A more complete picture of the conventions adopted by the AMP Project for the version and runtime version is below. This is more than you need to get started with hosting the AMP framework, but serves as a good reference in case you want to expand your hosting capabilities. + +- Version ([#16631](https://github.com/ampproject/amphtml/pull/16631) and [#27848](https://github.com/ampproject/amphtml/pull/27848)): the commit time of the last commit in the active branch + + ``` + TZ=UTC git log -1 --pretty="%cd" --date=format-local:%y%m%d%H%M + ``` + + with three trailing digits indicating the number of cherry-picks included in the release. This version corresponds to the release versions found on [github.com/ampproject/amphtml/releases](https://github.com/ampproject/amphtml/releases). + + Note: Prior to 2020-04-25, the version was based on date/time format `%y%m%d%H%M%S` with a single trailing digit – typically `0`, but could be change arbitrarily. + +- Runtime version (rtv): the version prefixed by a 2-digit config code: + + - Experimental: `00` + - Stable: `01` + - Control: `02` + - Beta: `03` + - Nightly: `04` + - Nightly-Control: `05` + - AMP Experiments: `10`, `11`, `12` + - INABOX Control and Experiments: `20`, `21`, `22`, `23`, `24`, `25` + +The runtime version is found in URLs and is reported in the console of browser inspectors when an AMP page loads. For example, runtime version `012004041903580` is stable version `2004041903580`. + +## Acquire the AMP framework + +The AMP framework can be built from source or downloaded pre-built. Building the framework yourself gives you the flexibility of modifying the framework for your needs and deploying changes on your own release cycle. Downloading a pre-built framework means you'll be using an unmodified release, vetted by the AMP Project for distribution. + +### Option 1: Build the framework yourself + +Refer to the [Developing in AMP](https://github.com/ampproject/amphtml/blob/master/contributing/DEVELOPING.md) guide to familiarize yourself with building and testing the AMP framework. Once you are comfortable with the build system, a few small changes will customize the framework to run from your host. + +#### Update URLs config + +When AMP is built, several scripts are prepended with an `AMP_CONFIG` environment variable (object) containing basic information like: runtime version, config type, experiment enable/disable status, etc. This object can be customized at build time to inform the runtime where the framework is hosted. See [build-system/global-configs/README.md](https://github.com/ampproject/amphtml/tree/master/build-system/global-configs#custom-configjson) for information about the `custom-config.json` overlay. + +Create JSON file `build-system/global-configs/custom-config.json` with the following contents: + +``` +{ + "cdnUrl": "https://example.com/amp-framework", + "geoApiUrl": "https://example.com/geo-api" +} +``` + +where + +- `cdnUrl` is the base URL to your AMP framework. Defaults to `https://cdn.ampproject.org`. +- `geoApiUrl` (optional) is your amp-geo fallback API URL. This API is described in section [amp-geo hotpatching](#amp-geo-hotpatching). Defaults to `null`. + +Important: `build-system/global-configs/custom-config.json` is not part of checked-in source. If it exists, it _always_ applies at build time, overlaying the active config. Don't forget about it! You can verify the overlay applies by looking for log line `Overlaid config with custom-config.json` during the build process. + +#### Build the framework + +Build an AMP release with + +``` +gulp dist +``` + +The built framework can be found in directory `dist`. The version assigned to the build is in `dist/version.txt` and a listing of all files included in build is in `dist/files.txt`. The framework is ready to be moved to and served from your host. + +If you have advanced hosting capabilities or would like to manually assign a version, `gulp dist` accepts these flags (among others): + +- `--config`: Indicate the release type, production (`prod`) or canary (`canary`). Defaults to `prod`. +- `--version_override`: Assign a version to the distribution. The version must consist of 13-digits. Defaults to the latest git commit time of the active branch. +- `--sourcemap_url`: Provide the base URL for JavaScript source map links. This URL should contain placeholder `{version}` that will be replaced with the actual version when the AMP framework is built, for example `https://raw.githubusercontent.com//amphtml/{version}/`. Defaults to `https://raw.githubusercontent.com/ampproject/amphtml/{version}/`. + +### Option 2: Download the framework with an AMP Toolbox tool + +[AMP Toolbox](https://github.com/ampproject/amp-toolbox) has both a Node.js module and a command line tool that will fetch a complete AMP framework from `cdn.ampproject.org`. Pick the tool best suited to your release workflow. + +- [@ampproject/toolbox-runtime-fetch](https://github.com/ampproject/amp-toolbox/tree/master/packages/runtime-fetch) - Node.js module +- [@ampproject/toolbox-cli](https://github.com/ampproject/amp-toolbox/tree/master/packages/cli) - command line interface + +### Option 3: Manually copy the framework from cdn.ampproject.org + +The AMP framework can be copied from `cdn.ampproject.org`. The latest weekly release is always served from the root of `cdn.ampproject.org`. All [non-deprecated releases](https://github.com/ampproject/amphtml/blob/master/spec/amp-versioning-policy.md#version-deprecations) can be found in versioned URLs: `cdn.ampproject.org/rtv/`, where `` is the runtime version. + +Note: The AMP Project is looking into options for packaging releases ([#27726](https://github.com/ampproject/amphtml/issues/27726)). + +#### Copy files + +A listing of files in each release can be found in `files.txt` at the root of the framework distribution. For example, the files included in the current weekly release are listed in [cdn.ampproject.org/files.txt](https://cdn.ampproject.org/files.txt). Use your favorite HTTP client to download all of the files in `files.txt`, retaining path structures. + +#### Undo amp-geo dynamic modification + +When you request `amp-geo-0.1.js` from `cdn.ampproject.org` using an HTTP client, the CDN detects the country where the request originated and patches `amp-geo-0.1.js` on-the-fly. This patch needs to be reversed to ensure users are not all assigned the same country when amp-geo loads. + +When `cdn.ampproject.org` serves `amp-geo-0.1.js`, it replaces string `{{AMP_ISO_COUNTRY_HOTPATCH}}` with an ISO 3166-1 country code or an ISO 3166-2 country-subdivision code, followed by enough spaces to maintain the length of the string being replaced. Reversal of this patch can be accomplished by a RegEx replacement: search for `/ {28}|[a-z]{2} {26}|[a-z]{2} [a-z]{2}-[a-z0-9]{1,3} {19,21}/i` and replace with `{{AMP_ISO_COUNTRY_HOTPATCH}}`. + +In addition to `amp-geo-0.1.js`, you may find module JS (`.mjs`) and unversioned (`amp-geo-latest.js`) variants of the same file. The same RegEx replacement should be performed in these files as well. + +## Modify AMP pages + +AMP pages should be updated to fetch all scripts from your host. For example, + +``` + + +``` + +becomes + +``` + + +``` + +Versioned URLs are also possible and discussed in section [Serve the AMP framework](#serve-the-amp-framework). + +Important: All scripts must come from the same origin. Fetching scripts from multiple origins is not supported and can lead to unpredictable end user experiences. + +### Meta tags + +If you opted to download the AMP framework, then it was built under the assumption that it would be hosted from its default location, `cdn.ampproject.org`. While ` + + +``` + +## Serve the AMP framework + +Depending on your hosting capabilities and goals, there are several options to consider when serving the AMP framework. + +### URLs + +When the AMP runtime is initialized, all components are verified to belong to the same AMP framework version as the runtime itself. This affects components that have already downloaded as well as components that are downloaded dynamically, like amp-loader. Dynamic components and components with incorrect versions are (re-)downloaded from an rtv-specific path. This has consequences on how you choose to host the runtime. + +#### Versioned URLs + +The simplest option for hosting the AMP framework is to host it from an rtv-specific path. For example, consider AMP framework host `https://example.com/amp`. Stable version `2002290616360` corresponds to runtime version `012002290616360` and should be hosted from `https://example.com/amp/rtv/012002290616360`. In this case, AMP meta and script URLs would look like the following: + +``` + + + +``` + +If hosting a single AMP framework version is your end goal, then you can update your AMP pages to download the runtime and components from your host and skip to section [amp-geo hotpatching](#amp-geo-hotpatching). If you expect to update the AMP framework regularly, then updating rtv-specific URLs in AMP pages could be cumbersome. See the next section, [Versionless URLs](#versionless-urls), for a solution. + +#### Versionless URLs + +The AMP Project has a [weekly release channel](https://amp.dev/documentation/guides-and-tutorials/learn/spec/release-schedule/#weekly), sometimes referred to as the "evergreen" release channel. AMP pages utilize this channel by including versionless URLs to AMP scripts and styles. This is relatively easy to accomplish when hosting the AMP framework yourself. The key is to ensure that the AMP framework hosted from versionless URLs is _also_ available from rtv-specific URLs. This suggests an update strategy: first make a new AMP framework version available from rtv-specific URLs and _then_ update the AMP framework available from versionless URLs. For example, if stable AMP framework version `2002290616360` is available from `https://example.com/amp`, then it must also be available from `https://example.com/amp/rtv/012002290616360`. In this case, AMP meta and script URLs would look like the following: + +``` + + + +``` + +If you inspect the DOM after an AMP page loads, you'll notice additional components are dynamically loaded by the runtime and use versioned URLs, for example + +``` + +``` + +### Metadata + +The AMP Project hosts a metadata endpoint at [cdn.ampproject.org/rtv/metadata](https://cdn.ampproject.org/rtv/metadata) that returns information on current releases. Hosting this endpoint yourself is optional, but may be useful if you use [AMP Toolbox](https://github.com/ampproject/amp-toolbox): + +- [@ampproject/toolbox-runtime-version](https://github.com/ampproject/amp-toolbox/tree/master/packages/runtime-version) uses the data to determine the latest AMP framework version available. +- [@ampproject/toolbox-optimizer](https://github.com/ampproject/amp-toolbox/tree/master/packages/optimizer) uses the data to identify the boilerplate CSS that should be included in optimized AMP pages. +- [@ampproject/toolbox-runtime-fetch](https://github.com/ampproject/amp-toolbox/tree/master/packages/runtime-fetch) uses the data to identify an rtv-specific path from which the framework should be downloaded. + +Consider the following sample from [cdn.ampproject.org/rtv/metadata](https://cdn.ampproject.org/rtv/metadata), most JSON properties are optional: + +``` +{ + "ampRuntimeVersion": "012002192257490", + "ampCssUrl": "https://cdn.ampproject.org/rtv/012002192257490/v0.css", + "canaryPercentage": "0.005", + "diversions": ["002002251816300", "032002251816300", "022002192257490"], + "ltsRuntimeVersion": "012002191527100", + "ltsCssUrl": "https://cdn.ampproject.org/rtv/012002191527100/v0.css" +} +``` + +The properties are defined as follows: + +- `ampRuntimeVersion` (required) is the current stable runtime version of the AMP framework. +- `ampCssUrl` (optional) is a URL to the boilerplate CSS for the current stable runtime version. +- `canaryPercentage` (optional) indicates the fraction of users who receive the experimental runtime version of the AMP framework instead of the current stable runtime version. +- `diversions` (optional) lists active non-stable runtime versions. +- `ltsRuntimeVersion` (optional) is the current [long-term stable](https://github.com/ampproject/amphtml/blob/master/contributing/lts-release.md) runtime version. +- `ltsCssUrl` (optional) is a URL to the boilerplate CSS for the current long-term stable runtime version. + +### amp-geo hotpatching + +[amp-geo](https://amp.dev/documentation/components/amp-geo/) requires special attention when hosting the AMP framework. When `cdn.ampproject.org` serves any of `amp-geo-0.1.js`, `amp-geo-0.1.mjs`, `amp-geo-latest.js`, or `amp-geo-latest.mjs`, it detects the country and subdivision where the request originated and replaces string `{{AMP_ISO_COUNTRY_HOTPATCH}}` with region data and enough trailing spaces to maintain the original string length. Ideally, when hosting the AMP framework, your content distribution platform would perform the same manipulation. The logic is as follows: + +- If country could not be determined, substitute 28 spaces: + ``` + Before: "{{AMP_ISO_COUNTRY_HOTPATCH}}" + After: " " + ``` +- If country could be determined, substitute the ISO 3166-1 alpha-2 country code followed by 26 spaces: + ``` + Before: "{{AMP_ISO_COUNTRY_HOTPATCH}}" + After: "xx " + ``` + where `xx` is the country code (e.g. `de` for Germany). +- If country and subdivision could be determined and are exactly `us` (United States) and `ca` (California), respectively (this is the only subdivision supported by amp-geo as of this writing), substitute the ISO 3166-1 alpha-2 country code _and_ the ISO 3166-2 country-subdivision code followed by 20 spaces: + ``` + Before: "{{AMP_ISO_COUNTRY_HOTPATCH}}" + After: "us us-ca " + ``` + +#### amp-geo fallback API + +If location detection and file modification at time of delivery are not possible, amp-geo supports use of an API to fetch the user's country at run time. Be aware that usage of an API for this feature increases the chances of visible content or style shifts due to the delay of an additional network request. The AMP Project does not provide this API service; you need to supply your own API. Two example providers (not a comprehensive list) that offer IP-to-country databases from which an API could be created include [MaxMind](https://www.maxmind.com) and [IP2Location](https://www.ip2location.com/). + +The API must meet the following requirements: + +- Satisfy [CORS security in AMP](https://github.com/ampproject/amphtml/blob/master/spec/amp-cors-requests.md) +- Be secure (HTTPS) +- Return `application/json` content conforming to the following schema: + ``` + { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "country": { + "type": "string", + "title": "ISO 3166-1 alpha-2 (case insensitive) country code of client request", + "default": "", + "pattern": "^[a-zA-Z]{2}$" + }, + "subdivision": { + "type": "string", + "title": "Subdivision part of ISO 3166-2 (case insensitive) country-subdivision code of client request", + "default": "", + "pattern": "^[a-zA-Z0-9]{1,3}$" + } + }, + "required": [ + "country" + ] + } + ``` + +A sample response for a user in Germany looks like: + +``` +{ + "country": "de" +} +``` + +A sample response for a user in California, US looks like: + +``` +{ + "country": "us", + "subdivision": "ca" +} +``` + +Note: As of April 2020, `us-ca` is the only subdivision supported by amp-geo. + +There are trade-offs in accuracy and performance when you set the client cache time for this API. Longer cache times are better for performance on subsequent page loads but can lead to incorrect country detection. For reference, `https://cdn.ampproject.org/v0/amp-geo-0.1.js` has a client cache time of 30 minutes. + +### HTTP response headers + +In addition to following [TLS best practices](https://infosec.mozilla.org/guidelines/web_security), consider the following headers when hosting the AMP framework: + +- `content-security-policy`: If your pages implement [AMP's CSP](https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/secure-pages/), apply a matching content security policy to your hosted framework responses. Inspect the headers on `https://cdn.ampproject.org/v0.js` for a base policy that should be expanded to include resources served from your host. +- `access-control-allow-origin`: Some runtime components are fetched via XHR. If your AMP pages will be served from a different host than your framework, be sure to include CORS headers (see also [CORS Requests in AMP](https://github.com/ampproject/amphtml/blob/master/spec/amp-cors-requests.md)). +- `content-type`: There are a few resources served without file extensions, or with extensions that may not be recognized by all web servers. In addition to [common types](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types), you may want to include special handling for the following: + + - `/rtv/metadata` - `application/json` + - `/amp_preconnect_polyfill_404_or_other_error_expected._Do_not_worry_about_it` - `text/html` + + A complete list of files in each AMP release can be found in `files.txt`, for example `https://cdn.ampproject.org/files.txt`. + +- `cache-control`: The AMP framework hosted from versioned URLs should be "immutable"; users should expect to find the same content from these URLs for as long as the URLs are active (amp-geo is an exception, see below). Long cache times are appropriate. On the other hand, the AMP framework hosted from versionless URLs should be served with relatively short cache times so that minimal time is required for your latest update to reach all users. + - Versioned URL example: `cdn.ampproject.org` sets a 1 year client cache time on resources served under `cdn.ampproject.org/rtv/`: `cache-control: public, max-age=31536000`. + - Versionless URL example: `cdn.ampproject.org` sets a 50 minute client cache time for resources served from versionless URLs, but also allows a long 2 week stale-while-revalidate time in the event that versionless URLs experience an outage: `cache-control: private, max-age=3000, stale-while-revalidate=1206600`. + - `amp-geo-*.(m)js`: If amp-geo hotpatching is utilized, there is a trade-off in accuracy and performance when you set this cache time. Longer cache times are better for performance on subsequent page loads but can lead to incorrect country detection. Both `cdn.ampproject.org/v0/amp-geo-0.1.js` and `cdn.ampproject.org/rtv//v0/amp-geo-0.1.js` set the client cache time to 30 minutes. From 947a5770c3836f5d1ab7acb1eb2f8c520b27b658 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 6 May 2020 16:50:45 +0200 Subject: [PATCH 02/62] =?UTF-8?q?=F0=9F=93=A6=20Update=20dependency=20fetc?= =?UTF-8?q?h-mock=20to=20v9.5.0=20(#28211)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6c26b9b24744..4878d58d5775 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,7 @@ "event-stream": "4.0.1", "express": "4.17.1", "fancy-log": "1.3.3", - "fetch-mock": "9.4.0", + "fetch-mock": "9.5.0", "formidable": "1.2.2", "fs-extra": "9.0.0", "fuse.js": "5.2.3", diff --git a/yarn.lock b/yarn.lock index 0f6638c84f30..226c05b6daae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6793,10 +6793,10 @@ fbjs@^0.8.4: setimmediate "^1.0.5" ua-parser-js "^0.7.18" -fetch-mock@9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-9.4.0.tgz#9be073577bcfa57af714ca91f7536aff8450ec88" - integrity sha512-tqnFmcjYheW5Z9zOPRVY+ZXjB/QWCYtPiOrYGEsPgKfpGHco97eaaj7Rv9MjK7PVWG4rWfv6t2IgQAzDQizBZA== +fetch-mock@9.5.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-9.5.0.tgz#aca0759fbfb6653f66bc28ac63adeb75645f0d17" + integrity sha512-x5t6+6owOr2x8WdloBpuKy7bRZ+JRjRJjjZSsEbBVNfUI8JzJfRw5TtdOOd+OyWXxqQtMjMLdRWh35ZLhRlv3g== dependencies: babel-runtime "^6.26.0" core-js "^3.0.0" From cec4cce04108ac952f00478f300e9c4f005c8319 Mon Sep 17 00:00:00 2001 From: Raghu Simha Date: Wed, 6 May 2020 11:27:19 -0400 Subject: [PATCH 03/62] =?UTF-8?q?=F0=9F=8F=97=20Reduce=20coverage=20thresh?= =?UTF-8?q?old=20for=20`codecov`=20check=20(#28215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Reduce threshold coverage for codecov checks * Revert change to upload logic * Don't require entire CI build to pass --- .codecov.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.codecov.yml b/.codecov.yml index 7072c0262cbf..95023199dd73 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -5,9 +5,9 @@ codecov: ci: - 'travis.org' max_report_age: 24 - require_ci_to_pass: yes + require_ci_to_pass: no notify: - wait_for_ci: yes + wait_for_ci: no # Pull request comments disabled because they were too noisy # See https://docs.codecov.io/docs/pull-request-comments @@ -18,19 +18,19 @@ comment: false coverage: precision: 2 round: down - range: '80...100' + range: '75...100' status: project: default: base: auto if_not_found: success only_pulls: true - target: 80% + target: 75% threshold: 1% patch: default: base: auto if_not_found: success only_pulls: true - target: 80% + target: 75% threshold: 1% From 3a2dc87422c67b1b36db006abf6c76238d9c80c6 Mon Sep 17 00:00:00 2001 From: Kevin Kimball Date: Wed, 6 May 2020 09:48:00 -0700 Subject: [PATCH 04/62] Remove experiment for fixedlayer due to failing sidebar test (#28228) --- build-system/global-configs/experiments-config.json | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/build-system/global-configs/experiments-config.json b/build-system/global-configs/experiments-config.json index 6aff5ed333c9..8f0b87d0f3fb 100644 --- a/build-system/global-configs/experiments-config.json +++ b/build-system/global-configs/experiments-config.json @@ -7,13 +7,6 @@ "expirationDateUTC": "2020-05-31", "defineExperimentConstant": "INTERSECTION_OBSERVER_POLYFILL" }, - "experimentB": { - "name": "MOVE_FIXED_LAYER", - "environment": "AMP", - "command": "gulp dist --define_experiment_constant=MOVE_FIXED_LAYER", - "issue": "", - "expiration_date_utc": "2020-12-31", - "define_experiment_constant": "MOVE_FIXED_LAYER" - }, + "experimentB": {}, "experimentC": {} } From 396c36c148ae1a4f4584cb0e3b0af116f7a8c44e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Su=20Zhang=20=28=E5=BC=B5=E7=94=A6=29?= Date: Wed, 6 May 2020 13:15:52 -0400 Subject: [PATCH 05/62] =?UTF-8?q?=F0=9F=93=96=20Exclude=20`amp-state:`=20d?= =?UTF-8?q?ocumentation=20for=20email=20(#28226)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Exclude `amp-state:` documentation for email This is not supported in the AMP for Email spec. * Run prettify --- extensions/amp-list/amp-list.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/amp-list/amp-list.md b/extensions/amp-list/amp-list.md index d4867a1bea87..635f4aa07a22 100644 --- a/extensions/amp-list/amp-list.md +++ b/extensions/amp-list/amp-list.md @@ -220,6 +220,8 @@ In several cases, we may need the `` to resize on user interaction. Fo ``` +[filter formats="websites, stories"] + ### Initialization from amp-state In most cases, you’ll probably want to have `` request JSON from a server. But `` can also use JSON you’ve included in an ``, right there in your HTML! This means rendering can occur without an additional server call, although, of course, if your page is served from an AMP cache, the data may not be fresh. @@ -249,6 +251,8 @@ See below for a full example, ``` +[/filter] + ## Attributes ### src (required) From 579e523aa998e7e07eaeb72d1d3949c190baf479 Mon Sep 17 00:00:00 2001 From: qidonna <968756+qidonna@users.noreply.github.com> Date: Wed, 6 May 2020 10:48:19 -0700 Subject: [PATCH 06/62] Add subscription-message-number to SwG smartbox button. This is allow users to control the message on the button. Example is updated accordingly. (#28213) --- .../amp-subscriptions-smartbox.amp.html | 9 ++++----- .../0.1/amp-subscriptions-google.js | 6 ++++++ .../0.1/test/test-amp-subscriptions-google.js | 14 ++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) rename examples/{ => amp-subscriptions-google}/amp-subscriptions-smartbox.amp.html (96%) diff --git a/examples/amp-subscriptions-smartbox.amp.html b/examples/amp-subscriptions-google/amp-subscriptions-smartbox.amp.html similarity index 96% rename from examples/amp-subscriptions-smartbox.amp.html rename to examples/amp-subscriptions-google/amp-subscriptions-smartbox.amp.html index 3fadcb4084fd..ecf679b9d9f7 100644 --- a/examples/amp-subscriptions-smartbox.amp.html +++ b/examples/amp-subscriptions-google/amp-subscriptions-smartbox.amp.html @@ -215,18 +215,16 @@ +

You need to append "#swg.experiments=smartbox" at the end of the URL and then refresh.

- - - -

"Smart Button (Light)"

"Smart Button (Dark)"

@@ -235,6 +233,7 @@

"Smart Button (Dark)"

subscriptions-lang="en" subscriptions-action="subscribe-smartbutton-dark" subscriptions-service="subscribe.google.com" + subscriptions-message-number="1" subscriptions-decorate>Yes I will Contribute

"Smart Button (Dark w/ custom message text color)"

@@ -244,8 +243,8 @@

"Smart Button (Dark w/ custom message text color)"

subscriptions-action="subscribe-smartbutton-dark" subscriptions-service="subscribe.google.com" subscriptions-message-text-color="rgba(0, 120, 255, 0.95)" + subscriptions-message-number="1" subscriptions-decorate>Yes I will Contribute - diff --git a/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js b/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js index 3d0ff75ee13a..1e5e0bd5631b 100644 --- a/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js +++ b/extensions/amp-subscriptions-google/0.1/amp-subscriptions-google.js @@ -579,6 +579,12 @@ export class GoogleSubscriptionsPlatform { if (messageTextColor) { opts.messageTextColor = messageTextColor; } + const messageNumber = element.getAttribute( + 'subscriptions-message-number' + ); + if (messageNumber) { + opts.messageNumber = messageNumber; + } this.runtime_.attachSmartButton(element, opts, () => {}); break; default: diff --git a/extensions/amp-subscriptions-google/0.1/test/test-amp-subscriptions-google.js b/extensions/amp-subscriptions-google/0.1/test/test-amp-subscriptions-google.js index e7b463e7f504..515b9775e4ff 100644 --- a/extensions/amp-subscriptions-google/0.1/test/test-amp-subscriptions-google.js +++ b/extensions/amp-subscriptions-google/0.1/test/test-amp-subscriptions-google.js @@ -673,6 +673,20 @@ describes.realWin('amp-subscriptions-google', {amp: true}, (env) => { }); }); + it('should use message number', () => { + const elem = env.win.document.createElement('div'); + const attachStub = env.sandbox.stub(platform.runtime_, 'attachSmartButton'); + elem.textContent = 'some html'; + elem.setAttribute('subscriptions-lang', 'en'); + elem.setAttribute('subscriptions-message-number', 1); + platform.decorateUI(elem, 'subscribe-smartbutton'); + expect(attachStub).to.be.calledWith(elem, { + lang: 'en', + theme: 'light', + messageNumber: '1', // Message is 'Subscribe with just a few taps' on smartbox button. + }); + }); + it('should throw if smartButton language is missing', () => { //expectAsyncConsoleError(/must have a language attrbiute​​​/); const elem = env.win.document.createElement('div'); From a0c0d0a0e1fef9cffb40c4fd22f78e7ea970ea91 Mon Sep 17 00:00:00 2001 From: Alan Orozco Date: Wed, 6 May 2020 11:11:18 -0700 Subject: [PATCH 07/62] =?UTF-8?q?=F0=9F=8F=97=20Configure=20Closure=20Comp?= =?UTF-8?q?ile=20to=20use=20ECMASCRIPT=5F2020=20(#28240)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows: - https://v8.dev/features/nullish-coalescing - https://v8.dev/features/optional-catch-binding - https://v8.dev/features/optional-chaining (whenever closure adds support) --- build-system/compile/compile.js | 2 +- build-system/compile/single-pass.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build-system/compile/compile.js b/build-system/compile/compile.js index 6123fd11cd7e..8318a8ef3a23 100644 --- a/build-system/compile/compile.js +++ b/build-system/compile/compile.js @@ -266,7 +266,7 @@ function compile( compilation_level: options.compilationLevel || 'SIMPLE_OPTIMIZATIONS', // Turns on more optimizations. assume_function_wrapper: true, - language_in: 'ECMASCRIPT_2018', + language_in: 'ECMASCRIPT_2020', // Do not transpile down to ES5 if running with `--esm`, since we do // limited transpilation in Babel. language_out: argv.esm ? 'NO_TRANSPILE' : 'ECMASCRIPT5', diff --git a/build-system/compile/single-pass.js b/build-system/compile/single-pass.js index 5dd692b623fe..43579fd53be6 100644 --- a/build-system/compile/single-pass.js +++ b/build-system/compile/single-pass.js @@ -107,7 +107,7 @@ exports.getFlags = function (config) { // accessing the symbol, we remedy this by attaching all public exports // to `_` and everything imported across modules is is accessed through `_`. rename_prefix_namespace: '_', - language_in: 'ECMASCRIPT_2018', + language_in: 'ECMASCRIPT_2020', language_out: config.esm ? 'NO_TRANSPILE' : config.language_out || 'ECMASCRIPT5', From d9a89470e1c50183f6c2b6f494a8da7a0f68d216 Mon Sep 17 00:00:00 2001 From: CrystalOnScript Date: Wed, 6 May 2020 11:41:27 -0700 Subject: [PATCH 08/62] updated amp-3d-gltf format for amp-rdu round 1 (#27913) --- extensions/amp-3d-gltf/amp-3d-gltf.md | 129 +++++++++++++++----------- 1 file changed, 77 insertions(+), 52 deletions(-) diff --git a/extensions/amp-3d-gltf/amp-3d-gltf.md b/extensions/amp-3d-gltf/amp-3d-gltf.md index c97fd927193e..4658f75f1748 100644 --- a/extensions/amp-3d-gltf/amp-3d-gltf.md +++ b/extensions/amp-3d-gltf/amp-3d-gltf.md @@ -28,7 +28,9 @@ limitations under the License. The `amp-3d-gltf` component displays 3D models that are in gITF format. -**Note**: A WebGL capable browser is required to display these models. +[tip type="note"] +You must use a [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API)-capable browser to display 3D models. +[/tip] ### Example @@ -43,67 +45,90 @@ The `amp-3d-gltf` component displays 3D models that are in gITF format. > ``` -### Limitations +### Compatibility -Currently, only works with glTF 2.0. +The component supports [glTF 2.0](https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#cameras), +with the following exceptions: -Unsupported features: +- Embedded cameras +- Animation -- embeded cameras -- animation +### Set CORS headers -### CORS - -`amp-3d-gltf` makes a `fetch` request from the origin `https://.ampproject.net` so `access-control-allow-origin: *.ampproject.net` must be set on the response header of the endpoint specified as `src`. Wildcard is needed since the origin has a random sub-domain component to it. +The `amp-3d-gltf` component makes a fetch request to the origin from +`https://.ampproject.net` when it requests the gltf file. Set +`access-control-allow-origin:*.ampproject.net` on the response header of the +`src` endpoint to avoid +[CORS issues when served from an AMP cache](https://amp.dev/documentation/guides-and-tutorials/learn/amp-caches-and-cors/amp-cors-requests/?format=websites). +Use a wildcard character to address the `` subdomain component. ## Attributes - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
src [required]A required attribute that specifies the URL to the gltf file.
alpha [optional]A Boolean attribute that specifies whether free space on canvas is transparent. By default, free space is filled with black. -Default value is false.
antialiasing [optional]A Boolean attribute that specifies whether to turn on antialiasing. Default value is false.
clearColor [optional]A string that must contain valid CSS color, that will be used to fill free space on canvas.
maxPixelRatio [optional]A numeric value that specifies the upper limit for the pixelRatio render option. The default is window.devicePixelRatio.
autoRotate [optional]A Boolean attribute that specifies whether to automatically rotate the camera around the model's center. Default value is false.
enableZoom [optional]A Boolean attribute that specifies whether to turn on zoom. Default value is true.
+### src + +The `src` attribute specifies the gltf file location. + +### alpha (optional) + +Use the `alpha` attribute to specify the behavior of the canvas background. +This attribute takes a boolean value. To enable transparency instead of the +default white, set the value to `true`. + +### antialiasing (optional) + +Use the `antialiasing` attribute to specify whether to enable antialiasing. To +enable antialiasing, set the value to true. + +### clearColor (optional) + +The `clearColor` attribute specifies a CSS color, such as #FF8C00. This color +fills free space on the canvas. + +### maxPixelRatio (optional) + +The `maxPixelRatio` attribute specifies an upper limit for the `pixelRatio` +render option. It defaults to `window.devicePixelRatio`. + +### autoRotate (optional) + +Use the `autoRotate` attribute to enable automatic rotation around the model's +center. To enable rotation, set the value to `true`. `autorotate` defaults to +`false`. + +### enableZoom (optional) + +Use the `enableZoom` attribute to disable zooming in and out of the model. To +disable zoom, set the value to `false`. `autororate` defaults to `true`. ## Actions - - - - - -
setModelRotation(x, y, z, xMin, xMax, yMin, yMax, zMin, zMax)sets model rotation. rotation order is ZYX -
    -
  • x/y/z - number 0..1, defaults to previous value of model rotation.
  • -
  • min/max - angle in radians, defaults to 0 / pi * 2, defines target range
  • -
- for example `setModelRotation(x=0.5, xMin=0, xMax=3.14)` will change `x` component of rotation to `1.57`.
+### setModelRotation + +The `setModelRotation` action specifies the model's rotation. The rotation order +is ZYX. + +- Specify the rotation value of the x, y, and/or z axis with `x`, `y`, and/or + `z` arguments. This action accepts a number between 0 and 1. It defaults to the + previous value. +- Specify the angle of rotation in radians with `xMin`, `xMax`, `yMin`, `yMax`, + and/or `zMin`, `zMax` arguments. Use a min and a max to define the target range. + The angle range defaults to `0 / pi * 2`. + +The following action changes the x axis of the component’s rotation to 1.57 when fired. + +```json +setModelRotation((x = 0.5), (xMin = 0), (xMax = 3.14)) +``` + +## Styling + +Here are a few things to keep in mind for style: + +- The `alpha` attribute specifies transparency. +- The `clearColor` attribute specifies the color of the background if it isn't transparent. +- `amp-3d-gltf` defaults to a white background if you don't specify a background color. ## Validation -See [amp-3d-gltf rules](https://github.com/ampproject/amphtml/blob/master/extensions/amp-3d-gltf/validator-amp-3d-gltf.protoascii) in the AMP validator specification. +See [amp-3d-gltf rules](https://github.com/ampproject/amphtml/blob/master/extensions/amp-3d-gltf/validator-amp-3d-gltf.protoascii) +in the AMP validator specification. From b431a5ffd56b563f997b310925b7379e85507a42 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Wed, 6 May 2020 22:07:07 +0200 Subject: [PATCH 09/62] =?UTF-8?q?=F0=9F=93=A6=20Update=20dependency=20pupp?= =?UTF-8?q?eteer=20to=20v3.0.3=20(#28239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build-system/tasks/e2e/package.json | 2 +- build-system/tasks/e2e/yarn.lock | 8 ++++---- build-system/tasks/performance/package.json | 2 +- build-system/tasks/performance/yarn.lock | 8 ++++---- build-system/tasks/visual-diff/package.json | 2 +- build-system/tasks/visual-diff/yarn.lock | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/build-system/tasks/e2e/package.json b/build-system/tasks/e2e/package.json index 9ece0207a85f..6d162dc19130 100644 --- a/build-system/tasks/e2e/package.json +++ b/build-system/tasks/e2e/package.json @@ -7,7 +7,7 @@ "@babel/register": "7.9.0", "babel-regenerator-runtime": "6.5.0", "chromedriver": "81.0.0", - "puppeteer": "3.0.2", + "puppeteer": "3.0.3", "geckodriver": "1.19.1", "selenium-webdriver": "4.0.0-alpha.7" }, diff --git a/build-system/tasks/e2e/yarn.lock b/build-system/tasks/e2e/yarn.lock index 7acdf01bc912..ab1548a05b32 100644 --- a/build-system/tasks/e2e/yarn.lock +++ b/build-system/tasks/e2e/yarn.lock @@ -882,10 +882,10 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -puppeteer@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.0.2.tgz#1d08cdb7c0c2666f5e743221b1cb1946fea493f0" - integrity sha512-5jS/POFVDW9fqb76O8o0IBpXOnq+Na8ocGMggYtnjCRBRqmAFvX0csmwgLOHkYnQ/vCBcBPYlOq0Pp60z1850Q== +puppeteer@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.0.3.tgz#91c4ce46b3822b849e096413457fe8ced7d5aeca" + integrity sha512-sWtx9XezIwXdXHNelvnyPEmMjXXiCAuUulEONZFqRO3l+w0vP7dNcGDUIf2HLr4HLLi6r9hcUuO62s/W6rkD/w== dependencies: "@types/mime-types" "^2.1.0" debug "^4.1.0" diff --git a/build-system/tasks/performance/package.json b/build-system/tasks/performance/package.json index 2c2c347de469..a243eb76661d 100644 --- a/build-system/tasks/performance/package.json +++ b/build-system/tasks/performance/package.json @@ -6,6 +6,6 @@ "devDependencies": { "chai": "4.2.0", "mocha": "7.1.2", - "puppeteer": "3.0.2" + "puppeteer": "3.0.3" } } diff --git a/build-system/tasks/performance/yarn.lock b/build-system/tasks/performance/yarn.lock index c17efe8a2c6a..3c31433122f6 100644 --- a/build-system/tasks/performance/yarn.lock +++ b/build-system/tasks/performance/yarn.lock @@ -728,10 +728,10 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -puppeteer@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.0.2.tgz#1d08cdb7c0c2666f5e743221b1cb1946fea493f0" - integrity sha512-5jS/POFVDW9fqb76O8o0IBpXOnq+Na8ocGMggYtnjCRBRqmAFvX0csmwgLOHkYnQ/vCBcBPYlOq0Pp60z1850Q== +puppeteer@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.0.3.tgz#91c4ce46b3822b849e096413457fe8ced7d5aeca" + integrity sha512-sWtx9XezIwXdXHNelvnyPEmMjXXiCAuUulEONZFqRO3l+w0vP7dNcGDUIf2HLr4HLLi6r9hcUuO62s/W6rkD/w== dependencies: "@types/mime-types" "^2.1.0" debug "^4.1.0" diff --git a/build-system/tasks/visual-diff/package.json b/build-system/tasks/visual-diff/package.json index d8a436d2ade9..650c2ba77b06 100644 --- a/build-system/tasks/visual-diff/package.json +++ b/build-system/tasks/visual-diff/package.json @@ -6,7 +6,7 @@ "devDependencies": { "@percy/agent": "0.26.2", "@percy/puppeteer": "1.1.0", - "puppeteer": "3.0.2" + "puppeteer": "3.0.3" }, "resolutions": { "**/**/minimist": "^1.2.3" diff --git a/build-system/tasks/visual-diff/yarn.lock b/build-system/tasks/visual-diff/yarn.lock index cbb479592b84..066d39a45176 100644 --- a/build-system/tasks/visual-diff/yarn.lock +++ b/build-system/tasks/visual-diff/yarn.lock @@ -1700,10 +1700,10 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.0.2.tgz#1d08cdb7c0c2666f5e743221b1cb1946fea493f0" - integrity sha512-5jS/POFVDW9fqb76O8o0IBpXOnq+Na8ocGMggYtnjCRBRqmAFvX0csmwgLOHkYnQ/vCBcBPYlOq0Pp60z1850Q== +puppeteer@3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-3.0.3.tgz#91c4ce46b3822b849e096413457fe8ced7d5aeca" + integrity sha512-sWtx9XezIwXdXHNelvnyPEmMjXXiCAuUulEONZFqRO3l+w0vP7dNcGDUIf2HLr4HLLi6r9hcUuO62s/W6rkD/w== dependencies: "@types/mime-types" "^2.1.0" debug "^4.1.0" From 1d29dfbc5ebb1bf89a77091afaa2a712457c1db6 Mon Sep 17 00:00:00 2001 From: Malte Ubl Date: Wed, 6 May 2020 14:26:11 -0700 Subject: [PATCH 10/62] =?UTF-8?q?=F0=9F=9A=80=20Allow=20prerendering=20of?= =?UTF-8?q?=20more=20elements=20(#28203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - amp-accordion - amp-base-carousel - amp-carousel 0.2 - amp-inline-gallery - amp-stream-gallery - amp-mega-menu - amp-nested-menu - amp-selector - amp-sidebar - amp-stream-gallery Some of these likely were just oversight. Others were not opted in because they don't have a `layoutCallback` but nowadays `buildCallback` is also guarded by `prerenderAllowed`. --- extensions/amp-accordion/0.1/amp-accordion.js | 5 +++++ extensions/amp-base-carousel/0.1/amp-base-carousel.js | 5 +++++ extensions/amp-carousel/0.2/amp-carousel.js | 5 +++++ .../amp-inline-gallery/0.1/amp-inline-gallery-pagination.js | 5 +++++ .../amp-inline-gallery/0.1/amp-inline-gallery-thumbnails.js | 5 +++++ extensions/amp-inline-gallery/0.1/amp-inline-gallery.js | 5 +++++ extensions/amp-mega-menu/0.1/amp-mega-menu.js | 5 +++++ extensions/amp-nested-menu/0.1/amp-nested-menu.js | 5 +++++ extensions/amp-selector/0.1/amp-selector.js | 5 +++++ extensions/amp-sidebar/0.2/amp-sidebar.js | 5 +++++ extensions/amp-stream-gallery/0.1/amp-stream-gallery.js | 5 +++++ 11 files changed, 55 insertions(+) diff --git a/extensions/amp-accordion/0.1/amp-accordion.js b/extensions/amp-accordion/0.1/amp-accordion.js index f79e764ca7b7..e647d43c2ada 100644 --- a/extensions/amp-accordion/0.1/amp-accordion.js +++ b/extensions/amp-accordion/0.1/amp-accordion.js @@ -82,6 +82,11 @@ class AmpAccordion extends AMP.BaseElement { return layout == Layout.CONTAINER; } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { this.action_ = Services.actionServiceForDoc(this.element); diff --git a/extensions/amp-base-carousel/0.1/amp-base-carousel.js b/extensions/amp-base-carousel/0.1/amp-base-carousel.js index 7347190569aa..bd101593f143 100644 --- a/extensions/amp-base-carousel/0.1/amp-base-carousel.js +++ b/extensions/amp-base-carousel/0.1/amp-base-carousel.js @@ -159,6 +159,11 @@ class AmpCarousel extends AMP.BaseElement { return isLayoutSizeDefined(layout); } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { this.action_ = Services.actionServiceForDoc(this.element); diff --git a/extensions/amp-carousel/0.2/amp-carousel.js b/extensions/amp-carousel/0.2/amp-carousel.js index a6f3ed800ec6..e829c34e3270 100644 --- a/extensions/amp-carousel/0.2/amp-carousel.js +++ b/extensions/amp-carousel/0.2/amp-carousel.js @@ -122,6 +122,11 @@ class AmpCarousel extends AMP.BaseElement { return isLayoutSizeDefined(layout); } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { this.action_ = Services.actionServiceForDoc(this.element); diff --git a/extensions/amp-inline-gallery/0.1/amp-inline-gallery-pagination.js b/extensions/amp-inline-gallery/0.1/amp-inline-gallery-pagination.js index b0f25fc91b79..5062ca21df48 100644 --- a/extensions/amp-inline-gallery/0.1/amp-inline-gallery-pagination.js +++ b/extensions/amp-inline-gallery/0.1/amp-inline-gallery-pagination.js @@ -73,6 +73,11 @@ export class AmpInlineGalleryPagination extends AMP.BaseElement { return layout == Layout.FIXED_HEIGHT; } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { this.element.appendChild(this.createDom_()); diff --git a/extensions/amp-inline-gallery/0.1/amp-inline-gallery-thumbnails.js b/extensions/amp-inline-gallery/0.1/amp-inline-gallery-thumbnails.js index bcf7deae8285..9b47417d687e 100644 --- a/extensions/amp-inline-gallery/0.1/amp-inline-gallery-thumbnails.js +++ b/extensions/amp-inline-gallery/0.1/amp-inline-gallery-thumbnails.js @@ -54,6 +54,11 @@ export class AmpInlineGalleryThumbnails extends AMP.BaseElement { return isLayoutSizeDefined(layout); } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { const aspectRatioWidth = diff --git a/extensions/amp-inline-gallery/0.1/amp-inline-gallery.js b/extensions/amp-inline-gallery/0.1/amp-inline-gallery.js index b9cd70ef5924..c359e1718e58 100644 --- a/extensions/amp-inline-gallery/0.1/amp-inline-gallery.js +++ b/extensions/amp-inline-gallery/0.1/amp-inline-gallery.js @@ -85,6 +85,11 @@ class AmpInlineGallery extends AMP.BaseElement { }); } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ isLayoutSupported(layout) { return layout === Layout.CONTAINER; diff --git a/extensions/amp-mega-menu/0.1/amp-mega-menu.js b/extensions/amp-mega-menu/0.1/amp-mega-menu.js index 11855c7585be..a51ceb4ca4f2 100644 --- a/extensions/amp-mega-menu/0.1/amp-mega-menu.js +++ b/extensions/amp-mega-menu/0.1/amp-mega-menu.js @@ -88,6 +88,11 @@ export class AmpMegaMenu extends AMP.BaseElement { return layout === Layout.FIXED_HEIGHT; } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { this.action_ = Services.actionServiceForDoc(this.element); diff --git a/extensions/amp-nested-menu/0.1/amp-nested-menu.js b/extensions/amp-nested-menu/0.1/amp-nested-menu.js index 9521a96398b9..967b2697ddb4 100644 --- a/extensions/amp-nested-menu/0.1/amp-nested-menu.js +++ b/extensions/amp-nested-menu/0.1/amp-nested-menu.js @@ -120,6 +120,11 @@ export class AmpNestedMenu extends AMP.BaseElement { return layout == Layout.FILL; } + /** @override */ + prerenderAllowed() { + return true; + } + /** * Handler for click event on any element inside the component. * @param {!Event} e diff --git a/extensions/amp-selector/0.1/amp-selector.js b/extensions/amp-selector/0.1/amp-selector.js index 69c5a8ebd131..ac932bda6cfa 100644 --- a/extensions/amp-selector/0.1/amp-selector.js +++ b/extensions/amp-selector/0.1/amp-selector.js @@ -81,6 +81,11 @@ export class AmpSelector extends AMP.BaseElement { return true; } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { this.action_ = Services.actionServiceForDoc(this.element); diff --git a/extensions/amp-sidebar/0.2/amp-sidebar.js b/extensions/amp-sidebar/0.2/amp-sidebar.js index d1a364000be0..16a4c0c96fa3 100644 --- a/extensions/amp-sidebar/0.2/amp-sidebar.js +++ b/extensions/amp-sidebar/0.2/amp-sidebar.js @@ -137,6 +137,11 @@ export class AmpSidebar extends AMP.BaseElement { ); } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { // TODO(#25022): remove this assert when cleaning up experiment post launch. diff --git a/extensions/amp-stream-gallery/0.1/amp-stream-gallery.js b/extensions/amp-stream-gallery/0.1/amp-stream-gallery.js index c598671619b6..376afaa58e18 100644 --- a/extensions/amp-stream-gallery/0.1/amp-stream-gallery.js +++ b/extensions/amp-stream-gallery/0.1/amp-stream-gallery.js @@ -268,6 +268,11 @@ class AmpStreamGallery extends AMP.BaseElement { return isLayoutSizeDefined(layout); } + /** @override */ + prerenderAllowed() { + return true; + } + /** @override */ buildCallback() { userAssert( From 70dc3b7304d0aad03cf65a553bd8f4b67d1d1768 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhou Date: Wed, 6 May 2020 15:42:02 -0700 Subject: [PATCH 11/62] delete skipped tests (#28242) --- .../{test-config-new.js => test-config.js} | 0 .../amp-analytics/0.1/test/test-vendors.js | 263 ------------------ 2 files changed, 263 deletions(-) rename extensions/amp-analytics/0.1/test/{test-config-new.js => test-config.js} (100%) delete mode 100644 extensions/amp-analytics/0.1/test/test-vendors.js diff --git a/extensions/amp-analytics/0.1/test/test-config-new.js b/extensions/amp-analytics/0.1/test/test-config.js similarity index 100% rename from extensions/amp-analytics/0.1/test/test-config-new.js rename to extensions/amp-analytics/0.1/test/test-config.js diff --git a/extensions/amp-analytics/0.1/test/test-vendors.js b/extensions/amp-analytics/0.1/test/test-vendors.js deleted file mode 100644 index 7c7ff737dece..000000000000 --- a/extensions/amp-analytics/0.1/test/test-vendors.js +++ /dev/null @@ -1,263 +0,0 @@ -/** - * 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 {ANALYTICS_CONFIG} from '../vendors'; -import {AmpAnalytics} from '../amp-analytics'; -import {ExpansionOptions, variableServiceForDoc} from '../variables'; -//import {IFRAME_TRANSPORTS} from '../iframe-transport-vendors'; -import { - ImagePixelVerifier, - mockWindowInterface, -} from '../../../../testing/test-helper'; -import {Services} from '../../../../src/services'; -//import {hasOwn} from '../../../../src/utils/object'; -import {macroTask} from '../../../../testing/yield'; - -// TODO(zhouyx@): Remove after ANALYTICS_VENDOR_SPLIT clean up - -/* global require: false */ -const VENDOR_REQUESTS = require('./vendor-requests.json'); -const AnalyticsConfig = {...ANALYTICS_CONFIG}; - -// TODO(zhouyx@) Fix the "describe block" if we are going to revert the test -// "Top-level "describe" blocks in test files have been deprecated" - -// describe.skip('iframe transport', () => { -// it('Should not contain iframe transport if not whitelisted', () => { -// for (const vendor in AnalyticsConfig) { -// const vendorEntry = AnalyticsConfig[vendor]; -// if ( -// hasOwn(vendorEntry, 'transport') && -// hasOwn(vendorEntry.transport, 'iframe') -// ) { -// expect(vendorEntry['transport']['iframe']).to.equal( -// IFRAME_TRANSPORTS[vendor] -// ); -// } -// } -// }); -// }); - -describes.realWin.skip( - 'amp-analytics', - { - amp: { - extensions: ['amp-analytics'], - }, - }, - function (env) { - let win, doc; - let requestVerifier; - let elementMacros; - - beforeEach(() => { - win = env.win; - doc = win.document; - const wi = mockWindowInterface(env.sandbox); - wi.getLocation.returns(win.location); - requestVerifier = new ImagePixelVerifier(wi); - elementMacros = { - 'COOKIE': null, - 'CONSENT_STATE': null, - }; - }); - - function getAnalyticsTag(config, attrs) { - config['transport'] = { - xhrpost: false, - beacon: false, - }; - config = JSON.stringify(config); - const el = doc.createElement('amp-analytics'); - const script = doc.createElement('script'); - script.textContent = config; - script.setAttribute('type', 'application/json'); - el.appendChild(script); - for (const k in attrs) { - el.setAttribute(k, attrs[k]); - } - - doc.body.appendChild(el); - - el.connectedCallback(); - const analytics = new AmpAnalytics(el); - analytics.createdCallback(); - analytics.buildCallback(); - return analytics; - } - - /** - * Clears the properties in the config that should only be used in vendor - * configs. This is needed because we pass in all the vendor requests as - * inline config and iframePings/optout are not allowed to be used without - * AMP team's approval. - * - * @param {!JsonObject} config The inline config to update. - * @return {!JsonObject} - */ - function clearVendorOnlyConfig(config) { - for (const t in config.triggers) { - if (config.triggers[t].iframePing) { - config.triggers[t].iframePing = undefined; - } - } - if (config.optout) { - config.optout = undefined; - } - return config; - } - - describe('vendor request tests', () => { - for (const vendor in AnalyticsConfig) { - if (vendor === 'default') { - continue; - } - const config = AnalyticsConfig[vendor]; - if (!config.requests) { - delete AnalyticsConfig[vendor]; - continue; - } - describe('analytics vendor: ' + vendor, function () { - beforeEach(() => { - // Remove all the triggers to prevent unwanted requests, for instance - // one from a "visible" trigger. Those unwanted requests are a source - // of test flakiness. Especially they will alternate value of var - // $requestCount. - config.triggers = {}; - }); - - for (const name in config.requests) { - it( - 'should produce request: ' + - name + - '. If this test fails update vendor-requests.json', - function* () { - const urlReplacements = Services.urlReplacementsForDoc( - doc.documentElement - ); - const analytics = getAnalyticsTag( - clearVendorOnlyConfig(config) - ); - window.sandbox - .stub(urlReplacements.getVariableSource(), 'get') - .callsFake(function (name) { - expect(this.replacements_).to.have.property(name); - const defaultValue = `_${name.toLowerCase()}_`; - return { - sync: () => defaultValue, - }; - }); - - window.sandbox - .stub(ExpansionOptions.prototype, 'getVar') - .callsFake(function (name) { - let val = this.vars[name]; - if (val == null || val == '') { - val = '!' + name; - } - return val; - }); - analytics.createdCallback(); - analytics.buildCallback(); - yield analytics.layoutCallback(); - - // Have to get service after analytics element is created - const variableService = variableServiceForDoc(doc); - - window.sandbox - .stub(variableService, 'getMacros') - .callsFake(function () { - // Add all the macros in amp-analytics - const merged = {...this.macros_, ...elementMacros}; - - // Change the resolving function - const keys = Object.keys(merged); - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - merged[key] = (opt_param, opt_param2, opt_param3) => { - return `_${key.replace('$', '')}_`; - }; - } - return /** @type {!JsonObject} */ (merged); - }); - - // Wait for event queue to clear. - yield macroTask(); - - analytics.handleEvent_( - { - request: name, - }, - { - vars: Object.create(null), - } - ); - yield macroTask(); - expect(requestVerifier.hasRequestSent()).to.be.true; - let url = requestVerifier.getLastRequestUrl(); - - const vendorData = VENDOR_REQUESTS[vendor]; - if (!vendorData) { - throw new Error( - 'Add vendor ' + vendor + ' to vendor-requests.json' - ); - } - const val = vendorData[name]; - if (val == '') { - url = ''; - } - if (val == null) { - throw new Error( - 'Define ' + - vendor + - '.' + - name + - ' in vendor-requests.json. Expected value: ' + - url - ); - } - - // Write this out for easy copy pasting. - writeOutput(vendor, name, url); - - expect(url).to.equal(val); - } - ); - } - }); - } - }); - } -); - -const actualResults = {}; - -function writeOutput(vendor, name, url) { - if (!actualResults[vendor]) { - actualResults[vendor] = {}; - } - actualResults[vendor][name] = url; - const cnt = Object.keys(AnalyticsConfig[vendor]['requests']).length; - AnalyticsConfig[vendor].testCnt = (AnalyticsConfig[vendor].testCnt || 0) + 1; - if (cnt == AnalyticsConfig[vendor].testCnt) { - delete AnalyticsConfig[vendor]; - if (Object.keys(AnalyticsConfig).length == 1) { - const out = top.document.createElement('div'); - out.textContent = JSON.stringify(actualResults, null, ' '); - top.document.body.insertBefore(out, top.document.body.firstChild); - } - } -} From ecfbc5abe2b5514d28b271b04b10003839cb2d28 Mon Sep 17 00:00:00 2001 From: Micajuine Ho Date: Wed, 6 May 2020 16:08:59 -0700 Subject: [PATCH 12/62] temporarily remove ads (#28241) --- .../performanceTestPages/visible-multiple.html | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/manual/amp-analytics/performanceTestPages/visible-multiple.html b/test/manual/amp-analytics/performanceTestPages/visible-multiple.html index 995195d7383f..a6287dd2b9e1 100644 --- a/test/manual/amp-analytics/performanceTestPages/visible-multiple.html +++ b/test/manual/amp-analytics/performanceTestPages/visible-multiple.html @@ -1,12 +1,13 @@ +text, images, links, and ads. +TODO(micajuineho) add back ads when serving compiled js --> - + Test Article @@ -68,6 +74,50 @@ + + +
+
+
+
+ + + +
+ { `<${pseudoArray(n.args_, n.dimensions_)}>` ); } - if (n instanceof ast.CssDimSizeNode) { + if (n instanceof ast.CssRectNode) { return ( - `DIM<${n.dim_}` + + `RECT<${n.field_}` + `, ${n.selector_ ? '"' + n.selector_ + '"' : null}` + `, ${n.selectionMethod_}>` ); @@ -301,24 +301,37 @@ describes.sandboxed('CSS parse', {}, () => { ).to.equal('CON'); }); - it('should parse a dimension function', () => { + it('should parse a rect function', () => { // Current. - expect(parsePseudo('width()')).to.equal('DIM'); - expect(parsePseudo('height()')).to.equal('DIM'); + expect(parsePseudo('width()')).to.equal('RECT'); + expect(parsePseudo('height()')).to.equal('RECT'); + expect(parsePseudo('x()')).to.equal('RECT'); + expect(parsePseudo('y()')).to.equal('RECT'); // Query. - expect(parsePseudo('width(".sel")')).to.equal('DIM'); + expect(parsePseudo('width(".sel")')).to.equal('RECT'); expect(parsePseudo('WIDTH(".sel > div")')).to.equal( - 'DIM div", null>' + 'RECT div", null>' ); - expect(parsePseudo('height(".sel")')).to.equal('DIM'); + expect(parsePseudo('height(".sel")')).to.equal('RECT'); + expect(parsePseudo('x(".sel")')).to.equal('RECT'); + expect(parsePseudo('x(".sel > div")')).to.equal( + 'RECT div", null>' + ); + expect(parsePseudo('y(".sel")')).to.equal('RECT'); // Closest. expect(parsePseudo('width(closest(".sel"))')).to.equal( - 'DIM' + 'RECT' ); expect(parsePseudo('height(closest(".sel"))')).to.equal( - 'DIM' + 'RECT' + ); + expect(parsePseudo('x(closest(".sel"))')).to.equal( + 'RECT' + ); + expect(parsePseudo('y(closest(".sel"))')).to.equal( + 'RECT' ); }); diff --git a/extensions/amp-animation/0.1/test/test-css-expr-resolve-calc.js b/extensions/amp-animation/0.1/test/test-css-expr-resolve-calc.js index 84bf8f8165d4..2a74e721ee55 100644 --- a/extensions/amp-animation/0.1/test/test-css-expr-resolve-calc.js +++ b/extensions/amp-animation/0.1/test/test-css-expr-resolve-calc.js @@ -179,7 +179,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { it('should resolve left as percent', () => { contextMock.expects('getDimension').returns('w'); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .once(); const node = new ast.CssCalcSumNode( @@ -196,7 +196,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { it('should resolve right as percent', () => { contextMock.expects('getDimension').returns('h'); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .once(); const node = new ast.CssCalcSumNode( @@ -214,7 +214,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { contextMock.expects('getCurrentFontSize').returns(2).once(); contextMock.expects('getDimension').returns('h'); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .once(); const node = new ast.CssCalcSumNode( @@ -230,12 +230,12 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { it('should resolve with dimension function', () => { contextMock - .expects('getElementSize') + .expects('getElementRect') .returns({width: 111, height: 222}) .twice(); const node = new ast.CssCalcSumNode( - new ast.CssDimSizeNode('w', '.sel'), - new ast.CssDimSizeNode('h', '.sel'), + new ast.CssRectNode('w', '.sel'), + new ast.CssRectNode('h', '.sel'), '+' ); // 111px + 222px = 333px @@ -542,7 +542,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { () => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); return [ @@ -561,7 +561,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { () => { contextMock.expects('getDimension').returns('h').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); return [new ast.CssLengthNode(25, 'px'), new ast.CssPercentNode(10)]; @@ -577,7 +577,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { contextMock.expects('getCurrentFontSize').returns(2).atLeast(1); contextMock.expects('getDimension').returns('h').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); return [new ast.CssLengthNode(10, 'em'), new ast.CssPercentNode(10)]; @@ -591,12 +591,12 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { 'should resolve with dimension function', () => { contextMock - .expects('getElementSize') + .expects('getElementRect') .returns({width: 111, height: 222}) .atLeast(1); return [ - new ast.CssDimSizeNode('w', '.sel'), - new ast.CssDimSizeNode('h', '.sel'), + new ast.CssRectNode('w', '.sel'), + new ast.CssRectNode('h', '.sel'), ]; }, // No CSS. @@ -610,7 +610,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { () => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); contextMock.expects('getCurrentFontSize').returns(1).atLeast(1); @@ -631,7 +631,7 @@ describes.sandboxed('CSS resolve calc', {}, (env) => { () => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); contextMock.expects('getCurrentFontSize').returns(1).atLeast(1); diff --git a/extensions/amp-animation/0.1/test/test-css-expr-resolve.js b/extensions/amp-animation/0.1/test/test-css-expr-resolve.js index c7ec0f035f25..c1ff45ec14eb 100644 --- a/extensions/amp-animation/0.1/test/test-css-expr-resolve.js +++ b/extensions/amp-animation/0.1/test/test-css-expr-resolve.js @@ -144,7 +144,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { it('should resolve a percent node w/dimension', () => { contextMock.expects('getDimension').returns('w').twice(); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}); expect(ast.isVarCss('11.5%', false)).to.be.false; const node = new ast.CssPercentNode(11.5); @@ -359,7 +359,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { it('should resolve a percent-length in w-direction', () => { contextMock.expects('getDimension').returns('w'); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .once(); const node = new ast.CssLengthNode(11.5, 'px'); @@ -370,7 +370,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { it('should resolve a percent-length in h-direction', () => { contextMock.expects('getDimension').returns('h'); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .once(); const node = new ast.CssLengthNode(11.5, 'px'); @@ -380,7 +380,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { it('should resolve a percent-length in unknown direction', () => { contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .once(); const node = new ast.CssLengthNode(11.5, 'px'); @@ -520,7 +520,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock.expects('getCurrentFontSize').returns(10).atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); const node = new ast.CssFuncNode('xyz', [ @@ -688,63 +688,97 @@ describes.sandboxed('CSS resolve', {}, (env) => { }); describe('dimension', () => { - let sizes; + let rects; beforeEach(() => { - sizes = { - 'null(.class)': {width: 111, height: 222}, - 'closest(.class > div)': {width: 112, height: 224}, + rects = { + 'null(.class)': {x: 10, y: 20, width: 111, height: 222}, + 'closest(.class > div)': {x: 11, y: 12, width: 112, height: 224}, }; - context.getCurrentElementSize = function () { - return {width: 110, height: 220}; + context.getCurrentElementRect = function () { + return {x: 13, y: 14, width: 110, height: 220}; }; - context.getElementSize = function (selector, selectionMethod) { - return sizes[`${selectionMethod}(${selector})`]; + context.getElementRect = function (selector, selectionMethod) { + return rects[`${selectionMethod}(${selector})`]; }; }); it('should always consider as non-const', () => { expect(ast.isVarCss('width()', false)).to.be.true; expect(ast.isVarCss('height()', false)).to.be.true; + expect(ast.isVarCss('x()', false)).to.be.true; + expect(ast.isVarCss('y()', false)).to.be.true; expect(ast.isVarCss('width("")')).to.be.true; expect(ast.isVarCss('height("")')).to.be.true; + expect(ast.isVarCss('x("")')).to.be.true; + expect(ast.isVarCss('y("")')).to.be.true; }); it('should be always a non-const and no css', () => { - const node = new ast.CssDimSizeNode('?'); + const node = new ast.CssRectNode('?'); expect(node.isConst()).to.be.false; expect(() => node.css()).to.throw(/no css/); }); it('should resolve width on the current node', () => { - const node = new ast.CssDimSizeNode('w'); + const node = new ast.CssRectNode('w'); expect(node.calc(context).css()).to.equal('110px'); }); it('should resolve height on the current node', () => { - const node = new ast.CssDimSizeNode('h'); + const node = new ast.CssRectNode('h'); expect(node.calc(context).css()).to.equal('220px'); }); it('should resolve width on the selected node', () => { - const node = new ast.CssDimSizeNode('w', '.class'); + const node = new ast.CssRectNode('w', '.class'); expect(node.calc(context).css()).to.equal('111px'); }); it('should resolve height on the selected node', () => { - const node = new ast.CssDimSizeNode('h', '.class'); + const node = new ast.CssRectNode('h', '.class'); expect(node.calc(context).css()).to.equal('222px'); }); it('should resolve width on the selected closest node', () => { - const node = new ast.CssDimSizeNode('w', '.class > div', 'closest'); + const node = new ast.CssRectNode('w', '.class > div', 'closest'); expect(node.calc(context).css()).to.equal('112px'); }); it('should resolve height on the selected closest node', () => { - const node = new ast.CssDimSizeNode('h', '.class > div', 'closest'); + const node = new ast.CssRectNode('h', '.class > div', 'closest'); expect(node.calc(context).css()).to.equal('224px'); }); + + it('should resolve x coord on the current node', () => { + const node = new ast.CssRectNode('x'); + expect(node.calc(context).css()).to.equal('13px'); + }); + + it('should resolve y coord on the current node', () => { + const node = new ast.CssRectNode('y'); + expect(node.calc(context).css()).to.equal('14px'); + }); + + it('should resolve x coord on the selected node', () => { + const node = new ast.CssRectNode('x', '.class'); + expect(node.calc(context).css()).to.equal('10px'); + }); + + it('should resolve y coord on the selected node', () => { + const node = new ast.CssRectNode('y', '.class'); + expect(node.calc(context).css()).to.equal('20px'); + }); + + it('should resolve x coord on the selected closest node', () => { + const node = new ast.CssRectNode('x', '.class > div', 'closest'); + expect(node.calc(context).css()).to.equal('11px'); + }); + + it('should resolve y coord on the selected closest node', () => { + const node = new ast.CssRectNode('y', '.class > div', 'closest'); + expect(node.calc(context).css()).to.equal('12px'); + }); }); describe('num-convert', () => { @@ -791,7 +825,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); expect(resolvedCss(node, normalize)).to.equal('11'); @@ -893,7 +927,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); expect(resolvedCss(node, normalize)).to.equal('13.75px'); @@ -1051,7 +1085,7 @@ describes.sandboxed('CSS resolve', {}, (env) => { it('should resolve a var node and normalize', () => { contextMock.expects('getDimension').returns('w').atLeast(1); contextMock - .expects('getCurrentElementSize') + .expects('getCurrentElementRect') .returns({width: 110, height: 220}) .atLeast(1); contextMock diff --git a/extensions/amp-animation/0.1/test/test-web-animations.js b/extensions/amp-animation/0.1/test/test-web-animations.js index 1ed0490009c4..a85b72491577 100644 --- a/extensions/amp-animation/0.1/test/test-web-animations.js +++ b/extensions/amp-animation/0.1/test/test-web-animations.js @@ -1548,13 +1548,13 @@ describes.realWin('MeasureScanner', {amp: 1}, (env) => { target1.style.width = '11px'; target1.style.height = '12px'; allowConsoleError(() => { - expect(() => css.getCurrentElementSize()).to.throw( + expect(() => css.getCurrentElementRect()).to.throw( /target is specified/ ); }); expect( - css.withTarget(target1, 0, () => css.getCurrentElementSize()) - ).to.deep.equal({width: 11, height: 12}); + css.withTarget(target1, 0, () => css.getCurrentElementRect()) + ).to.include({width: 11, height: 12}); }); it('should resolve the selected element size', () => { @@ -1565,23 +1565,65 @@ describes.realWin('MeasureScanner', {amp: 1}, (env) => { target1.appendChild(child); // Normal selectors search whole DOM and don't need context. - expect(css.getElementSize('#target1', null)).to.deep.equal({ + expect(css.getElementRect('#target1', null)).to.include({ width: 11, height: 12, }); expect( - css.withTarget(target2, 0, () => css.getElementSize('#target1', null)) - ).to.deep.equal({width: 11, height: 12}); + css.withTarget(target2, 0, () => css.getElementRect('#target1', null)) + ).to.include({width: 11, height: 12}); // Closest selectors always need a context node. allowConsoleError(() => { - expect(() => css.getElementSize('#target1', 'closest')).to.throw( + expect(() => css.getElementRect('#target1', 'closest')).to.throw( /target is specified/ ); }); expect( - css.withTarget(child, 0, () => css.getElementSize('.parent', 'closest')) - ).to.deep.equal({width: 11, height: 12}); + css.withTarget(child, 0, () => css.getElementRect('.parent', 'closest')) + ).to.include({width: 11, height: 12}); + }); + + it("should resolve current element's position", () => { + target1.style.position = 'absolute'; + target1.style.left = '11px'; + target1.style.top = '12px'; + allowConsoleError(() => { + expect(() => css.getCurrentElementRect()).to.throw( + /target is specified/ + ); + }); + expect( + css.withTarget(target1, 0, () => css.getCurrentElementRect()) + ).to.include({x: 11, y: 12}); + }); + + it("should resolve the selected element's position", () => { + target1.style.position = 'absolute'; + target1.style.left = '11px'; + target1.style.top = '12px'; + target1.classList.add('parent'); + const child = target1.ownerDocument.createElement('div'); + target1.appendChild(child); + + // Normal selectors search whole DOM and don't need context. + expect(css.getElementRect('#target1', null)).to.include({ + x: 11, + y: 12, + }); + expect( + css.withTarget(target2, 0, () => css.getElementRect('#target1', null)) + ).to.include({x: 11, y: 12}); + + // Closest selectors always need a context node. + allowConsoleError(() => { + expect(() => css.getElementRect('#target1', 'closest')).to.throw( + /target is specified/ + ); + }); + expect( + css.withTarget(child, 0, () => css.getElementRect('.parent', 'closest')) + ).to.include({x: 11, y: 12}); }); it('should resolve a valid URL', () => { @@ -1805,17 +1847,236 @@ describes.realWin('MeasureScanner (scoped)', {amp: 1}, (env) => { const css = builder.css_; expect(() => - css.withTarget(target1, 0, () => css.getElementSize('.parent', 'closest')) + css.withTarget(target1, 0, () => css.getElementRect('.parent', 'closest')) ).to.not.throw(); allowConsoleError(() => { expect(() => css.withTarget(target2, 0, () => - css.getElementSize('.parent', 'closest') + css.getElementRect('.parent', 'closest') ) ).to.throw(/Element not found/); }); }); + + it("should not resolve viewport size as scope element's size", () => { + const scope = html`
`; + + scope.style.width = '200px'; + scope.style.height = '300px'; + + env.win.document.body.appendChild(scope); + + const builder = new Builder( + env.win, + env.win.document, + 'https://acme.org/', + /* vsync */ null, + /* owners */ null, + {scope, scaleByScope: false} + ); + + const css = builder.css_; + + const size = css.getViewportSize(); + expect(size.width).to.equal(env.win.innerWidth); + expect(size.height).to.equal(env.win.innerHeight); + + // cached: + expect(css.getViewportSize()).to.equal(size); + }); + + it("should resolve viewport size as scope element's size", () => { + const scope = html`
`; + + scope.style.width = '200px'; + scope.style.height = '300px'; + + env.win.document.body.appendChild(scope); + + const builder = new Builder( + env.win, + env.win.document, + 'https://acme.org/', + /* vsync */ null, + /* owners */ null, + {scope, scaleByScope: true} + ); + + const css = builder.css_; + + const size = css.getViewportSize(); + expect(size.width).to.equal(200); + expect(size.height).to.equal(300); + + // cached: + expect(css.getViewportSize()).to.equal(size); + }); + + it('should resolve x and y relative to scope', () => { + const scope = html`
+
+
+
`; + + const {target1, target2} = htmlRefs(scope); + + scope.style.position = 'absolute'; + scope.style.left = '10px'; + scope.style.top = '20px'; + + scope.style.width = '1000px'; + scope.style.height = '1000px'; + + target1.style.position = 'absolute'; + target1.style.left = '40px'; + target1.style.top = '30px'; + + target2.style.position = 'absolute'; + target2.style.left = '30px'; + target2.style.top = '20px'; + + env.win.document.body.appendChild(scope); + + const builder = new Builder( + env.win, + env.win.document, + 'https://acme.org/', + /* vsync */ null, + /* owners */ null, + {scope, scaleByScope: true} + ); + + const css = builder.css_; + + const pos1 = css.getElementRect('#target1', null); + expect(pos1.x).to.equal(40); + expect(pos1.y).to.equal(30); + + const pos2 = css.getElementRect('#target2', null); + expect(pos2.x).to.equal(30); + expect(pos2.y).to.equal(20); + }); + + it('should resolve dimensions and size rescaled relative to scope', () => { + const scope = html`
+
+
+
`; + + const {target1, target2} = htmlRefs(scope); + + scope.style.position = 'absolute'; + scope.style.top = '20px'; + scope.style.left = '10px'; + + scope.style.width = '1000px'; + scope.style.height = '1000px'; + + scope.style.transform = 'scale(0.5)'; + + target1.style.position = 'absolute'; + target1.style.left = '40px'; + target1.style.top = '30px'; + target1.style.width = '200px'; + target1.style.height = '100px'; + + target2.style.position = 'absolute'; + target2.style.left = '30px'; + target2.style.top = '20px'; + target2.style.width = '300px'; + target2.style.height = '200px'; + + env.win.document.body.appendChild(scope); + + const builder = new Builder( + env.win, + env.win.document, + 'https://acme.org/', + /* vsync */ null, + /* owners */ null, + {scope, scaleByScope: true} + ); + + const css = builder.css_; + + const pos1 = css.getElementRect('#target1', null); + expect(pos1.x).to.equal(40); + expect(pos1.y).to.equal(30); + + const size1 = css.getElementRect('#target1', null); + expect(size1.width).to.equal(200); + expect(size1.height).to.equal(100); + + const pos2 = css.getElementRect('#target2', null); + expect(pos2.x).to.equal(30); + expect(pos2.y).to.equal(20); + + const size2 = css.getElementRect('#target2', null); + expect(size2.width).to.equal(300); + expect(size2.height).to.equal(200); + }); + + it('should resolve dimensions and size not rescaled relative to scope', () => { + const scope = html`
+
+
+
`; + + const {target1, target2} = htmlRefs(scope); + + scope.style.position = 'absolute'; + scope.style.top = '20px'; + scope.style.left = '10px'; + + scope.style.width = '1000px'; + scope.style.height = '1000px'; + + scope.style.transform = 'scale(0.5)'; + + target1.style.position = 'absolute'; + target1.style.left = '40px'; + target1.style.top = '30px'; + target1.style.width = '200px'; + target1.style.height = '100px'; + + target2.style.position = 'absolute'; + target2.style.left = '30px'; + target2.style.top = '20px'; + target2.style.width = '300px'; + target2.style.height = '200px'; + + env.win.document.body.appendChild(scope); + + const builder = new Builder( + env.win, + env.win.document, + 'https://acme.org/', + /* vsync */ null, + /* owners */ null, + {scope, scaleByScope: false} + ); + + const css = builder.css_; + + const pos1 = css.getElementRect('#target1', null); + const rect1 = target1.getBoundingClientRect(); + expect(pos1.x).to.equal(rect1.x); + expect(pos1.y).to.equal(rect1.y); + + const size1 = css.getElementRect('#target1', null); + expect(size1.width).to.equal(200 * 0.5); + expect(size1.height).to.equal(100 * 0.5); + + const pos2 = css.getElementRect('#target2', null); + const rect2 = target2.getBoundingClientRect(); + expect(pos2.x).to.equal(rect2.x); + expect(pos2.y).to.equal(rect2.y); + + const size2 = css.getElementRect('#target2', null); + expect(size2.width).to.equal(300 * 0.5); + expect(size2.height).to.equal(200 * 0.5); + }); }); describes.sandboxed('NativeWebAnimationRunner', {}, (env) => { diff --git a/extensions/amp-animation/0.1/web-animation-types.js b/extensions/amp-animation/0.1/web-animation-types.js index a495f775a538..fb5fe80c1e53 100644 --- a/extensions/amp-animation/0.1/web-animation-types.js +++ b/extensions/amp-animation/0.1/web-animation-types.js @@ -152,7 +152,18 @@ export let WebAnimationSubtargetDef; /** * @typedef {{ * scope: (!Element|undefined), + * scaleByScope: (boolean|undefined), * }} + * + * - scope delimits selectors. + * - scaleByScope determines that CSS resolution should treat the scope + * element as a virtual viewport, so that: + * 1. vw/vh units are relative to the scope's size + * 2. element's x() and y() coords are relative to the scope's top-left corner + * 3. element's size and position (width()/height()/x()/y()) are inversely + * relative to the scope's transformed scale, e.g. if the scope is scaled + * to 90%, the element's dimensions will be returned as if it was unscaled + * to 100%. */ export let WebAnimationBuilderOptionsDef; diff --git a/extensions/amp-animation/0.1/web-animations.js b/extensions/amp-animation/0.1/web-animations.js index 685283c40f0d..c2c457460bd2 100644 --- a/extensions/amp-animation/0.1/web-animations.js +++ b/extensions/amp-animation/0.1/web-animations.js @@ -48,6 +48,7 @@ import {getMode} from '../../../src/mode'; import {isArray, isObject, toArray} from '../../../src/types'; import {isExperimentOn} from '../../../src/experiments'; import {isInFie} from '../../../src/iframe-helper'; +import {layoutRectLtwh} from '../../../src/layout-rect'; import {map} from '../../../src/utils/object'; import {parseCss} from './parsers/css-expr'; @@ -852,16 +853,20 @@ class CssContextImpl { * @param {!./web-animation-types.WebAnimationBuilderOptionsDef} options */ constructor(win, rootNode, baseUrl, options) { + const {scope = null, scaleByScope = false} = options; + /** @const @private */ this.win_ = win; /** @const @private {!Document|!ShadowRoot} */ this.rootNode_ = rootNode; - const {scope = null} = options; /** @const @private {?Element} */ this.scope_ = scope; + /** @const @private {boolean} */ + this.scaleByScope_ = scaleByScope; + /** @const @private */ this.baseUrl_ = baseUrl; @@ -889,8 +894,15 @@ class CssContextImpl { /** @private {?string} */ this.dim_ = null; - /** @private {?{width: number, height: number}} */ - this.viewportSize_ = null; + /** + * @private {?{ + * size: {width: number, height: number}, + * offset: {x: number, y: number}, + * scaleFactorX: number, + * scaleFactorY: number, + * }} + */ + this.viewportParams_ = null; } /** @return {!Document|!ShadowRoot} */ @@ -1179,13 +1191,32 @@ class CssContextImpl { /** @override */ getViewportSize() { - if (!this.viewportSize_) { - this.viewportSize_ = { - width: this.win_./*OK*/ innerWidth, - height: this.win_./*OK*/ innerHeight, - }; + return this.getViewportParams_().size; + } + + /** @private */ + getViewportParams_() { + if (!this.viewportParams_) { + if (this.scope_ && this.scaleByScope_) { + const rect = this.scope_./*OK*/ getBoundingClientRect(); + const {offsetWidth, offsetHeight} = this.scope_; + this.viewportParams_ = { + offset: {x: rect.x, y: rect.y}, + size: {width: offsetWidth, height: offsetHeight}, + scaleFactorX: offsetWidth / (rect.width || 1), + scaleFactorY: offsetHeight / (rect.height || 1), + }; + } else { + const {innerWidth, innerHeight} = this.win_; + this.viewportParams_ = { + offset: {x: 0, y: 0}, + size: {width: innerWidth, height: innerHeight}, + scaleFactorX: 1, + scaleFactorY: 1, + }; + } } - return this.viewportSize_; + return this.viewportParams_; } /** @override */ @@ -1220,13 +1251,13 @@ class CssContextImpl { } /** @override */ - getCurrentElementSize() { - return this.getElementSize_(this.requireTarget_()); + getCurrentElementRect() { + return this.getElementRect_(this.requireTarget_()); } /** @override */ - getElementSize(selector, selectionMethod) { - return this.getElementSize_(this.getElement_(selector, selectionMethod)); + getElementRect(selector, selectionMethod) { + return this.getElementRect_(this.getElement_(selector, selectionMethod)); } /** @@ -1265,12 +1296,20 @@ class CssContextImpl { /** * @param {!Element} target - * @return {!{width: number, height: number}} + * @return {!../../../src/layout-rect.LayoutRectDef} * @private */ - getElementSize_(target) { - const b = target./*OK*/ getBoundingClientRect(); - return {width: b.width, height: b.height}; + getElementRect_(target) { + const {offset, scaleFactorX, scaleFactorY} = this.getViewportParams_(); + const {x, y, width, height} = target./*OK*/ getBoundingClientRect(); + + // This assumes default `transform-origin: center center` + return layoutRectLtwh( + (x - offset.x) * scaleFactorX, + (y - offset.y) * scaleFactorY, + width * scaleFactorX, + height * scaleFactorY + ); } /** @override */ diff --git a/extensions/amp-animation/amp-animation.md b/extensions/amp-animation/amp-animation.md index 6434b9492542..7df038eab8d7 100644 --- a/extensions/amp-animation/amp-animation.md +++ b/extensions/amp-animation/amp-animation.md @@ -398,7 +398,7 @@ CSS `@keyframes` are mostly equivalent to inlining keyframes definition in the J - For broad-platform support, vendor prefixes, e.g. `@-ms-keyframes {}` or `-moz-transform` may be needed. Vendor prefixes are not needed and not allowed in the JSON format, but in CSS they could be necessary. - Platforms that do not support `calc()` and `var()` will not be able to take advantage of `amp-animation` polyfills when keyframes are specified in CSS. It's thus recommended to always include fallback values in CSS. -- CSS extensions such as [`width()`, `height()`, `num()`, `rand()`, `index()` and `length()`](#css-extensions) cannot be used in CSS. +- CSS extensions such as [`width()`, `height()`, `x()`, `y()`, `num()`, `rand()`, `index()` and `length()`](#css-extensions) cannot be used in CSS. #### White listed properties for keyframes @@ -555,7 +555,7 @@ Animation components can specify their own variables as `--var-name` fields. The ### CSS extensions -`amp-animation` provides several CSS extensions for typical animations needs: `rand()`, `num()`, `width()`, and `height()`. These functions can be used everywhere where CSS values can be used within `amp-animation`, including timing and keyframes values. +`amp-animation` provides several CSS extensions for typical animations needs: `rand()`, `num()`, `width()`, `height()`, `x()` and `y()`. These functions can be used everywhere where CSS values can be used within `amp-animation`, including timing and keyframes values. #### CSS `index()` extension @@ -601,17 +601,17 @@ The second form has two arguments and returns the random value between these two } ``` -#### CSS `width()` and `height()` extensions +#### CSS `width()`, `height()`, `x()` and `y()` extensions -The `width()` and `height()` extensions return the width/height of the animated element or the element specified by the selector. The returned value is in pixels, e.g. `100px`. +The `width()`/`height()` and `x()`/`y()` extensions return the size or coordinates of the animated element or the element specified by the selector. The returned value is in pixels, e.g. `100px`. The following forms are supported: -- `width()` and `height()` - width/height of the animated element. -- `width('.selector')` and `height('.selector')` - width/height of the element specified by the selector. Any CSS selector can be used. For instance, `width('#container > li')`. -- `width(closest('.selector'))` and `height(closest('.selector'))` - width/height of the element specified by the closest selector. +- `width()`, `height()`, `x()`, `y()` - width/height or coordinates of the animated element. +- With a selector, like: `width('.selector')` or `x('.selector')` - dimension or coordinate of the element specified by the selector. Any CSS selector can be used. For instance, `height('#container > li')`. +- With a closest selector, like: `height(closest('.selector'))` or `y(closest('.selector'))` - dimension or coordinate of the element specified by the closest selector. -The `width()` and `height()` are epsecially useful for transforms. The `left`, `top` and similar CSS properties that can use `%` values to express animations proportional to container size. However, `transform` property interpretes `%` values differently - as a percent of the selected element. Thus, the `width()` and `height()` can be used to express transform animations in terms of container elements and similar. +The `width()` and `height()` are especially useful for transforms. The `left`, `top` and similar CSS properties that can use `%` values to express animations proportional to container size. However, `transform` property interpretes `%` values differently - as a percent of the selected element. Thus, the `width()` and `height()` can be used to express transform animations in terms of container elements and similar. These functions can be combined with `calc()`, `var()` and other CSS expressions. For instance: diff --git a/extensions/amp-story/1.0/animation.js b/extensions/amp-story/1.0/animation.js index c06676a9cb1f..39e496709781 100644 --- a/extensions/amp-story/1.0/animation.js +++ b/extensions/amp-story/1.0/animation.js @@ -717,6 +717,7 @@ export class AnimationManager { .then((webAnimationService) => webAnimationService.createBuilder({ scope: this.page_, + scaleByScope: true, }) ); } diff --git a/extensions/amp-story/1.0/test/test-animation.js b/extensions/amp-story/1.0/test/test-animation.js index 466b37916cb6..8c5745c7ccc4 100644 --- a/extensions/amp-story/1.0/test/test-animation.js +++ b/extensions/amp-story/1.0/test/test-animation.js @@ -458,6 +458,7 @@ describes.realWin('amp-story animations', {}, (env) => { webAnimationService.createBuilder.withArgs( env.sandbox.match({ scope: page, + scaleByScope: true, }) ) ).to.have.been.calledOnce; From 10648a1472a10aa727bc6898a01c4543f02321c2 Mon Sep 17 00:00:00 2001 From: Shihua Zheng Date: Thu, 7 May 2020 13:44:45 -0700 Subject: [PATCH 18/62] Ramp up no center css experiment to 5% prod (#28249) --- build-system/global-configs/prod-config.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build-system/global-configs/prod-config.json b/build-system/global-configs/prod-config.json index c4d5bde7575d..084a5d4153b7 100644 --- a/build-system/global-configs/prod-config.json +++ b/build-system/global-configs/prod-config.json @@ -37,5 +37,6 @@ "random-subdomain-for-safeframe": 0.02, "swg-gpay-api": 1, "swg-gpay-native": 1, - "version-locking": 1 + "version-locking": 1, + "amp-ad-no-center-css": 0.05 } From 86cb885963d8ae9d6c0f6230fdcaad3f05e2767e Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 7 May 2020 23:17:58 +0200 Subject: [PATCH 19/62] =?UTF-8?q?=F0=9F=93=A6=20Update=20dependency=20karm?= =?UTF-8?q?a=20to=20v5.0.5=20(#28258)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- third_party/amp-toolbox-cache-url/package.json | 2 +- yarn.lock | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 4878d58d5775..a03f688a59e8 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "jsdom": "16.2.2", "json-stable-stringify": "1.0.1", "json5": "2.1.3", - "karma": "5.0.4", + "karma": "5.0.5", "karma-browserify": "7.0.0", "karma-chai": "0.1.0", "karma-chrome-launcher": "3.1.0", diff --git a/third_party/amp-toolbox-cache-url/package.json b/third_party/amp-toolbox-cache-url/package.json index bd7d9e61e9e3..ae46bcf04f05 100644 --- a/third_party/amp-toolbox-cache-url/package.json +++ b/third_party/amp-toolbox-cache-url/package.json @@ -36,7 +36,7 @@ "eslint": "6.8.0", "eslint-config-google": "0.14.0", "jasmine": "3.5.0", - "karma": "5.0.4", + "karma": "5.0.5", "karma-chrome-launcher": "3.1.0", "karma-jasmine": "3.1.1", "npm-run-all": "4.1.5", diff --git a/yarn.lock b/yarn.lock index 226c05b6daae..50c7e05eab3d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10274,10 +10274,10 @@ karma-super-dots-reporter@0.2.0: chalk "^0.5.1" log-symbols "^1.0.1" -karma@5.0.4: - version "5.0.4" - resolved "https://registry.yarnpkg.com/karma/-/karma-5.0.4.tgz#b374a1e541ad66da668460b99c6faf20cfffc97a" - integrity sha512-UGqTe2LBiGQBXRN+Fygeiq63tbfOX45639SKSbPkLpARwnxROWJZg+froGkpHxr84FXCe8UGCf+1PITM6frT5w== +karma@5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/karma/-/karma-5.0.5.tgz#f7716f459f354734a7856ef9ed67fe0a38270aa4" + integrity sha512-Q4Su7kNwkTgqS+KbSCYgH0p4a/0JIxVLyp7qKNV7vgPNhIF4kIoh0GlUfMKpw67BrR3hgPQSJoxgF7xnzUtPpg== dependencies: body-parser "^1.16.1" braces "^3.0.2" From 9d4b993ec61cf516b32db098924eb1cc157342b7 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 7 May 2020 23:19:33 +0200 Subject: [PATCH 20/62] =?UTF-8?q?=F0=9F=93=A6=20Update=20dependency=20esli?= =?UTF-8?q?nt-plugin-jsdoc=20to=20v24.0.6=20(#28230)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a03f688a59e8..69d653d26dd5 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ "eslint-plugin-eslint-plugin": "2.2.1", "eslint-plugin-google-camelcase": "0.0.2", "eslint-plugin-jasmine": "4.1.1", - "eslint-plugin-jsdoc": "24.0.2", + "eslint-plugin-jsdoc": "24.0.6", "eslint-plugin-local": "1.0.0", "eslint-plugin-notice": "0.9.10", "eslint-plugin-prettier": "3.1.3", diff --git a/yarn.lock b/yarn.lock index 50c7e05eab3d..6ed176792d8a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4435,10 +4435,10 @@ commander@~2.1.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" integrity sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E= -comment-parser@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.2.tgz#baf6d99b42038678b81096f15b630d18142f4b8a" - integrity sha512-4Rjb1FnxtOcv9qsfuaNuVsmmVn4ooVoBHzYfyKteiXwIU84PClyGA5jASoFMwPV93+FPh9spwueXauxFJZkGAg== +comment-parser@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.4.tgz#f5eb83cbae323cae6533c057f41d52692361c83a" + integrity sha512-Nnl77/mt6sj1BiYSVMeMWzvD0183F2MFOJyFRmZHimUVDYS9J40AvXpiFA7RpU5pQH+HkvYc0dnsHpwW2xmbyQ== common-path-prefix@^1.0.0: version "1.0.0" @@ -6112,12 +6112,12 @@ eslint-plugin-jasmine@4.1.1: resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-4.1.1.tgz#afdf8ca6a91e85041d9e0fb650af446874bf8852" integrity sha512-uS7kvt7RPUB/gLDwhJ/Ax0APrmkj7In8VJWkiZLYHafz2Ix74mMRJx82YZZ3zHRa/YOuTL+ig6dW/aKwdNzUuw== -eslint-plugin-jsdoc@24.0.2: - version "24.0.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-24.0.2.tgz#c679c7afac42cf872ffb1a6a246b5518b7b8eed4" - integrity sha512-tvk/jPpIP6MBVZETYnurKL/VGKzSinSbhpz1x+CzDOX20bmhRnCiexrgre4xF0WD/feg7ARKTVLCygVB3pfG5Q== +eslint-plugin-jsdoc@24.0.6: + version "24.0.6" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-24.0.6.tgz#360f75b7b79a64eb6f072de9f37322588578abf0" + integrity sha512-WDzUgShMK7Zg9N6s19LxUqy71At/PxCuMEXaKyBzybhABq6iU4DaZtWZ+4fkCMBvMzMwMAPa2oRD/+fQGORMhg== dependencies: - comment-parser "^0.7.2" + comment-parser "^0.7.4" debug "^4.1.1" jsdoctypeparser "^6.1.0" lodash "^4.17.15" From 3a07e0358be2fe248db9d2e6281e82553bb1dd29 Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Thu, 7 May 2020 23:20:18 +0200 Subject: [PATCH 21/62] =?UTF-8?q?=F0=9F=93=A6=20Update=20dependency=20amph?= =?UTF-8?q?tml-validator=20to=20v1.0.31=20(#28221)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 69d653d26dd5..91c25a1d83d6 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@jest/core": "26.0.1", "@types/minimist": "1.2.0", "acorn-globals": "6.0.0", - "amphtml-validator": "1.0.30", + "amphtml-validator": "1.0.31", "ansi-colors": "4.1.1", "ast-replace": "1.1.3", "atob": "2.1.2", diff --git a/yarn.lock b/yarn.lock index 6ed176792d8a..7ed07e27022d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2291,10 +2291,10 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= -amphtml-validator@1.0.30: - version "1.0.30" - resolved "https://registry.yarnpkg.com/amphtml-validator/-/amphtml-validator-1.0.30.tgz#b722ea5e965d0cc028cbdc360fc76b97e669715e" - integrity sha512-CaEm2ivIi4M4QTiFnCE9t4MRgawCf88iAV/+VsS0zEw6T4VBU6zoXcgn4L+dt6/WZ/NYxKpc38duSoRLqZJhNQ== +amphtml-validator@1.0.31: + version "1.0.31" + resolved "https://registry.yarnpkg.com/amphtml-validator/-/amphtml-validator-1.0.31.tgz#cf926ad4bbefd720b4cbeef6221f794f35849ac6" + integrity sha512-GEVZeUJqm2ssLLrERi3Zeyk6TEQlvMGRxKqkgQMt2IvyYiGEWLbwp5K6UfX6cD9aCc1xqbk9n+rDBi4HZv3uOQ== dependencies: colors "1.2.5" commander "2.15.1" From 0e3e8475b57cb5d9cf3decf7d09a1a927191e956 Mon Sep 17 00:00:00 2001 From: Greg Grothaus Date: Thu, 7 May 2020 14:55:28 -0700 Subject: [PATCH 22/62] Delete validator/AUTHORS. (#28265) --- validator/AUTHORS | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 validator/AUTHORS diff --git a/validator/AUTHORS b/validator/AUTHORS deleted file mode 100644 index ccc1dd378b9f..000000000000 --- a/validator/AUTHORS +++ /dev/null @@ -1,6 +0,0 @@ -# This is a list of contributors to AMP HTML. - -# Names should be added to this file like so: -# Name or Organization - -Google Inc. From c985e344b25effca5e8f2a604a3cef11eff6c0d3 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Thu, 7 May 2020 17:56:08 -0400 Subject: [PATCH 23/62] Add support for module/nomodule script pairs (#28250) * Add support for module/nomodule script pairs Fixes the only two (that I've found) issues with module/nomodule script pairs: 1. When extensions are reloaded, we didn't mark all current scripts as "replaced". 2. When Multidoc inserts scripts into the new ampdoc, it couldn't correctly parse `v0.mjs`. Everywhere else won't care about script pairs. The extension service collates all `custom-element` attributes into a set. Multidoc will try to insert the pairs twice (once for each script), but only the first will actually insert since it'll be registered. And reloading/inserting will always insert the correct `.js` or `.mjs` based on whether the runtime is currently using `getMode().esm`. * Add tests * Fix tests * Rename private method * Fix tests --- src/multidoc-manager.js | 12 +- src/service/extensions-impl.js | 27 ++- test/unit/test-extensions.js | 333 +++++++++++++++++++++++++-------- test/unit/test-runtime.js | 80 ++++++-- 4 files changed, 345 insertions(+), 107 deletions(-) diff --git a/src/multidoc-manager.js b/src/multidoc-manager.js index 59019d82e781..f5713675dd78 100644 --- a/src/multidoc-manager.js +++ b/src/multidoc-manager.js @@ -28,6 +28,7 @@ import {disposeServicesForDoc, getServicePromiseOrNullForDoc} from './service'; import {getMode} from './mode'; import {installStylesForDoc} from './style-installer'; import {isArray, isObject} from './types'; +import {parseExtensionUrl} from './service/extension-location'; import {parseUrlDeprecated} from './url'; import {setStyle} from './style'; @@ -417,15 +418,12 @@ export class MultidocManager { if (n.hasAttribute('src')) { dev().fine(TAG, '- src script: ', n); const src = n.getAttribute('src'); - const isRuntime = - src.indexOf('/amp.js') != -1 || src.indexOf('/v0.js') != -1; + const urlParts = parseExtensionUrl(src); + const isRuntime = !urlParts.extensionId; // Note: Some extensions don't have [custom-element] or // [custom-template] e.g. amp-viewer-integration. const customElement = n.getAttribute('custom-element'); const customTemplate = n.getAttribute('custom-template'); - const versionRe = /-(\d+.\d+)(.max)?\.js$/; - const match = versionRe.exec(src); - const version = match ? match[1] : '0.1'; if (isRuntime) { dev().fine(TAG, '- ignore runtime script: ', src); } else if (customElement || customTemplate) { @@ -433,14 +431,14 @@ export class MultidocManager { this.extensions_.installExtensionForDoc( ampdoc, customElement || customTemplate, - version + urlParts.extensionVersion ); dev().fine( TAG, '- load extension: ', customElement || customTemplate, ' ', - version + urlParts.extensionVersion ); if (customElement) { extensionIds.push(customElement); diff --git a/src/service/extensions-impl.js b/src/service/extensions-impl.js index 4b240d5771c6..12f1f26b9d49 100644 --- a/src/service/extensions-impl.js +++ b/src/service/extensions-impl.js @@ -243,11 +243,15 @@ export class Extensions { */ reloadExtension(extensionId) { // Ignore inserted script elements to prevent recursion. - const el = this.getExtensionScript_( + const els = this.getExtensionScripts_( extensionId, /* includeInserted */ false ); - devAssert(el, 'Cannot find script for extension: %s', extensionId); + devAssert( + els.length > 0, + 'Cannot find script for extension: %s', + extensionId + ); // The previously awaited extension loader must not have finished or // failed. const holder = this.extensions_[extensionId]; @@ -255,8 +259,10 @@ export class Extensions { devAssert(!holder.loaded && !holder.error); holder.scriptPresent = false; } - el.setAttribute('i-amphtml-loaded-new-version', extensionId); - const urlParts = parseExtensionUrl(el.src); + els.forEach((el) => + el.setAttribute('i-amphtml-loaded-new-version', extensionId) + ); + const urlParts = parseExtensionUrl(els[0].src); return this.preloadExtension(extensionId, urlParts.extensionVersion); } @@ -266,10 +272,10 @@ export class Extensions { * @param {string} extensionId * @param {boolean=} includeInserted If true, includes script elements that * are inserted by the runtime dynamically. Default is true. - * @return {?Element} + * @return {!Array} * @private */ - getExtensionScript_(extensionId, includeInserted = true) { + getExtensionScripts_(extensionId, includeInserted = true) { // Always ignore '); + writer.write( + '' + ); writer.write(''); return ampdoc.waitForBodyOpen().then(() => { @@ -1627,11 +1682,6 @@ describes.realWin( }); it('should ignore unknown script', () => { - expectAsyncConsoleError( - '[multidoc-manager] - unknown script: [object HTMLScriptElement] ' + - 'https://cdn.ampproject.org/other.js' - ); - shadowDoc = win.AMP.attachShadowDocAsStream(hostElement, docUrl); writer = shadowDoc.writer; extensionsMock.expects('preloadExtension').never(); @@ -1665,7 +1715,9 @@ describes.realWin( }) ) .once(); - writer.write(''); + writer.write( + '' + ); writer.write(''); return ampdoc.waitForBodyOpen().then(() => { expect( @@ -1682,7 +1734,9 @@ describes.realWin( .withExactArgs('amp-ext1', '0.1') .returns(Promise.resolve({elements: {}})) .once(); - writer.write(''); + writer.write( + '' + ); writer.write(''); return ampdoc.waitForBodyOpen().then(() => { expect( From 9f79bc10831055b6b8b9321cf633822efd498941 Mon Sep 17 00:00:00 2001 From: Chris Antaki Date: Thu, 7 May 2020 22:49:18 -0700 Subject: [PATCH 24/62] Fixes a typo (#28263) --- extensions/amp-subscriptions/amp-subscriptions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-subscriptions/amp-subscriptions.md b/extensions/amp-subscriptions/amp-subscriptions.md index 5d04a296c455..e9edf7d96995 100644 --- a/extensions/amp-subscriptions/amp-subscriptions.md +++ b/extensions/amp-subscriptions/amp-subscriptions.md @@ -234,7 +234,7 @@ The properties in the "local" service are (remote mode): - "pingbackUrl" - the pingback endpoint URL. - "actions" - a named map of action URLs. At a minimum there must be two actions specified: "login" and "subscribe". -In iframe mode the `authorzationUrl` and `pingbackUrl` are deleted +In iframe mode the `authorizationUrl` and `pingbackUrl` are deleted and replaced by: - "iframeSrc" - publisher supplied iframe From 85e84ee71c0b427b0d1203e52137f547ad36710d Mon Sep 17 00:00:00 2001 From: Jake Fried Date: Fri, 8 May 2020 14:04:49 -0400 Subject: [PATCH 25/62] e2e tests: support building specific extensions (#28277) --- build-system/common/utils.js | 10 +++++++++- build-system/tasks/e2e/index.js | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/build-system/common/utils.js b/build-system/common/utils.js index 3b1f80ed4ccb..1589a66564de 100644 --- a/build-system/common/utils.js +++ b/build-system/common/utils.js @@ -32,7 +32,15 @@ const ROOT_DIR = path.resolve(__dirname, '../../'); */ function buildMinifiedRuntime() { execOrDie('gulp clean'); - execOrDie(`gulp dist --fortesting --config ${argv.config}`); + + let command = `gulp dist --fortesting --config ${argv.config}`; + if (argv.core_runtime_only) { + command += ` --core_runtime_only`; + } else if (argv.extensions) { + command += ` --extensions=${argv.extensions}`; + } + + execOrDie(command); } /** diff --git a/build-system/tasks/e2e/index.js b/build-system/tasks/e2e/index.js index f525ee226ad9..5d9594581b11 100644 --- a/build-system/tasks/e2e/index.js +++ b/build-system/tasks/e2e/index.js @@ -150,7 +150,9 @@ e2e.flags = { '`chrome`, `firefox`, `safari`.', 'config': ' Sets the runtime\'s AMP_CONFIG to one of "prod" (default) or "canary"', + 'core_runtime_only': ' Builds only the core runtime.', 'nobuild': ' Skips building the runtime via `gulp dist --fortesting`', + 'extensions': ' Builds only the listed extensions.', 'files': ' Run tests found in a specific path (ex: **/test-e2e/*.js)', 'testnames': ' Lists the name of each test being run', 'watch': ' Watches for changes in files, runs corresponding test(s)', From c31c08cfea9a82b9abfb662cf8662e527cc3ba8e Mon Sep 17 00:00:00 2001 From: Khoi Doan Date: Fri, 8 May 2020 14:45:27 -0400 Subject: [PATCH 26/62] Launch inabox-viewport-friendly. (#27892) * Launch inabox-viewport-friendly. * Oops how did this get in here * Fix * Lint fix --- ads/inabox/inabox-host.md | 5 ----- build-system/global-configs/canary-config.json | 6 +----- build-system/global-configs/prod-config.json | 6 +----- build-system/server/routes/a4a-envelopes.js | 4 +--- src/inabox/inabox-iframe-messaging-client.js | 6 +----- src/inabox/inabox-viewport.js | 5 +---- test/integration/test-amphtml-ads.js | 2 -- test/unit/inabox/test-inabox-viewport.js | 3 --- tools/experiments/experiments-config.js | 8 -------- 9 files changed, 5 insertions(+), 40 deletions(-) diff --git a/ads/inabox/inabox-host.md b/ads/inabox/inabox-host.md index 0adfe8bdffe9..24f950c3e633 100644 --- a/ads/inabox/inabox-host.md +++ b/ads/inabox/inabox-host.md @@ -78,11 +78,6 @@ Main workflow: 1. The ads tag creates an iframe, and set the response content to the iframe using `srcdoc`. -Note that this friendly iframe approach right now is in an experimental -stage. Put the following meta tag in the head of the ad HTML to opt-in -the experiment: -``. - ###### Security assurance In case that the AMPHTML ad is generated by a 3rd party, to ensure it's diff --git a/build-system/global-configs/canary-config.json b/build-system/global-configs/canary-config.json index 202b4e78e626..b99c28447964 100644 --- a/build-system/global-configs/canary-config.json +++ b/build-system/global-configs/canary-config.json @@ -1,9 +1,5 @@ { - "allow-doc-opt-in": [ - "amp-next-page", - "inabox-viewport-friendly", - "analytics-chunks" - ], + "allow-doc-opt-in": ["amp-next-page", "analytics-chunks"], "allow-url-opt-in": ["pump-early-frame"], "canary": 1, "a4aProfilingRate": 0.01, diff --git a/build-system/global-configs/prod-config.json b/build-system/global-configs/prod-config.json index 084a5d4153b7..c8292cf8dc0d 100644 --- a/build-system/global-configs/prod-config.json +++ b/build-system/global-configs/prod-config.json @@ -1,9 +1,5 @@ { - "allow-doc-opt-in": [ - "amp-next-page", - "inabox-viewport-friendly", - "analytics-chunks" - ], + "allow-doc-opt-in": ["amp-next-page", "analytics-chunks"], "allow-url-opt-in": ["pump-early-frame"], "canary": 0, "a4aProfilingRate": 0.01, diff --git a/build-system/server/routes/a4a-envelopes.js b/build-system/server/routes/a4a-envelopes.js index 1247b611f63b..0912d7329845 100644 --- a/build-system/server/routes/a4a-envelopes.js +++ b/build-system/server/routes/a4a-envelopes.js @@ -48,14 +48,12 @@ app.use('/inabox-(friendly|safeframe)', (req, res) => { fs.promises .readFile(process.cwd() + templatePath, 'utf8') .then((template) => { - let url; + const url = getInaboxUrl(req); if (req.baseUrl == '/inabox-friendly') { - url = getInaboxUrl(req, 'inabox-viewport-friendly'); template = template .replace('SRCDOC_ATTRIBUTE', 'srcdoc="BODY"') .replace('INABOX_ADS_TAG_INTEGRATION', ''); } else { - url = getInaboxUrl(req); template = template .replace( /NAME/g, diff --git a/src/inabox/inabox-iframe-messaging-client.js b/src/inabox/inabox-iframe-messaging-client.js index 6b96e47ab50e..f444249f3055 100644 --- a/src/inabox/inabox-iframe-messaging-client.js +++ b/src/inabox/inabox-iframe-messaging-client.js @@ -17,7 +17,6 @@ import {IframeMessagingClient} from '../../3p/iframe-messaging-client'; import {canInspectWindow} from '../iframe-helper'; import {getExistingServiceOrNull, registerServiceBuilder} from '../service'; -import {isExperimentOn} from '../experiments'; import {tryParseJson} from '../json'; /** @@ -35,10 +34,7 @@ export function iframeMessagingClientFor(win) { * @param {!Window} win */ export function installIframeMessagingClient(win) { - if ( - !isExperimentOn(win, 'inabox-viewport-friendly') || - !canInspectWindow(win.top) - ) { + if (!canInspectWindow(win.top)) { registerServiceBuilder( win, 'iframeMessagingClient', diff --git a/src/inabox/inabox-viewport.js b/src/inabox/inabox-viewport.js index 26c7f39b8c31..7fda5e07094e 100644 --- a/src/inabox/inabox-viewport.js +++ b/src/inabox/inabox-viewport.js @@ -24,7 +24,6 @@ import {dev, devAssert} from '../log'; import {getFrameOverlayManager} from '../../ads/inabox/frame-overlay-manager.js'; import {getPositionObserver} from '../../ads/inabox/position-observer'; import {iframeMessagingClientFor} from './inabox-iframe-messaging-client'; -import {isExperimentOn} from '../experiments'; import {isIframed} from '../dom'; import { layoutRectFromDomRect, @@ -481,9 +480,7 @@ export class ViewportBindingInabox { ); /** @private @const {boolean} */ - this.isFriendlyIframed_ = - isExperimentOn(this.win, 'inabox-viewport-friendly') && - canInspectWindow(this.win.top); + this.isFriendlyIframed_ = canInspectWindow(this.win.top); /** @private {?../../ads/inabox/position-observer.PositionObserver} */ this.topWindowPositionObserver_ = this.isFriendlyIframed_ diff --git a/test/integration/test-amphtml-ads.js b/test/integration/test-amphtml-ads.js index 8ed0dbed8333..0fed096292b4 100644 --- a/test/integration/test-amphtml-ads.js +++ b/test/integration/test-amphtml-ads.js @@ -17,7 +17,6 @@ import {RequestBank} from '../../testing/test-helper'; import {maybeSwitchToCompiledJs} from '../../testing/iframe'; import {parseQueryString} from '../../src/url'; -import {toggleExperiment} from '../../src/experiments'; import {xhrServiceForTesting} from '../../src/service/xhr-impl'; describe('AMPHTML ad on AMP Page', () => { @@ -351,7 +350,6 @@ describe('A more real AMPHTML image ad', () => { }); it('should properly render ad in a friendly iframe with viewability pings', () => { - toggleExperiment(env.win, 'inabox-viewport-friendly', true); writeFriendlyFrame(doc, iframe, adBody); return testVisibilityPings(0, 1000); }); diff --git a/test/unit/inabox/test-inabox-viewport.js b/test/unit/inabox/test-inabox-viewport.js index 8ddddbee2c4e..b51df78489fe 100644 --- a/test/unit/inabox/test-inabox-viewport.js +++ b/test/unit/inabox/test-inabox-viewport.js @@ -27,7 +27,6 @@ import { import {installIframeMessagingClient} from '../../../src/inabox/inabox-iframe-messaging-client'; import {installPlatformService} from '../../../src/service/platform-impl'; import {layoutRectLtwh} from '../../../src/layout-rect'; -import {toggleExperiment} from '../../../src/experiments'; const NOOP = () => {}; @@ -92,12 +91,10 @@ describes.fakeWin('inabox-viewport', {amp: {}}, (env) => { }; win.frameElement = iframeElement; - toggleExperiment(win, 'inabox-viewport-friendly', false); installIframeMessagingClient(win); installPlatformService(win); binding = new ViewportBindingInabox(win); env.sandbox./*OK*/ stub(iframeHelper, 'canInspectWindow').returns(true); - toggleExperiment(win, 'inabox-viewport-friendly', true); bindingFriendly = new ViewportBindingInabox(win); measureSpy = env.sandbox.spy(); element = { diff --git a/tools/experiments/experiments-config.js b/tools/experiments/experiments-config.js index 7a3e7deffb4a..97351a0893f8 100644 --- a/tools/experiments/experiments-config.js +++ b/tools/experiments/experiments-config.js @@ -241,14 +241,6 @@ export const EXPERIMENTS = [ spec: 'https://github.com/ampproject/amphtml/issues/17475', cleanupIssue: 'https://github.com/ampproject/amphtml/issues/18897', }, - { - id: 'inabox-viewport-friendly', - name: - 'Inabox viewport measures the host window directly if ' + - 'within friendly iframe', - spec: 'https://github.com/ampproject/amphtml/issues/19869', - cleanupIssue: 'TODO', - }, { id: 'amp-user-location', name: From 9e048edd55858ec6d0ca177fba8086a996129ecd Mon Sep 17 00:00:00 2001 From: Caroline Liu <10456171+caroqliu@users.noreply.github.com> Date: Fri, 8 May 2020 15:00:04 -0400 Subject: [PATCH 27/62] Add timeout parameter to findElement (#28276) --- build-system/tasks/e2e/functional-test-controller.js | 3 ++- build-system/tasks/e2e/puppeteer-controller.js | 5 +++-- build-system/tasks/e2e/selenium-webdriver-controller.js | 5 +++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/build-system/tasks/e2e/functional-test-controller.js b/build-system/tasks/e2e/functional-test-controller.js index 6e19f8dd74f7..934e21081d4f 100644 --- a/build-system/tasks/e2e/functional-test-controller.js +++ b/build-system/tasks/e2e/functional-test-controller.js @@ -170,9 +170,10 @@ class FunctionalTestController { * {@link https://www.w3.org/TR/webdriver1/#find-element} * * @param {string} unusedSelector + * @param {number=} unusedTimeout * @return {!Promise} */ - async findElement(unusedSelector) {} + async findElement(unusedSelector, unusedTimeout) {} /** * The Find Elements command is used to find all elements matching the diff --git a/build-system/tasks/e2e/puppeteer-controller.js b/build-system/tasks/e2e/puppeteer-controller.js index a9f8269006fc..263ac2868e48 100644 --- a/build-system/tasks/e2e/puppeteer-controller.js +++ b/build-system/tasks/e2e/puppeteer-controller.js @@ -189,17 +189,18 @@ class PuppeteerController { /** * @param {string} selector + * @param {number=} timeout * @return {!Promise>} * @override */ - async findElement(selector) { + async findElement(selector, timeout = DEFAULT_WAIT_TIMEOUT) { const frame = await this.getCurrentFrame_(); const root = await this.getRoot_(); const jsHandle = await frame.waitForFunction( (root, selector) => { return root./*OK*/ querySelector(selector); }, - {timeout: DEFAULT_WAIT_TIMEOUT}, + {timeout}, root, selector ); diff --git a/build-system/tasks/e2e/selenium-webdriver-controller.js b/build-system/tasks/e2e/selenium-webdriver-controller.js index ea57e7343dbb..ae55755ded7a 100644 --- a/build-system/tasks/e2e/selenium-webdriver-controller.js +++ b/build-system/tasks/e2e/selenium-webdriver-controller.js @@ -117,10 +117,11 @@ class SeleniumWebDriverController { * until.js#elementLocated * {@link https://github.com/SeleniumHQ/selenium/blob/6a717f20/javascript/node/selenium-webdriver/lib/until.js#L237} * @param {string} selector + * @param {number=} timeout * @return {!Promise>} * @override */ - async findElement(selector) { + async findElement(selector, timeout = ELEMENT_WAIT_TIMEOUT) { const bySelector = By.css(selector); const label = 'for element to be located ' + selector; @@ -139,7 +140,7 @@ class SeleniumWebDriverController { throw e; } }); - const webElement = await this.driver.wait(condition, ELEMENT_WAIT_TIMEOUT); + const webElement = await this.driver.wait(condition, timeout); return new ElementHandle(webElement, this); } From 34c3f78535608d7cdd3061756088308561891764 Mon Sep 17 00:00:00 2001 From: Jon Newmuis Date: Fri, 8 May 2020 15:35:50 -0400 Subject: [PATCH 28/62] Check for CSS variable support (#28256) * Check for CSS variable support * Update extensions/amp-story/1.0/amp-story.js Co-authored-by: Gabriel Majoulet Co-authored-by: Gabriel Majoulet --- extensions/amp-story/1.0/amp-story.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/amp-story/1.0/amp-story.js b/extensions/amp-story/1.0/amp-story.js index c0ef90ea243b..7c0cd96921e5 100644 --- a/extensions/amp-story/1.0/amp-story.js +++ b/extensions/amp-story/1.0/amp-story.js @@ -2783,7 +2783,10 @@ export class AmpStory extends AMP.BaseElement { */ static isBrowserSupported(win) { return Boolean( - win.CSS && win.CSS.supports && win.CSS.supports('display', 'grid') + win.CSS && + win.CSS.supports && + win.CSS.supports('display', 'grid') && + win.CSS.supports('color', 'var(--test)') ); } } From c88e1bb04bd82023bf312451b837edcce9d6fbcb Mon Sep 17 00:00:00 2001 From: Caroline Liu <10456171+caroqliu@users.noreply.github.com> Date: Fri, 8 May 2020 17:07:44 -0400 Subject: [PATCH 29/62] amp-social-share should not layout without a share endpoint (#28285) * Add guard * Add comment for reasoning --- extensions/amp-social-share/0.1/amp-social-share.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/extensions/amp-social-share/0.1/amp-social-share.js b/extensions/amp-social-share/0.1/amp-social-share.js index 47b08039a6ad..4e698371725a 100644 --- a/extensions/amp-social-share/0.1/amp-social-share.js +++ b/extensions/amp-social-share/0.1/amp-social-share.js @@ -125,6 +125,12 @@ class AmpSocialShare extends AMP.BaseElement { /** @override */ layoutCallback() { + // Do not layout if the component returns before + // this.shareEndpoint_ is resolved from buildCallback. + if (!this.shareEndpoint_) { + return Promise.resolve(); + } + const hrefWithVars = addParamsToUrl( dev().assertString(this.shareEndpoint_), this.params_ From 9dc6a41f938cb9147d3a160a4e68fdb94ab4a46d Mon Sep 17 00:00:00 2001 From: Yuxuan Zhou Date: Fri, 8 May 2020 14:15:54 -0700 Subject: [PATCH 30/62] =?UTF-8?q?=E2=99=BB=EF=B8=8FChange=20ANALYTICS=5FCO?= =?UTF-8?q?NFIG=20to=20DEFAULT=5FCONFIG=20to=20better=20reflect=20its=20us?= =?UTF-8?q?age=20(#28261)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * better name * fix --- extensions/amp-ad-exit/0.1/amp-ad-exit.js | 2 +- extensions/amp-analytics/0.1/config.js | 50 ++++++++----------- .../0.1/{vendors.js => default-config.js} | 14 ++---- extensions/amp-analytics/0.1/transport.js | 2 +- 4 files changed, 26 insertions(+), 42 deletions(-) rename extensions/amp-analytics/0.1/{vendors.js => default-config.js} (92%) diff --git a/extensions/amp-ad-exit/0.1/amp-ad-exit.js b/extensions/amp-ad-exit/0.1/amp-ad-exit.js index 22685fe216c2..ac08d1d7087e 100644 --- a/extensions/amp-ad-exit/0.1/amp-ad-exit.js +++ b/extensions/amp-ad-exit/0.1/amp-ad-exit.js @@ -440,7 +440,7 @@ export class AmpAdExit extends AMP.BaseElement { /** * amp-analytics will create an iframe for vendors in - * extensions/amp-analytics/0.1/vendors.js who have transport/iframe defined. + * extensions/amp-analytics/0.1/vendors/* who have transport/iframe defined. * This is limited to MRC-accreddited vendors. The frame is removed in * amp-analytics, and the listener is destroyed here, if the user * navigates/swipes away from the page. Both are recreated if the user diff --git a/extensions/amp-analytics/0.1/config.js b/extensions/amp-analytics/0.1/config.js index d745f7eff9a7..c5093ea459f0 100644 --- a/extensions/amp-analytics/0.1/config.js +++ b/extensions/amp-analytics/0.1/config.js @@ -14,7 +14,7 @@ * limitations under the License. */ -import {ANALYTICS_CONFIG} from './vendors'; +import {DEFAULT_CONFIG} from './default-config'; import {Services} from '../../../src/services'; import {assertHttpsUrl} from '../../../src/url'; import {calculateScriptBaseUrl} from '../../../src/service/extension-location'; @@ -40,10 +40,13 @@ export class AnalyticsConfig { this.win_ = null; /** - * @const {!JsonObject} Copied here for tests. + * @const {!JsonObject} * @private */ - this.predefinedConfig_ = ANALYTICS_CONFIG; + this.defaultConfig_ = DEFAULT_CONFIG || dict(); + + /** @private {!JsonObject} */ + this.vendorConfig_ = dict(); /** * @private {JsonObject} @@ -115,7 +118,7 @@ export class AnalyticsConfig { .then((res) => res.json()) .then( (jsonValue) => { - this.predefinedConfig_[type] = jsonValue; + this.vendorConfig_ = jsonValue || dict(); dev().fine(TAG, 'Vendor config loaded for ' + type, jsonValue); }, (err) => { @@ -336,7 +339,7 @@ export class AnalyticsConfig { * Order of precedence for configs from highest to lowest: * - Remote config: specified through an attribute of the tag. * - Inline config: specified insize the tag. - * - Predefined config: Defined as part of the platform. + * - Predefined Vendor config: Defined as part of the platform. * - Default config: Built-in config shared by all amp-analytics tags. * * @private @@ -350,17 +353,16 @@ export class AnalyticsConfig { 'requestCount': 0, }, }); - const defaultConfig = this.predefinedConfig_['default'] || {}; - mergeObjects(expandConfigRequest(defaultConfig), config); + mergeObjects(expandConfigRequest(this.defaultConfig_), config); mergeObjects( - expandConfigRequest(this.getTypeConfig_()), + expandConfigRequest(this.vendorConfig_), config, - /* predefined */ true + /* predefined-vendor */ true ); mergeObjects( expandConfigRequest(rewrittenConfig), config, - /* predefined */ true + /* predefined-vendor */ true ); return config; } @@ -370,16 +372,7 @@ export class AnalyticsConfig { * @return {!JsonObject} */ getConfigRewriter_() { - return this.getTypeConfig_()['configRewriter'] || {}; - } - - /** - * Reads a vendor configuration. - * @return {!JsonObject} - */ - getTypeConfig_() { - const type = this.element_.getAttribute('type'); - return this.predefinedConfig_[type] || {}; + return this.vendorConfig_['configRewriter'] || {}; } /** @@ -412,8 +405,7 @@ export class AnalyticsConfig { * @param {!JsonObject} inlineConfig */ validateTransport_(inlineConfig) { - const type = this.element_.getAttribute('type'); - if (this.predefinedConfig_[type]) { + if (this.vendorConfig_) { // TODO(zhouyx, #7096) Track overwrite percentage. Prevent transport // overwriting if (inlineConfig['transport'] || this.remoteConfig_['transport']) { @@ -494,19 +486,19 @@ export class AnalyticsConfig { * * @param {Object|Array} from Object or array to merge from * @param {Object|Array} to Object or Array to merge into - * @param {boolean=} opt_predefinedConfig + * @param {boolean=} opt_predefinedVendorConfig * @return {*} TODO(#23582): Specify return type */ -export function mergeObjects(from, to, opt_predefinedConfig) { +export function mergeObjects(from, to, opt_predefinedVendorConfig) { if (to === null || to === undefined) { to = {}; } - // Assert that optouts are allowed only in predefined configs. + // Assert that optouts are allowed only in predefined vendor configs. // The last expression adds an exception of known, safe optout function // that is already being used in the wild. userAssert( - opt_predefinedConfig || + opt_predefinedVendorConfig || !from || !from['optout'] || from['optout'] == '_gaUserPrefs.ioo' || @@ -516,7 +508,7 @@ export function mergeObjects(from, to, opt_predefinedConfig) { for (const property in from) { userAssert( - opt_predefinedConfig || property != 'iframePing', + opt_predefinedVendorConfig || property != 'iframePing', 'iframePing config is only available to vendor config.' ); // Only deal with own properties. @@ -528,7 +520,7 @@ export function mergeObjects(from, to, opt_predefinedConfig) { to[property] = mergeObjects( from[property], to[property], - opt_predefinedConfig + opt_predefinedVendorConfig ); } else if (isObject(from[property])) { if (!isObject(to[property])) { @@ -537,7 +529,7 @@ export function mergeObjects(from, to, opt_predefinedConfig) { to[property] = mergeObjects( from[property], to[property], - opt_predefinedConfig + opt_predefinedVendorConfig ); } else { to[property] = from[property]; diff --git a/extensions/amp-analytics/0.1/vendors.js b/extensions/amp-analytics/0.1/default-config.js similarity index 92% rename from extensions/amp-analytics/0.1/vendors.js rename to extensions/amp-analytics/0.1/default-config.js index 9ac3b0917059..d490ddf80433 100644 --- a/extensions/amp-analytics/0.1/vendors.js +++ b/extensions/amp-analytics/0.1/default-config.js @@ -14,14 +14,10 @@ * limitations under the License. */ -import { - includeJsonLiteral, - jsonConfiguration, - jsonLiteral, -} from '../../../src/json'; +import {jsonConfiguration} from '../../../src/json'; // TODO(zhouyx@: Rename file and object name) -const DEFAULT_CONFIG = jsonLiteral({ +const defaultConfig = jsonConfiguration({ 'transport': {'beacon': true, 'xhrpost': true, 'image': true}, 'vars': { 'accessReaderId': 'ACCESS_READER_ID', @@ -91,11 +87,7 @@ const DEFAULT_CONFIG = jsonLiteral({ }, }); -const analyticsConfig = jsonConfiguration({ - 'default': includeJsonLiteral(DEFAULT_CONFIG), -}); - /** * @const {!JsonObject} */ -export const ANALYTICS_CONFIG = analyticsConfig; +export const DEFAULT_CONFIG = defaultConfig; diff --git a/extensions/amp-analytics/0.1/transport.js b/extensions/amp-analytics/0.1/transport.js index 45222630d708..2de4ee2db194 100644 --- a/extensions/amp-analytics/0.1/transport.js +++ b/extensions/amp-analytics/0.1/transport.js @@ -142,7 +142,7 @@ export class Transport { /** * amp-analytics will create an iframe for vendors in - * extensions/amp-analytics/0.1/vendors.js who have transport/iframe defined. + * extensions/amp-analytics/0.1/vendors/* who have transport/iframe defined. * This is limited to MRC-accreddited vendors. The frame is removed if the * user navigates/swipes away from the page, and is recreated if the user * navigates back to the page. From 87d71fc4ee87622ae4c0591bff6ba39f82f1f615 Mon Sep 17 00:00:00 2001 From: Michael Rybak Date: Fri, 8 May 2020 14:50:10 -0700 Subject: [PATCH 31/62] Add missing semicolon. (#28291) Co-authored-by: Greg Grothaus --- validator/nodejs/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/nodejs/index.js b/validator/nodejs/index.js index 15f42d24afa2..6797e018907c 100755 --- a/validator/nodejs/index.js +++ b/validator/nodejs/index.js @@ -497,4 +497,4 @@ function main() { }); } -exports.main = main +exports.main = main; From c8caf9d2eb8f00f6f4f35dad09a01f73cd1f2381 Mon Sep 17 00:00:00 2001 From: Yuxuan Zhou Date: Fri, 8 May 2020 18:05:55 -0700 Subject: [PATCH 32/62] =?UTF-8?q?=F0=9F=90=9B=20Register=20`LINKER=5FCREAT?= =?UTF-8?q?ED`=20on=20ampdoc=20instead=20of=20attribute=20(#28268)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * check document.body * generic object * use enum & rename * dep-check * move enum to its own file --- .../amp-analytics/0.1/linker-manager.js | 19 ++------------- extensions/amp-iframe/0.1/amp-iframe.js | 7 +++++- src/enums.js | 24 +++++++++++++++++++ src/service/ampdoc-impl.js | 19 ++++++++------- 4 files changed, 42 insertions(+), 27 deletions(-) create mode 100644 src/enums.js diff --git a/extensions/amp-analytics/0.1/linker-manager.js b/extensions/amp-analytics/0.1/linker-manager.js index 8224877fcd0f..e7f6579181ee 100644 --- a/extensions/amp-analytics/0.1/linker-manager.js +++ b/extensions/amp-analytics/0.1/linker-manager.js @@ -14,6 +14,7 @@ * limitations under the License. */ +import {AMPDOC_SINGLETON_NAME} from '../../../src/enums'; import {ExpansionOptions, variableServiceForDoc} from './variables'; import {Priority} from '../../../src/service/navigation'; import {Services} from '../../../src/services'; @@ -29,9 +30,6 @@ import {user} from '../../../src/log'; /** @const {string} */ const TAG = 'amp-analytics/linker-manager'; -/** @const {string} */ -const LINKER_CREATED = 'i-amphtml-linker-created'; - export class LinkerManager { /** * @param {!../../../src/service/ampdoc-impl.AmpDoc} ampdoc @@ -232,20 +230,7 @@ export class LinkerManager { return false; } - const headNode = this.ampdoc_.getHeadNode(); - const linkerCreatedEl = - headNode instanceof ShadowRoot - ? this.ampdoc_.getBody() - : headNode.querySelector( - 'meta[name="amp-google-client-id-api"][content="googleanalytics"]' - ); - - if (linkerCreatedEl.hasAttribute(LINKER_CREATED)) { - return false; - } - - linkerCreatedEl.setAttribute(LINKER_CREATED, ''); - return true; + return this.ampdoc_.registerSingleton(AMPDOC_SINGLETON_NAME.LINKER); } /** diff --git a/extensions/amp-iframe/0.1/amp-iframe.js b/extensions/amp-iframe/0.1/amp-iframe.js index 42ed6d805b7b..7747034d3342 100644 --- a/extensions/amp-iframe/0.1/amp-iframe.js +++ b/extensions/amp-iframe/0.1/amp-iframe.js @@ -14,6 +14,7 @@ * limitations under the License. */ +import {AMPDOC_SINGLETON_NAME} from '../../../src/enums'; import {ActionTrust} from '../../../src/action-constants'; import {IntersectionObserverHostApi} from '../../../src/utils/intersection-observer-polyfill'; import {LayoutPriority, isLayoutSizeDefined} from '../../../src/layout'; @@ -395,7 +396,11 @@ export class AmpIframe extends AMP.BaseElement { } if (this.isTrackingFrame_) { - if (!this.getAmpDoc().registerTrackingIframe()) { + if ( + !this.getAmpDoc().registerSingleton( + AMPDOC_SINGLETON_NAME.TRACKING_IFRAME + ) + ) { console /*OK*/ .error( 'Only 1 analytics/tracking iframe allowed per ' + diff --git a/src/enums.js b/src/enums.js new file mode 100644 index 000000000000..088974ec5a6c --- /dev/null +++ b/src/enums.js @@ -0,0 +1,24 @@ +/** + * Copyright 2020 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. + */ + +/** + * Registred singleton on AMP doc. + * @enum {number} + */ +export const AMPDOC_SINGLETON_NAME = { + TRACKING_IFRAME: 1, + LINKER: 2, +}; diff --git a/src/service/ampdoc-impl.js b/src/service/ampdoc-impl.js index f646da0afcb0..6ac07f97ad9f 100644 --- a/src/service/ampdoc-impl.js +++ b/src/service/ampdoc-impl.js @@ -308,8 +308,8 @@ export class AmpDoc { /** @public @const {!Window} */ this.win = win; - /** @private */ - this.hasTrackingIframe_ = false; + /** @private {!Object<../enums.AMPDOC_SINGLETON_NAME, boolean>} */ + this.registeredSingleton_ = map(); /** @public @const {?AmpDoc} */ this.parent_ = parent; @@ -754,16 +754,17 @@ export class AmpDoc { } /** - * Allow one tracking iframe for each amp doc. Caller need to handle user - * error when registeration returns false + * Attempt to register a singleton for each ampdoc. + * Caller need to handle user error when registration returns false. + * @param {!../enums.AMPDOC_SINGLETON_NAME} name * @return {boolean} */ - registerTrackingIframe() { - if (this.hasTrackingIframe_) { - return false; + registerSingleton(name) { + if (!this.registeredSingleton_[name]) { + this.registeredSingleton_[name] = true; + return true; } - this.hasTrackingIframe_ = true; - return true; + return false; } } From 5f88eb8c1c1f6872b459d0abbb6d4ca5dc3e638a Mon Sep 17 00:00:00 2001 From: Malte Ubl Date: Mon, 11 May 2020 06:35:40 -0700 Subject: [PATCH 33/62] Fix race condition that fails to hide video loading indicator (#28297) Auto-playing videos might have already started pllaying by the time we attach the playing event listener. Since there isn't a direct attribute to tell whether a video is playing, this checks whether the `currentTime` is 0. Fixes #28275 --- extensions/amp-story/1.0/amp-story-page.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/amp-story/1.0/amp-story-page.js b/extensions/amp-story/1.0/amp-story-page.js index a90d3182dc67..fe8c5f5929f0 100644 --- a/extensions/amp-story/1.0/amp-story-page.js +++ b/extensions/amp-story/1.0/amp-story-page.js @@ -1529,7 +1529,10 @@ export class AmpStoryPage extends AMP.BaseElement { const videoEls = this.getAllVideos_(); if (videoEls.length) { - this.debounceToggleLoadingSpinner_(true); + const alreadyPlaying = videoEls.some((video) => video.currentTime != 0); + if (!alreadyPlaying) { + this.debounceToggleLoadingSpinner_(true); + } } Array.prototype.forEach.call(videoEls, (videoEl) => { From ceef8eefddbf0fcb4b4acb9acd0da53bc9a3f95c Mon Sep 17 00:00:00 2001 From: William Chou Date: Mon, 11 May 2020 12:20:09 -0400 Subject: [PATCH 34/62] =?UTF-8?q?Revert=20"=F0=9F=9A=AE=20Remove=20idleRen?= =?UTF-8?q?derOutsideViewport=20(#28043)"=20(#28306)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ab03e7c87443b5c1f69d4de1e409202475f5b238. --- .../0.1/amp-ad-network-doubleclick-impl.js | 45 +++++++++ .../test-amp-ad-network-doubleclick-impl.js | 94 +++++++++++++++++++ src/base-element.js | 11 +++ src/custom-element.js | 10 ++ src/service/resource.js | 9 ++ src/service/resources-impl.js | 24 ++++- test/unit/test-resource.js | 53 +++++++++++ test/unit/test-resources.js | 53 +++++++++++ 8 files changed, 297 insertions(+), 2 deletions(-) diff --git a/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js b/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js index 26a18aeaf90a..8b7e57615710 100644 --- a/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js +++ b/extensions/amp-ad-network-doubleclick-impl/0.1/amp-ad-network-doubleclick-impl.js @@ -334,6 +334,51 @@ export class AmpAdNetworkDoubleclickImpl extends AmpA4A { this.inZIndexHoldBack_ = false; } + /** + * @return {number|boolean} render on idle configuration with false + * indicating disabled. + * @private + */ + getIdleRenderEnabled_() { + if (this.isIdleRender_) { + return this.isIdleRender_; + } + // Disable if publisher has indicated a non-default loading strategy. + if (this.element.getAttribute('data-loading-strategy')) { + return false; + } + const expVal = this.postAdResponseExperimentFeatures['render-idle-vp']; + const vpRange = parseInt(expVal, 10); + if (expVal && isNaN(vpRange)) { + // holdback branch sends non-numeric value. + return false; + } + return vpRange || 12; + } + + /** @override */ + idleRenderOutsideViewport() { + const vpRange = this.getIdleRenderEnabled_(); + if (vpRange === false) { + return vpRange; + } + const renderOutsideViewport = this.renderOutsideViewport(); + // False will occur when throttle in effect. + if (typeof renderOutsideViewport === 'boolean') { + return renderOutsideViewport; + } + this.isIdleRender_ = true; + // NOTE(keithwrightbos): handle race condition where previous + // idleRenderOutsideViewport marked slot as idle render despite never + // being schedule due to being beyond viewport max offset. If slot + // comes within standard outside viewport range, then ensure throttling + // will not be applied. + this.getResource() + .whenWithinViewport(renderOutsideViewport) + .then(() => (this.isIdleRender_ = false)); + return vpRange; + } + /** @override */ isLayoutSupported(layout) { this.isFluidPrimaryRequest_ = layout == Layout.FLUID; diff --git a/extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js b/extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js index 412201372ed1..cad58b3f61e3 100644 --- a/extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js +++ b/extensions/amp-ad-network-doubleclick-impl/0.1/test/test-amp-ad-network-doubleclick-impl.js @@ -1725,6 +1725,100 @@ describes.realWin( }); }); + describe('#idleRenderOutsideViewport', () => { + beforeEach(() => { + element = createElementWithAttributes(doc, 'amp-ad', { + 'width': '200', + 'height': '50', + 'type': 'doubleclick', + }); + impl = new AmpAdNetworkDoubleclickImpl(element); + env.sandbox + .stub(impl, 'getResource') + .returns({whenWithinViewport: () => Promise.resolve()}); + }); + + it('should use experiment value', () => { + impl.postAdResponseExperimentFeatures['render-idle-vp'] = '4'; + expect(impl.idleRenderOutsideViewport()).to.equal(4); + expect(impl.isIdleRender_).to.be.true; + }); + + it('should return false if using loading strategy', () => { + impl.postAdResponseExperimentFeatures['render-idle-vp'] = '4'; + impl.element.setAttribute( + 'data-loading-strategy', + 'prefer-viewability-over-views' + ); + expect(impl.idleRenderOutsideViewport()).to.be.false; + expect(impl.isIdleRender_).to.be.false; + }); + + it('should return false if invalid experiment value', () => { + impl.postAdResponseExperimentFeatures['render-idle-vp'] = 'abc'; + expect(impl.idleRenderOutsideViewport()).to.be.false; + }); + + it('should return 12 if no experiment header', () => { + expect(impl.idleRenderOutsideViewport()).to.equal(12); + }); + + it('should return renderOutsideViewport boolean', () => { + env.sandbox.stub(impl, 'renderOutsideViewport').returns(false); + expect(impl.idleRenderOutsideViewport()).to.be.false; + }); + }); + + describe('idle renderNonAmpCreative', () => { + beforeEach(() => { + element = createElementWithAttributes(doc, 'amp-ad', { + 'width': '200', + 'height': '50', + 'type': 'doubleclick', + }); + impl = new AmpAdNetworkDoubleclickImpl(element); + impl.postAdResponseExperimentFeatures['render-idle-vp'] = '4'; + impl.postAdResponseExperimentFeatures['render-idle-throttle'] = 'true'; + env.sandbox + .stub(AmpA4A.prototype, 'renderNonAmpCreative') + .returns(Promise.resolve()); + }); + + // TODO(jeffkaufman, #13422): this test was silently failing + it.skip('should throttle if idle render and non-AMP creative', () => { + impl.win['3pla'] = 1; + const startTime = Date.now(); + return impl.renderNonAmpCreative().then(() => { + expect(Date.now() - startTime).to.be.at.least(1000); + }); + }); + + it('should NOT throttle if idle experiment not enabled', () => { + impl.win['3pla'] = 1; + delete impl.postAdResponseExperimentFeatures['render-idle-vp']; + const startTime = Date.now(); + return impl.renderNonAmpCreative().then(() => { + expect(Date.now() - startTime).to.be.at.most(50); + }); + }); + + it('should NOT throttle if experiment throttle not enabled', () => { + impl.win['3pla'] = 1; + const startTime = Date.now(); + return impl.renderNonAmpCreative().then(() => { + expect(Date.now() - startTime).to.be.at.most(50); + }); + }); + + it('should NOT throttle if idle render and no previous', () => { + impl.win['3pla'] = 0; + const startTime = Date.now(); + return impl.renderNonAmpCreative().then(() => { + expect(Date.now() - startTime).to.be.at.most(50); + }); + }); + }); + describe('#preconnect', () => { beforeEach(() => { element = createElementWithAttributes(doc, 'amp-ad', { diff --git a/src/base-element.js b/src/base-element.js index b9515b0b0af8..a250f0086726 100644 --- a/src/base-element.js +++ b/src/base-element.js @@ -429,6 +429,17 @@ export class BaseElement { return getMode(this.win).runtime == 'inabox' || 3; } + /** + * Allows for rendering outside of the constraint set by renderOutsideViewport + * so long task scheduler is idle. Integer values less than those returned + * by renderOutsideViewport have no effect. Subclasses can override (default + * is disabled). + * @return {boolean|number} + */ + idleRenderOutsideViewport() { + return false; + } + /** * Subclasses can override this method to opt-in into receiving additional * {@link layoutCallback} calls. Note that this method is not consulted for diff --git a/src/custom-element.js b/src/custom-element.js index 095b8810ed94..c5a86d01a783 100644 --- a/src/custom-element.js +++ b/src/custom-element.js @@ -1033,6 +1033,16 @@ function createBaseCustomElementClass(win) { return this.implementation_.renderOutsideViewport(); } + /** + * Whether the element should render outside of renderOutsideViewport when + * the scheduler is idle. + * @return {boolean|number} + * @final + */ + idleRenderOutsideViewport() { + return this.implementation_.idleRenderOutsideViewport(); + } + /** * Returns a previously measured layout box adjusted to the viewport. This * mainly affects fixed-position elements that are adjusted to be always diff --git a/src/service/resource.js b/src/service/resource.js index 89254826c641..030268e1c3da 100644 --- a/src/service/resource.js +++ b/src/service/resource.js @@ -843,6 +843,15 @@ export class Resource { ); } + /** + * Whether this is allowed to render when scheduler is idle but not in + * viewport. + * @return {boolean} + */ + idleRenderOutsideViewport() { + return this.isWithinViewportRatio(this.element.idleRenderOutsideViewport()); + } + /** * Sets the resource's state to LAYOUT_SCHEDULED. * @param {number} scheduleTime The time at which layout was scheduled. diff --git a/src/service/resources-impl.js b/src/service/resources-impl.js index 869907535769..85ad154d5648 100644 --- a/src/service/resources-impl.js +++ b/src/service/resources-impl.js @@ -1349,8 +1349,27 @@ export class ResourcesImpl { this.queue_.getSize() == 0 && now > this.exec_.getLastDequeueTime() + 5000 ) { + // Phase 5: Idle Render Outside Viewport layout: layout up to 4 items + // with idleRenderOutsideViewport true let idleScheduledCount = 0; - // Phase 5: Idle layout: layout more if we are otherwise not doing much. + for ( + let i = 0; + i < this.resources_.length && idleScheduledCount < 4; + i++ + ) { + const r = this.resources_[i]; + if ( + r.getState() == ResourceState.READY_FOR_LAYOUT && + !r.hasOwner() && + r.isDisplayed() && + r.idleRenderOutsideViewport() + ) { + dev().fine(TAG_, 'idleRenderOutsideViewport layout:', r.debugid); + this.scheduleLayoutOrPreload(r, /* layout */ false); + idleScheduledCount++; + } + } + // Phase 6: Idle layout: layout more if we are otherwise not doing much. // TODO(dvoytenko): document/estimate IDLE timeouts and other constants for ( let i = 0; @@ -1628,7 +1647,8 @@ export class ResourcesImpl { if ( !forceOutsideViewport && !resource.isInViewport() && - !resource.renderOutsideViewport() + !resource.renderOutsideViewport() && + !resource.idleRenderOutsideViewport() ) { return false; } diff --git a/test/unit/test-resource.js b/test/unit/test-resource.js index 8f91cd297395..0a75d2738843 100644 --- a/test/unit/test-resource.js +++ b/test/unit/test-resource.js @@ -1010,6 +1010,59 @@ describes.realWin('Resource', {amp: true}, (env) => { }); }); +describe('Resource idleRenderOutsideViewport', () => { + let element; + let resources; + let resource; + let idleRenderOutsideViewport; + let isWithinViewportRatio; + + beforeEach(() => { + idleRenderOutsideViewport = window.sandbox.stub(); + element = { + idleRenderOutsideViewport, + ownerDocument: {defaultView: window}, + tagName: 'AMP-AD', + hasAttribute: () => false, + isBuilt: () => false, + isBuilding: () => false, + isUpgraded: () => false, + prerenderAllowed: () => false, + renderOutsideViewport: () => true, + build: () => false, + getBoundingClientRect: () => null, + updateLayoutBox: () => {}, + isRelayoutNeeded: () => false, + layoutCallback: () => {}, + applySize: () => {}, + unlayoutOnPause: () => false, + unlayoutCallback: () => true, + pauseCallback: () => false, + resumeCallback: () => false, + viewportCallback: () => {}, + getLayoutPriority: () => LayoutPriority.CONTENT, + }; + resources = new ResourcesImpl(new AmpDocSingle(window)); + resource = new Resource(1, element, resources); + isWithinViewportRatio = window.sandbox.stub( + resource, + 'isWithinViewportRatio' + ); + }); + + it('should return true if isWithinViewportRatio', () => { + idleRenderOutsideViewport.returns(5); + isWithinViewportRatio.withArgs(5).returns(true); + expect(resource.idleRenderOutsideViewport()).to.equal(true); + }); + + it('should return false for false element idleRenderOutsideViewport', () => { + idleRenderOutsideViewport.returns(false); + isWithinViewportRatio.withArgs(false).returns(false); + expect(resource.idleRenderOutsideViewport()).to.equal(false); + }); +}); + describes.realWin('Resource renderOutsideViewport', {amp: true}, (env) => { let element; let resources; diff --git a/test/unit/test-resources.js b/test/unit/test-resources.js index f340bb65b294..3e66c5b8b228 100644 --- a/test/unit/test-resources.js +++ b/test/unit/test-resources.js @@ -336,6 +336,7 @@ describe('Resources', () => { isInViewport: () => false, prerenderAllowed: () => true, renderOutsideViewport: () => false, + idleRenderOutsideViewport: () => false, startLayout: () => {}, applySizesAndMediaQuery: () => {}, }; @@ -355,6 +356,7 @@ describe('Resources', () => { isInViewport: () => false, prerenderAllowed: () => true, renderOutsideViewport: () => false, + idleRenderOutsideViewport: () => false, getLayoutPriority: () => LayoutPriority.METADATA, startLayout: () => {}, layoutScheduled: () => {}, @@ -383,6 +385,31 @@ describe('Resources', () => { isInViewport: () => false, prerenderAllowed: () => true, renderOutsideViewport: () => true, + idleRenderOutsideViewport: () => false, + getLayoutPriority: () => LayoutPriority.METADATA, + startLayout: () => {}, + layoutScheduled: () => {}, + getTaskId: () => 'resource#L', + applySizesAndMediaQuery: () => {}, + }; + resources.scheduleLayoutOrPreload(resource, true); + expect(resources.queue_.getSize()).to.equal(1); + expect(resources.queue_.tasks_[0].forceOutsideViewport).to.be.false; + } + ); + + it( + 'should schedule idleRenderOutsideViewport resource when' + + ' resource is not visible', + () => { + const resource = { + getState: () => ResourceState.READY_FOR_LAYOUT, + isDisplayed: () => true, + isFixed: () => false, + isInViewport: () => false, + prerenderAllowed: () => true, + renderOutsideViewport: () => false, + idleRenderOutsideViewport: () => true, getLayoutPriority: () => LayoutPriority.METADATA, startLayout: () => {}, layoutScheduled: () => {}, @@ -585,6 +612,8 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { element.getLayoutPriority = () => LayoutPriority.CONTENT; element.dispatchCustomEvent = () => {}; element.getLayout = () => 'fixed'; + + element.idleRenderOutsideViewport = () => true; element.isInViewport = () => false; element.getAttribute = () => null; element.hasAttribute = () => false; @@ -973,6 +1002,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { const layoutCanceledSpy = sandbox.spy(resource1, 'layoutCanceled'); sandbox.stub(resource1, 'isInViewport').returns(false); sandbox.stub(resource1, 'renderOutsideViewport').returns(false); + sandbox.stub(resource1, 'idleRenderOutsideViewport').returns(false); resources.work_(); expect(resources.exec_.getSize()).to.equal(0); expect(measureSpy).to.be.calledOnce; @@ -993,6 +1023,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { const measureSpy = sandbox.spy(resource1, 'measure'); sandbox.stub(resource1, 'isInViewport').returns(false); sandbox.stub(resource1, 'renderOutsideViewport').returns(false); + sandbox.stub(resource1, 'idleRenderOutsideViewport').returns(false); resources.work_(); expect(resources.exec_.getSize()).to.equal(1); expect(measureSpy).to.be.calledOnce; @@ -1083,6 +1114,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { .returns(true) .onSecondCall() .returns(false); + resource2.element.idleRenderOutsideViewport = () => false; resource1.state_ = ResourceState.NOT_BUILT; resource1.build = sandbox.spy(); @@ -1106,6 +1138,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { .returns(false) .onSecondCall() .returns(true); + resource2.element.idleRenderOutsideViewport = () => false; resource1.state_ = ResourceState.NOT_BUILT; resource1.build = sandbox.spy(); @@ -1126,6 +1159,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { resource1.prerenderAllowed = () => false; resource1.state_ = ResourceState.NOT_BUILT; resource1.build = sandbox.spy(); + resource2.element.idleRenderOutsideViewport = () => false; resources.discoverWork_(); @@ -1139,6 +1173,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { resources.buildAttemptsCount_ = 21; // quota is 20 resource1.element.isBuilt = () => false; + resource1.element.idleRenderOutsideViewport = () => true; resource1.prerenderAllowed = () => true; resource1.isBuildRenderBlocking = () => false; resource1.state_ = ResourceState.NOT_BUILT; @@ -1155,6 +1190,7 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { resources.buildAttemptsCount_ = 21; // quota is 20 resource1.element.isBuilt = () => false; + resource1.element.idleRenderOutsideViewport = () => true; resource1.prerenderAllowed = () => true; resource1.isBuildRenderBlocking = () => true; resource1.state_ = ResourceState.NOT_BUILT; @@ -1164,12 +1200,29 @@ describes.realWin('Resources discoverWork', {amp: true}, (env) => { expect(resource1.build).to.be.called; }); + it('should layout resource if outside viewport but idle', () => { + const schedulePassStub = sandbox.stub(resources, 'schedulePass'); + resources.documentReady_ = true; + sandbox.stub(resource1.element, 'nextSibling').returns({}); + resource1.element.isBuilt = () => true; + resource1.element.renderOutsideViewport = () => false; + resource1.element.idleRenderOutsideViewport = () => true; + resource2.element.renderOutsideViewport = () => false; + resource2.element.idleRenderOutsideViewport = () => false; + resource1.state_ = ResourceState.READY_FOR_LAYOUT; + + resources.discoverWork_(); + + expect(schedulePassStub).to.be.calledOnce; + }); + it('should force build resources during discoverWork layout phase', () => { const buildResourceSpy = sandbox.spy(resources, 'buildResourceUnsafe_'); sandbox.stub(resources, 'schedule_'); resources.documentReady_ = true; // Emulates a resource not building. resource1.element.isBuilt = sandbox.stub().returns(false); + resource2.element.idleRenderOutsideViewport = () => false; resource1.state_ = ResourceState.NOT_BUILT; resource1.build = sandbox.spy(); From 9aed75e5204dbe738a0850f7933a5ba7b18733e0 Mon Sep 17 00:00:00 2001 From: Usercentrics-AMP <63666142+Usercentrics-AMP@users.noreply.github.com> Date: Mon, 11 May 2020 19:12:53 +0200 Subject: [PATCH 35/62] =?UTF-8?q?=E2=9C=A8amp-consent:=20Add=20Usercentric?= =?UTF-8?q?s=20CMP=20(#28272)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ Add Usercentrics CMP to amp-consent * Fixed broken link in documentation md --- examples/amp-consent/cmp-vendors.amp.html | 18 +++++++ extensions/amp-consent/0.1/cmps.js | 6 +++ extensions/amp-consent/amp-consent.md | 1 + extensions/amp-consent/cmps/usercentrics.md | 52 +++++++++++++++++++++ 4 files changed, 77 insertions(+) create mode 100644 extensions/amp-consent/cmps/usercentrics.md diff --git a/examples/amp-consent/cmp-vendors.amp.html b/examples/amp-consent/cmp-vendors.amp.html index 5d950b4d5d29..e35ff2b2bcd8 100644 --- a/examples/amp-consent/cmp-vendors.amp.html +++ b/examples/amp-consent/cmp-vendors.amp.html @@ -101,6 +101,7 @@ + @@ -269,6 +270,23 @@

Image that is NOT blocked by consent

+ + + + + + + diff --git a/extensions/amp-consent/0.1/cmps.js b/extensions/amp-consent/0.1/cmps.js index 7036f24b3824..89524ad532ea 100644 --- a/extensions/amp-consent/0.1/cmps.js +++ b/extensions/amp-consent/0.1/cmps.js @@ -70,3 +70,9 @@ CMP_CONFIG['SourcePoint'] = { 'checkConsentHref': 'https://sourcepoint.mgr.consensu.org/consent/v2/amp', 'promptUISrc': 'https://amp.pm.sourcepoint.mgr.consensu.org/', }; + +CMP_CONFIG['Usercentrics'] = { + 'consentInstanceId': 'Usercentrics', + 'checkConsentHref': 'https://consents.usercentrics.eu/amp/checkConsent', + 'promptUISrc': 'https://amp.usercentrics.eu/amp.html', +}; diff --git a/extensions/amp-consent/amp-consent.md b/extensions/amp-consent/amp-consent.md index 22041e65dde6..f60b4113b526 100644 --- a/extensions/amp-consent/amp-consent.md +++ b/extensions/amp-consent/amp-consent.md @@ -637,5 +637,6 @@ Join in on the discussion where we are discussing [upcoming potential features]( - Marfeel : [Website](https://www.marfeel.com/) - [Documentation](./cmps/marfeel.md) - Ogury : [Website](https://www.ogury.com/) - [Documentation](./cmps/ogury.md) - SourcePoint : [Website](https://www.sourcepoint.com/) - [Documentation](./cmps/sourcepoint.md) +- Usercentrics : [Website](https://www.usercentrics.com/) - [Documentation](./cmps/usercentrics.md) - Your Integrated platform here! diff --git a/extensions/amp-consent/cmps/usercentrics.md b/extensions/amp-consent/cmps/usercentrics.md new file mode 100644 index 000000000000..7250b0922ad6 --- /dev/null +++ b/extensions/amp-consent/cmps/usercentrics.md @@ -0,0 +1,52 @@ + + +# Usercentrics + +Collect user consent with Usercentrics CMP. + +## Example + +```html + + +
+ Post Prompt UI + +
+
+``` + +## Configuration + +| Attribute | Type | Mandatory | Description | +| --------- | :----: | :-------: | ----------------------------------------- | +| id | String | yes | Settings id, provided via Admin Interface | + +In order to retrieve your settings id, please use the [Usercentrics Admin Interface](https://admin.usercentrics.com/). + +## Support + +Please get in contact with [Usercentrics](https://usercentrics.com/) if you need further support. From 5365f610383b3b91d487ffb8f82723e25889f6be Mon Sep 17 00:00:00 2001 From: Gabriel Chicoye Date: Mon, 11 May 2020 19:36:11 +0200 Subject: [PATCH 36/62] =?UTF-8?q?=E2=9C=A8=20Galaxie=20Media=20RTC=20adapt?= =?UTF-8?q?er=20added=20(#27008)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Galaxie Media RTC adapter The Galaxie Media alliance brings together the best French sites dedicated to high-tech and innovation topics. * Update callout-vendors.js * Update rtc-documentation.md * callout vendor - Space removal --- extensions/amp-a4a/0.1/callout-vendors.js | 6 ++++++ extensions/amp-a4a/rtc-documentation.md | 1 + 2 files changed, 7 insertions(+) diff --git a/extensions/amp-a4a/0.1/callout-vendors.js b/extensions/amp-a4a/0.1/callout-vendors.js index a9ae2d673d82..989acfd1ef93 100644 --- a/extensions/amp-a4a/0.1/callout-vendors.js +++ b/extensions/amp-a4a/0.1/callout-vendors.js @@ -114,6 +114,12 @@ const RTC_VENDORS = jsonConfiguration({ macros: ['PLACEMENT_ID', 'DIV_ID'], disableKeyAppend: true, }, + glxm: { + url: + 'https://pbserver.galaxiemedia.fr/openrtb2/amp?tag_id=REQUEST_ID&w=ATTR(width)&h=ATTR(height)&ow=ATTR(data-override-width)&oh=ATTR(data-override-height)&ms=ATTR(data-multi-size)&slot=ATTR(data-slot)&targeting=TGT&curl=CANONICAL_URL&timeout=TIMEOUT&adc=ADCID&purl=HREF&gdpr_consent=CONSENT_STRING&account=ACCOUNT_ID', + macros: ['REQUEST_ID', 'CONSENT_STRING', 'ACCOUNT_ID'], + disableKeyAppend: true, + }, aps: { url: 'https://aax.amazon-adsystem.com/e/dtb/bid?src=PUB_ID&pubid=PUB_UUID&=1&u=CANONICAL_URL&slots=%5B%7B%22sd%22%3A%22ATTR(data-slot)%22%2C%22s%22%3A%5B%22ATTR(width)xATTR(height)%22%5D%2C%22ms%22%3A%22ATTR(data-multi-size)%22%7D%5D&pj=PARAMS&gdprc=CONSENT_STRING', diff --git a/extensions/amp-a4a/rtc-documentation.md b/extensions/amp-a4a/rtc-documentation.md index e668d5e9b6ed..e82536ad4ef7 100644 --- a/extensions/amp-a4a/rtc-documentation.md +++ b/extensions/amp-a4a/rtc-documentation.md @@ -135,6 +135,7 @@ The `errorReportingUrl` property is optional. The only available macros are ERRO - The Ozone Project - PubMatic OpenWrap - Purch +- Galaxie Media - Rubicon - Salesforce - Yieldbot From a47826532d8d72001f3403a283f341b07004169a Mon Sep 17 00:00:00 2001 From: Micajuine Ho Date: Mon, 11 May 2020 11:06:25 -0700 Subject: [PATCH 37/62] =?UTF-8?q?=E2=99=BB=EF=B8=8FTest=20page=20folder=20?= =?UTF-8?q?refactor=20(#28084)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build-system/tasks/performance/config.json | 4 ++-- .../visible-basic.html | 0 .../visible-multiple.html | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename test/manual/amp-analytics/{performanceTestPages => performance-test-pages}/visible-basic.html (100%) rename test/manual/amp-analytics/{performanceTestPages => performance-test-pages}/visible-multiple.html (100%) diff --git a/build-system/tasks/performance/config.json b/build-system/tasks/performance/config.json index 9474ff1cdd26..791640714400 100644 --- a/build-system/tasks/performance/config.json +++ b/build-system/tasks/performance/config.json @@ -9,8 +9,8 @@ { "handlerName": "analyticsHandler", "urls": [ - "http://localhost:8000/test/manual/amp-analytics/performanceTestPages/visible-basic.html", - "http://localhost:8000/test/manual/amp-analytics/performanceTestPages/visible-multiple.html" + "http://localhost:8000/test/manual/amp-analytics/performance-test-pages/visible-basic.html", + "http://localhost:8000/test/manual/amp-analytics/performance-test-pages/visible-multiple.html" ], "timeout": 2000, "extraUrlParam": { diff --git a/test/manual/amp-analytics/performanceTestPages/visible-basic.html b/test/manual/amp-analytics/performance-test-pages/visible-basic.html similarity index 100% rename from test/manual/amp-analytics/performanceTestPages/visible-basic.html rename to test/manual/amp-analytics/performance-test-pages/visible-basic.html diff --git a/test/manual/amp-analytics/performanceTestPages/visible-multiple.html b/test/manual/amp-analytics/performance-test-pages/visible-multiple.html similarity index 100% rename from test/manual/amp-analytics/performanceTestPages/visible-multiple.html rename to test/manual/amp-analytics/performance-test-pages/visible-multiple.html From 04e633c12dc3a07ad99c93a5f1c62c30fd739e9d Mon Sep 17 00:00:00 2001 From: Micajuine Ho Date: Mon, 11 May 2020 11:40:09 -0700 Subject: [PATCH 38/62] amp-consent iframe ui 100% height (#28290) * init --- extensions/amp-consent/0.1/amp-consent.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/amp-consent/0.1/amp-consent.css b/extensions/amp-consent/0.1/amp-consent.css index 5581a95ea8a3..48de6a9868f7 100644 --- a/extensions/amp-consent/0.1/amp-consent.css +++ b/extensions/amp-consent/0.1/amp-consent.css @@ -108,7 +108,7 @@ iframe.i-amphtml-consent-ui-fill { amp-consent.i-amphtml-consent-ui-iframe-active { width: 100% !important; - height: 100vh !important; + height: 100% !important; padding: 0px !important; margin: 0px !important; From c7b1cc53f746f6733cd553037a2429c56d83e82a Mon Sep 17 00:00:00 2001 From: WhiteSource Renovate Date: Mon, 11 May 2020 20:44:27 +0200 Subject: [PATCH 39/62] =?UTF-8?q?=F0=9F=93=A6=20Update=20dependency=20anim?= =?UTF-8?q?ate.css=20to=20v4=20(#28267)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/amp-consent/diy-consent.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/amp-consent/diy-consent.html b/examples/amp-consent/diy-consent.html index 57d24d03bada..6dc8139580ad 100644 --- a/examples/amp-consent/diy-consent.html +++ b/examples/amp-consent/diy-consent.html @@ -3,7 +3,7 @@ - + @@ -217,8 +226,8 @@
@@ -236,7 +245,7 @@

Lorem Ipsum

-