Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adapt heights, sizes and media attributes for SSR #4482

Merged
merged 41 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c026485
Update test suite and local metadata fallback
schlessera Mar 27, 2020
f248341
Add logic to SSR to convert sizes and heights attributes
schlessera Mar 30, 2020
f1f5fb3
Add basic media attribute handling
schlessera Mar 30, 2020
272862e
Exclude amp-audio from applying layout at the server
schlessera Mar 30, 2020
6c589a5
Add context info for media attribute
schlessera Mar 30, 2020
fa9d521
Skip spec tests for newly supported attributes
schlessera Mar 30, 2020
3b46da7
Fix PHPStan issue
schlessera Mar 30, 2020
8668a85
Merge branch 'develop' into add/4439-transform-media-sizes-heights
schlessera Mar 30, 2020
f1102c6
Adapt tests for getElementId()
schlessera Mar 31, 2020
e04f9cf
Add another getElementId() test
schlessera Mar 31, 2020
6efc85a
Adapt media query to use for replacing media attribute
schlessera Mar 31, 2020
330fd94
Fix negated media syntax
schlessera Mar 31, 2020
e24422e
Another stab at fixing media query generation
schlessera Mar 31, 2020
48bfebf
Merge branch 'develop' into add/4439-transform-media-sizes-heights
schlessera Apr 7, 2020
dd9e71b
Add size check for adding CSS
schlessera Apr 7, 2020
5b514b0
Merge branch 'develop' into add/4439-transform-media-sizes-heights
schlessera Apr 29, 2020
1891b37
Just strip sizes when srcset is missing
schlessera Apr 30, 2020
a666012
Use neutral formatting for CSS size number
schlessera May 5, 2020
f5f99b4
Merge branch 'develop' into add/4439-transform-media-sizes-heights
schlessera May 5, 2020
0b677f4
Don't remove sizes when disable-inline-width is set
schlessera May 5, 2020
b2172e6
Remove trailing commas in method calls
schlessera May 5, 2020
2d978c6
Skip currently broken tests in Optimizer
schlessera May 5, 2020
4b2781a
Merge branch 'develop' into add/4439-transform-media-sizes-heights
schlessera May 7, 2020
9731fe3
Remove test exception for #4654
schlessera May 7, 2020
58b550f
Omit unneeded semicolons
schlessera May 7, 2020
4842fc8
Remove left-over attribute removal
schlessera May 7, 2020
facc016
Align equal sign
schlessera May 7, 2020
bf6da0a
Adapt tests for removed semi-colons
schlessera May 7, 2020
11ff49c
Fix attribute name in error messages
schlessera May 7, 2020
c470761
Test precise error messages
schlessera May 7, 2020
0906a39
Provide more precise error for thrown exception
schlessera May 7, 2020
69f0b36
Fix error type for bad sizes test
schlessera May 7, 2020
2f423ca
Use :first-child to reference sizer in CSS
schlessera May 7, 2020
f877435
Update php-parallel-lint dependency to new name
schlessera May 7, 2020
f4d37fa
Use padding-top and override regular inline style for heights
schlessera May 7, 2020
a36dc0f
Pass DOMAttr instances rather than attribute name and value as separa…
westonruter May 7, 2020
84a4464
Ensure that source map comment is preserved at the end of amp-custom.css
westonruter May 7, 2020
8767882
Defer removal of attributes until after layout application
schlessera May 8, 2020
a7de3cd
Avoid method map and replace with a switch
schlessera May 8, 2020
3960f9c
Use explicit string type instead of generic array in return
westonruter May 8, 2020
5807091
Remove unused RuntimeException
westonruter May 8, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions includes/sanitizers/class-amp-core-theme-sanitizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -1638,7 +1638,7 @@ public function wrap_modal_in_lightbox( $args = [] ) {
return;
}

$body_id = AMP_DOM_Utils::get_element_id( $this->dom->body, 'body' );
$body_id = $this->dom->getElementId( $this->dom->body, 'body' );

$open_xpaths = isset( $args['open_button_xpath'] ) ? $args['open_button_xpath'] : [];
$close_xpaths = isset( $args['close_button_xpath'] ) ? $args['close_button_xpath'] : [];
Expand Down Expand Up @@ -1776,7 +1776,7 @@ public function add_twentytwenty_modals() {
}
}

$modal_id = AMP_DOM_Utils::get_element_id( $modal );
$modal_id = $this->dom->getElementId( $modal );

// Add the lightbox itself as a close button xpath as well.
// With twentytwenty compat, the lightbox fills the entire screen, and only an inner wrapper will contain
Expand Down Expand Up @@ -1808,7 +1808,7 @@ public function add_twentytwenty_modals() {
*/
public function add_twentytwenty_toggles() {
$toggles = $this->dom->xpath->query( '//*[ @data-toggle-target ]' );
$body_id = AMP_DOM_Utils::get_element_id( $this->dom->body, 'body' );
$body_id = $this->dom->getElementId( $this->dom->body, 'body' );

if ( false === $toggles || 0 === $toggles->length ) {
return;
Expand All @@ -1821,7 +1821,7 @@ public function add_twentytwenty_toggles() {
*/
foreach ( $toggles as $toggle ) {
$toggle_target = $toggle->getAttribute( 'data-toggle-target' );
$toggle_id = AMP_DOM_Utils::get_element_id( $toggle );
$toggle_id = $this->dom->getElementId( $toggle );

if ( 'next' === $toggle_target ) {
$target_node = $toggle->nextSibling;
Expand All @@ -1847,7 +1847,7 @@ public function add_twentytwenty_toggles() {

$is_sub_menu = AMP_DOM_Utils::has_class( $target_node, 'sub-menu' );
$new_target_node = $is_sub_menu ? $this->get_closest_submenu( $toggle ) : $target_node;
$new_target_id = AMP_DOM_Utils::get_element_id( $new_target_node );
$new_target_id = $this->dom->getElementId( $new_target_node );

$state_string = str_replace( '-', '_', $new_target_id );

Expand All @@ -1869,7 +1869,7 @@ public function add_twentytwenty_toggles() {
// Skip adding the 'active' class on the "Close" button in the primary nav menu.
continue;
}
$target_toggle_id = AMP_DOM_Utils::get_element_id( $target_toggle );
$target_toggle_id = $this->dom->getElementId( $target_toggle );
AMP_DOM_Utils::add_amp_action( $toggle, 'tap', "{$target_toggle_id}.toggleClass(class='active')" );
}
}
Expand All @@ -1888,7 +1888,7 @@ public function add_twentytwenty_toggles() {
$focus_element = $this->dom->xpath->query( $focus_xpath )->item( 0 );

if ( $focus_element instanceof DOMElement ) {
$focus_element_id = AMP_DOM_Utils::get_element_id( $focus_element );
$focus_element_id = $this->dom->getElementId( $focus_element );
AMP_DOM_Utils::add_amp_action( $toggle, 'tap', "{$focus_element_id}.focus" );
}
}
Expand Down
29 changes: 10 additions & 19 deletions includes/utils/class-amp-dom-utils.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,31 +365,22 @@ public static function has_class( DOMElement $element, $class ) {
* If the element does not have an ID, create one first.
*
* @since 1.4.0
* @since 1.5.1 Deprecated for AmpProject\Dom\Document::getElementId()
*
* @deprecated Use AmpProject\Dom\Document::getElementId() instead.
*
* @param DOMElement $element Element to get the ID for.
* @param string $prefix Optional. Defaults to '_amp_wp_id_'.
* @param string $prefix Optional. Defaults to 'amp-wp-id'.
schlessera marked this conversation as resolved.
Show resolved Hide resolved
* @return string ID to use.
*/
public static function get_element_id( $element, $prefix = 'amp-wp-id' ) {
static $index_counter = [];

if ( $element->hasAttribute( 'id' ) ) {
return $element->getAttribute( 'id' );
}

if ( ! array_key_exists( $prefix, $index_counter ) ) {
$index_counter[ $prefix ] = 2;
$element->setAttribute( 'id', $prefix );

return $prefix;
}

$id = "{$prefix}-{$index_counter[ $prefix ]}";
$index_counter[ $prefix ] ++;

$element->setAttribute( 'id', $id );
_deprecated_function(
'AMP_DOM_Utils::get_element_id',
'1.5.1',
'Use AmpProject\Amp\Dom\Document::getElementId() instead'
);

return $id;
return Document::fromNode( $element )->getElementId( $element, $prefix );
}

/**
Expand Down
4 changes: 2 additions & 2 deletions lib/common/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
"ext-dom": "*",
"ext-iconv": "*",
"ext-json": "*",
"ext-libxml": "*"
"ext-libxml": "*",
"php-parallel-lint/php-parallel-lint": "^1.2"
},
"require-dev": {
"civicrm/composer-downloads-plugin": "^2.1",
"jakub-onderka/php-parallel-lint": "^1.0",
"roave/security-advisories": "dev-master",
"squizlabs/php_codesniffer": "^3"
},
Expand Down
1 change: 1 addition & 0 deletions lib/common/src/Attribute.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ interface Attribute
const PROFILE = 'profile';
const REL = 'rel';
const ROLE = 'role';
const SRCSET = 'srcset';
const SIZES = 'sizes';
const STYLE = 'style';
const SRC = 'src';
Expand Down
79 changes: 79 additions & 0 deletions lib/common/src/Dom/CssByteCountCalculator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace AmpProject\Dom;

use DOMNodeList;

/**
* Class AmpProject\Dom\CssByteCountCalculator.
*
* Calculates the total byte count of CSS styles in a given document.
*
* This can be used to check against the allowed limit of 75kB that AMP enforces.
*
* @package ampproject/common
*/
final class CssByteCountCalculator
{

/**
* XPath query to fetch the <style amp-custom> tag, relative to the <head> node.
*
* @var string
*/
const AMP_CUSTOM_STYLE_TAG_XPATH = './/style[@amp-custom]';

/**
* XPath query to fetch the inline style attributes, relative to the <body> node.
*
* @var string
*/
const INLINE_STYLE_ATTRIBUTES_XPATH = './/@style';

/**
* Document to calculate the total byte count for.
*
* @var Document
*/
private $document;

/**
* Instantiate a CssByteCountCalculator object.
*
* @param Document $document Document to calculate the total byte count for.
*/
public function __construct(Document $document)
{
$this->document = $document;
}

/**
* Calculate the byte count for the provided document.
*
* @return int Total byte count of CSS styles in the document.
*/
public function calculate()
{
$ampCustomStyle = $this->document->xpath->query(self::AMP_CUSTOM_STYLE_TAG_XPATH, $this->document->head);
$inlineStyles = $this->document->xpath->query(self::INLINE_STYLE_ATTRIBUTES_XPATH, $this->document->body);

return $this->calculateForNodeList($ampCustomStyle) + $this->calculateForNodeList($inlineStyles);
}

/**
* Calculate the byte count of CSS styles for a given node list.
*
* @param DOMNodeList $nodeList Node list to calculate the byte count of CSS styles for.
* @return int Byte count of CSS styles for the given node list.
*/
private function calculateForNodeList(DOMNodeList $nodeList)
{
$byteCount = 0;

foreach ($nodeList as $node) {
$byteCount += strlen($node->textContent);
westonruter marked this conversation as resolved.
Show resolved Hide resolved
}

return $byteCount;
}
}
44 changes: 43 additions & 1 deletion lib/common/src/Dom/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,15 @@ final class Document extends DOMDocument
*/
private $usedAmpEmoji;

/**
* Store the current index by prefix.
*
* This is used to generate unique-per-prefix IDs.
*
* @var int[]
*/
private $indexCounter = [];

/**
* Creates a new AmpProject\Dom\Document object
*
Expand Down Expand Up @@ -314,7 +323,7 @@ public static function fromNode(DOMNode &$node)
// We replace the $node by reference, to make sure the next lines of code will
// work as expected with the new document.
// Otherwise $dom and $node would refer to two different DOMDocuments.
$node = $dom->importNode($root->documentElement ?: $root, true);
$node = $dom->importNode($node, true);
westonruter marked this conversation as resolved.
Show resolved Hide resolved
$dom->appendChild($node);

$dom->hasInitialAmpDevMode = $dom->documentElement->hasAttribute(DevMode::DEV_MODE_ATTRIBUTE);
Expand Down Expand Up @@ -1474,6 +1483,39 @@ public function isValidHeadNode(DOMNode $node)
);
}

/**
* Get the ID for an element.
*
* If the element does not have an ID, create one first.
*
* @param DOMElement $element Element to get the ID for.
* @param string $prefix Optional. The prefix to use (should not have a trailing dash). Defaults to 'i-amp-id'.
* @return string ID to use.
*/
public function getElementId(DOMElement $element, $prefix = 'i-amp-id')
{
if ($element->hasAttribute('id')) {
return $element->getAttribute('id');
}

if (array_key_exists($prefix, $this->indexCounter)) {
++$this->indexCounter[$prefix];
$id = "{$prefix}-{$this->indexCounter[ $prefix ]}";
} else {
$id = $prefix;
$this->indexCounter[$prefix] = 1;
}

while ($this->getElementById($id) instanceof DOMElement) {
++$this->indexCounter[$prefix];
$id = "{$prefix}-{$this->indexCounter[ $prefix ]}";
}
westonruter marked this conversation as resolved.
Show resolved Hide resolved

$element->setAttribute('id', $id);

return $id;
}

/**
* Determine whether `data-ampdevmode` was initially set on the document element.
*
Expand Down
58 changes: 58 additions & 0 deletions lib/common/tests/Dom/CssByteCountCalculatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace AmpProject\Common;

use AmpProject\Dom\CssByteCountCalculator;
use AmpProject\Dom\Document;
use PHPUnit\Framework\TestCase;

/**
* Tests for AmpProject\Dom\CssByteCountCalculator.
*
* @covers CssByteCountCalculator
* @package ampproject/common
*/
class CssByteCountCalculatorTest extends TestCase
{

/**
* Data provider for testing the calculate() method.
*
* @return array Testing data.
*/
public function dataCalculate()
{
return [
'amp_custom_style_tag' => [
'<html><head><style amp-custom>12345</style>', 5,
],
'one_inline_style_attribute' => [
'<html><body><div style="12345"></div></body></html>', 5,
],
'multiple_inline_style_attributes' => [
'<html><body><div style="1234"></div><div style="567"><div style="89"></body></html>', 9,
],
'amp_custom_style_tag_and_multiple_inline_style_attributes' => [
'<html><head><style amp-custom>12345</style></head><body><div style="1234"></div><div style="567"><div style="89"></body></html>', 14,
],
'amp_custom_style_tag_outside_head' => [
'<html><head><style amp-custom>12345</style></head><body><style amp-custom>123</style></body></html>', 5,
],
'multibyte_chars_are_counted_in_bytes_not_chars' => [
'<html><head><style amp-custom>Iñtërnâtiônàlizætiøn</style></head><body><div style="Iñtërnâtiônàlizætiøn"></div></body></html>', 54,
],
];
}

/**
* Test the calculate() method.
*
* @dataProvider dataCalculate
* @covers CssByteCountCalculator::calculate()
*/
public function testCalculate($html, $expected)
{
$document = Document::fromHtml($html);
$this->assertEquals($expected, (new CssByteCountCalculator($document))->calculate());
}
}
Loading