-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Whitelist actions on the special AMP target #13192
Conversation
Also cc'ing @raywainman @zhangsu |
src/service/standard-actions-impl.js
Outdated
|
||
// Cache the whitelist of allowed AMP actions (if provided). | ||
if (meta) { | ||
/** @const @private {!Array<string>} */ |
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.
Actually, given the current code this field should really be type-annotated as !Array<string>|undefined
given that it's conditionally initialized. It might be better to extract this logic to a helper method, and use null
as the default value.
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.
Done.
src/service/standard-actions-impl.js
Outdated
// in its content attribute, a whitelist of actions on the special AMP target. | ||
if (this.ampdoc.getRootNode() && this.ampdoc.getRootNode().head) { | ||
const meta = | ||
this.ampdoc.getRootNode().head |
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.
Hoist duplicate calls to a local var.
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.
Done.
src/service/standard-actions-impl.js
Outdated
@@ -68,6 +68,21 @@ export class StandardActions { | |||
/** @const @private {!./viewport/viewport-impl.Viewport} */ | |||
this.viewport_ = Services.viewportForDoc(ampdoc); | |||
|
|||
// A meta[name="amp-action-whitelist"] tag, if present, contains, | |||
// in its content attribute, a whitelist of actions on the special AMP target. | |||
if (this.ampdoc.getRootNode() && this.ampdoc.getRootNode().head) { |
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.
Let's lazily create this whitelist instead.
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.
Done.
src/service/standard-actions-impl.js
Outdated
*/ | ||
handleAmpTarget(invocation, opt_actionIndex, opt_actionInfos) { | ||
const method = invocation.method; | ||
if (this.ampActionWhitelist_ && | ||
!this.ampActionWhitelist_.includes(method)) { |
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.
Let's include the target as well to disambiguate.
<meta name="amp-action-whitelist" content="AMP.setState, AMP.pushState">
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.
Done.
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.
Is this feature meant to be valid AMP? If so, we need a validator change too since <meta>
tags whose name starts with "amp" are whitelisted.
createElementWithAttributes(window.document, 'meta', { | ||
name: 'amp-action-whitelist', | ||
content: 'pushState,setState', | ||
})); |
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.
This will mutate the test window which is shared by other tests. Instead, use stubs or describes.realWin
to test in an iframe that will be teared down after the test.
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.
done.
return standardActions.handleAmpTarget(pushState, 0, []).then(result => { | ||
expect(result).to.equal('push-state-complete'); | ||
expect(pushStateWithExpression).to.be.calledOnce; | ||
expect(pushStateWithExpression).to.be.calledWith('{foo: 123}'); |
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.
Simply stubbing/spying handleAmpBindAction_
should suffice. Let's not dupe test logic from other functions unless it pertains to the unit under test.
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.
done.
src/service/standard-actions-impl.js
Outdated
*/ | ||
handleAmpTarget(invocation, opt_actionIndex, opt_actionInfos) { | ||
const method = invocation.method; | ||
if (this.ampActionWhitelist_ && | ||
!this.ampActionWhitelist_.includes(method)) { | ||
throw user().createError('AMP action', method, 'is not whitelisted'); |
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.
"AMP.print" is not whitelisted in <meta name="amp-action-whitelist" content="AMP.setState">. See (link) for more details."
(link) should point to a new section in amp-actions-and-events.md.
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.
Adding this meta tag makes the amp document invalid (as the insertion of meta tag is post validation). With this in mind, could you please clarify what should go in the documentation?
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.
No documentation update then. 😄 Just want to make sure the error message is developer friendly.
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.
@lannka This is the conversation I told you about
Sorry for the late review, this slipped under my radar. |
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.
Thanks a lot Will. Regarding your previous comment, this feature is not meant to be valid AMP. This embedding of meta tag containing the whitelist will happen post validation.
return standardActions.handleAmpTarget(pushState, 0, []).then(result => { | ||
expect(result).to.equal('push-state-complete'); | ||
expect(pushStateWithExpression).to.be.calledOnce; | ||
expect(pushStateWithExpression).to.be.calledWith('{foo: 123}'); |
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.
done.
src/service/standard-actions-impl.js
Outdated
*/ | ||
createWhitelist_() { | ||
const head = this.ampdoc.getRootNode().head; | ||
if (head) { |
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.
Consider exiting early here with the negative condition to make the precondition clear and the code below more width-efficient.
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.
Done.
src/service/standard-actions-impl.js
Outdated
const meta = | ||
head.querySelector('meta[name="amp-action-whitelist"]'); | ||
|
||
if (meta) { |
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.
ditto
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.
Done.
src/service/standard-actions-impl.js
Outdated
this.installActions_(this.actions_); | ||
} | ||
|
||
/** | ||
* @return {?Array<string>} the whitelist of allowed AMP actions |
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.
... on the special AMP
target.
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.
Done.
src/service/standard-actions-impl.js
Outdated
const method = invocation.method; | ||
if (this.ampActionWhitelist_ && | ||
!this.ampActionWhitelist_.includes('AMP.' + method)) { |
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.
`AMP.${method}`
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.
Done.
src/service/standard-actions-impl.js
Outdated
const method = invocation.method; | ||
if (this.ampActionWhitelist_ && | ||
!this.ampActionWhitelist_.includes('AMP.' + method)) { | ||
throw user().createError('AMP.${method}$ is not whitelisted.'); |
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.
Don't you need a template string (escaped with the backtick) for string interpolation?
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.
Done.
getAttribute: () => 'AMP.pushState,AMP.setState', | ||
}; | ||
sandbox.stub(window.document.head, | ||
'querySelector').callsFake(() => fakeMeta); |
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.
I think I'd much prefer using describes.realWin
to create a real <meta>
tag and use real querySelector
. The test would be much useful that way, since it actually tests the real thing that will be running under the code in prod.
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.
LGTM, just one last set of suggestions...
@@ -354,6 +354,57 @@ describes.sandboxed('StandardActions', {}, () => { | |||
}); | |||
}); | |||
|
|||
describes.realWin('#whitelist', { |
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.
I think #foo
is usually for describing a method in the class under test. In this case, whitelist
is not really a method, so we probably want to describe the behavior in English (i.e., drop the #
).
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.
Done.
src/service/standard-actions-impl.js
Outdated
* special AMP target. | ||
* @private | ||
*/ | ||
createWhitelist_() { |
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.
How about calling this method getAmpActionWhitelist
, and return this.ampActionWhitelist_
if it is already cached (and update the JSDoc accordingly). That way, callers don't need to worry about this.ampActionWhitelist_
and can just call this.getAmpActionWhitelist_()
everywhere.
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.
Done.
src/service/standard-actions-impl.js
Outdated
* the special AMP target, e.g., | ||
* <meta name="amp-action-whitelist" content="AMP.setState,AMP.pushState"> | ||
* @return {?Array<string>} the whitelist of actions on the | ||
* special AMP target. |
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.
Doesn't the text here fit on the previous line? If not, this line should be indented 4 space, starting from the column where the @
on the previous line is.
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.
Done.
src/service/standard-actions-impl.js
Outdated
const method = invocation.method; | ||
if (this.ampActionWhitelist_ && | ||
!this.ampActionWhitelist_.includes(`AMP.${method}`)) { | ||
throw user().createError(`AMP.${method}$ is not whitelisted.`); |
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.
What is the second $
doing?
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.
Done.
Thanks a lot Su. |
@choumx appreciate if you could take a look |
Try merging master to restart the presubmit. Also need to fix a minor import sorting issue. |
Thanks @choumx. Fixed the lint error. |
* respect the whitelist of actions specified in meta tags * fixed the failing tests * lint fix * Fixed some of the concerns in the review * Faking the window * ran lint --fix * simplifying the functional tests * more code review fixes * using real window in test * a few suggestions by Su * fixing import orders * fixed lint errors * removing const identifier
If a whitelist of actions on AMP target is specified via a meta tag for example as follows
<meta name="amp-action-whitelist" content="setState,pushState">
then the runtime should respect it. In our example the runtime must reject the print, gotBack and navigateTo actions.
In order to do this, we parse the meta tag in StandardActions's constructor and cache the result. In handleAmpTarget we ensure that the given action is whitelisted before allowing it to trigger.