-
Notifications
You must be signed in to change notification settings - Fork 384
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
1094: Transform CSS selectors according to sanitizer HTML element to AMP component conversions #1175
1094: Transform CSS selectors according to sanitizer HTML element to AMP component conversions #1175
Changes from 10 commits
e530045
f2395ee
958fd48
488ebd2
077a094
ed1cc03
6091533
7a218aa
fcc0b44
d815891
12c9ff8
91e3eef
04f6b85
19db2c1
1b8405a
592aca7
a564410
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,20 @@ class AMP_Img_Sanitizer extends AMP_Base_Sanitizer { | |
*/ | ||
private static $anim_extension = '.gif'; | ||
|
||
/** | ||
* Get mapping of HTML selectors to the AMP component selectors which they may be converted into. | ||
* | ||
* @return array Mapping. | ||
*/ | ||
public function get_selector_conversion_mapping() { | ||
return array( | ||
'img' => array( | ||
'amp-img', | ||
'amp-anim', | ||
), | ||
); | ||
} | ||
|
||
/** | ||
* Sanitize the <img> elements from the HTML contained in this instance's DOMDocument. | ||
* | ||
|
@@ -222,15 +236,12 @@ private function adjust_and_replace_node( $node ) { | |
$amp_data = $this->get_data_amp_attributes( $node ); | ||
$old_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $node ); | ||
$old_attributes = $this->filter_data_amp_attributes( $old_attributes, $amp_data ); | ||
$old_attributes = $this->maybe_add_lightbox_attributes( $old_attributes, $node ); | ||
|
||
$new_attributes = $this->filter_attributes( $old_attributes ); | ||
$layout = isset( $amp_data['layout'] ) ? $amp_data['layout'] : false; | ||
$new_attributes = $this->filter_attachment_layout_attributes( $node, $new_attributes, $layout ); | ||
|
||
if ( isset( $old_attributes['data-amp-lightbox'] ) ) { | ||
$this->maybe_add_amp_image_lightbox_node(); | ||
} | ||
|
||
$this->add_or_append_attribute( $new_attributes, 'class', 'amp-wp-enforced-sizes' ); | ||
if ( empty( $new_attributes['layout'] ) && ! empty( $new_attributes['height'] ) && ! empty( $new_attributes['width'] ) ) { | ||
$new_attributes['layout'] = 'intrinsic'; | ||
|
@@ -248,6 +259,33 @@ private function adjust_and_replace_node( $node ) { | |
$node->parentNode->replaceChild( $new_node, $node ); | ||
} | ||
|
||
/** | ||
* Set lightbox attributes. | ||
* | ||
* @param array $attributes Array of attributes. | ||
* @param DomNode $node Array of AMP attributes. | ||
* @return array Updated attributes. | ||
*/ | ||
private function maybe_add_lightbox_attributes( $attributes, $node ) { | ||
$parent_node = $node->parentNode; | ||
if ( 'figure' !== $parent_node->tagName ) { | ||
return $attributes; | ||
} | ||
|
||
$parent_attributes = AMP_DOM_Utils::get_node_attributes_as_assoc_array( $parent_node ); | ||
|
||
if ( isset( $parent_attributes['data-amp-lightbox'] ) && true === filter_var( $parent_attributes['data-amp-lightbox'], FILTER_VALIDATE_BOOLEAN ) ) { | ||
$attributes['data-amp-lightbox'] = ''; | ||
$attributes['on'] = 'tap:' . self::AMP_IMAGE_LIGHTBOX_ID; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that |
||
$attributes['role'] = 'button'; | ||
$attributes['tabindex'] = 0; | ||
|
||
$this->maybe_add_amp_image_lightbox_node(); | ||
} | ||
|
||
return $attributes; | ||
} | ||
|
||
/** | ||
* Determines is a URL is considered a GIF URL | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,6 +182,13 @@ class AMP_Style_Sanitizer extends AMP_Base_Sanitizer { | |
*/ | ||
private $processed_imported_stylesheet_urls = array(); | ||
|
||
/** | ||
* Mapping of HTML element selectors to AMP selector elements. | ||
* | ||
* @var array | ||
*/ | ||
private $selector_mappings = array(); | ||
|
||
/** | ||
* Get error codes that can be raised during parsing of CSS. | ||
* | ||
|
@@ -308,6 +315,30 @@ private function get_used_tag_names() { | |
return $this->used_tag_names; | ||
} | ||
|
||
/** | ||
* Run logic before any sanitizers are run. | ||
* | ||
* After the sanitizers are instantiated but before calling sanitize on each of them, this | ||
* method is called with list of all the instantiated sanitizers. | ||
* | ||
* @param AMP_Base_Sanitizer[] $sanitizers Sanitizers. | ||
*/ | ||
public function init( $sanitizers ) { | ||
parent::init( $sanitizers ); | ||
|
||
foreach ( $sanitizers as $sanitizer ) { | ||
foreach ( $sanitizer->get_selector_conversion_mapping() as $html_selectors => $amp_selectors ) { | ||
if ( ! isset( $this->selector_mappings[ $html_selectors ] ) ) { | ||
$this->selector_mappings[ $html_selectors ] = $amp_selectors; | ||
} else { | ||
$this->selector_mappings[ $html_selectors ] = array_unique( | ||
array_merge( $this->selector_mappings[ $html_selectors ], $amp_selectors ) | ||
); | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Sanitize CSS styles within the HTML contained in this instance's DOMDocument. | ||
* | ||
|
@@ -647,7 +678,7 @@ private function fetch_external_stylesheet( $url ) { | |
private function process_stylesheet( $stylesheet, $options = array() ) { | ||
$parsed = null; | ||
$cache_key = null; | ||
$cache_group = 'amp-parsed-stylesheet-v6'; | ||
$cache_group = 'amp-parsed-stylesheet-v7'; | ||
|
||
$cache_impacting_options = array_merge( | ||
wp_array_slice_assoc( | ||
|
@@ -1236,6 +1267,10 @@ private function real_path_urls( $urls, $stylesheet_url ) { | |
private function process_css_declaration_block( RuleSet $ruleset, CSSList $css_list, $options ) { | ||
$results = array(); | ||
|
||
if ( $ruleset instanceof DeclarationBlock ) { | ||
$this->ampify_ruleset_selectors( $ruleset ); | ||
} | ||
|
||
// Remove disallowed properties. | ||
if ( ! empty( $options['property_whitelist'] ) ) { | ||
$properties = $ruleset->getRules(); | ||
|
@@ -1754,6 +1789,40 @@ private function finalize_styles() { | |
} | ||
} | ||
|
||
/** | ||
* Convert CSS selectors. | ||
* | ||
* @param DeclarationBlock $ruleset Ruleset. | ||
*/ | ||
private function ampify_ruleset_selectors( $ruleset ) { | ||
$selectors = array(); | ||
$replacements = 0; | ||
foreach ( $ruleset->getSelectors() as $old_selector ) { | ||
$edited_selectors = array( $old_selector->getSelector() ); | ||
foreach ( $this->selector_mappings as $html_selector => $amp_selectors ) { // Note: The $selector_mappings array contains ~6 items. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These notes are here because 4 level of loop nesting is normally something that is a big red flag for performance. |
||
$html_pattern = '/(?<=^|[^a-z0-9_-])' . preg_quote( $html_selector ) . '(?=$|[^a-z0-9_-])/i'; | ||
foreach ( $edited_selectors as &$edited_selector ) { // Note: The $edited_selectors array contains only item in the normal case. | ||
$original_selector = $edited_selector; | ||
$amp_selector = array_shift( $amp_selectors ); | ||
$edited_selector = preg_replace( $html_pattern, $amp_selector, $edited_selector, -1, $count ); | ||
if ( ! $count ) { | ||
continue; | ||
} | ||
$replacements += $count; | ||
while ( ! empty( $amp_selectors ) ) { // Note: This array contains only a couple items. | ||
$amp_selector = array_shift( $amp_selectors ); | ||
$edited_selectors[] = preg_replace( $html_pattern, $amp_selector, $original_selector, -1, $count ); | ||
} | ||
} | ||
} | ||
$selectors = array_merge( $selectors, $edited_selectors ); | ||
} | ||
|
||
if ( $replacements > 0 ) { | ||
$ruleset->setSelectors( $selectors ); | ||
} | ||
} | ||
|
||
/** | ||
* Finalize a stylesheet set (amp-custom or amp-keyframes). | ||
* | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this is also used by
AMP_Gallery_Block_Sanitizer
.