Skip to content

AMP signal collection frame#6830

Closed
keithwrightbos wants to merge 9 commits intoampproject:masterfrom
google:a4a_signal_collection_frame
Closed

AMP signal collection frame#6830
keithwrightbos wants to merge 9 commits intoampproject:masterfrom
google:a4a_signal_collection_frame

Conversation

@keithwrightbos
Copy link
Copy Markdown
Contributor

To be included in valid AMP A4A ad content allowing for click url augmentation based on custom javascript execution within a cross domain iframe. Note the following:

  • Creates new amp-signal-collection-frame extension with priority 1 which creates hidden xdomain iframe
  • URL to xdomain source is hardcoded within config to ensure only specific custom JS is allowed to be executed
  • amp-a4a url replacement has been augmented to allow for information postmessaged from the cross domain frames to be included (will need to be ported to amp-inabox functionality as part of A4A Cross Domained AMP Creatives Click URL Does Not Replace X/Y #6385
  • amp-signal-collection-frame element sends postmessage to hidden iframes with touch based events in 100 ms intervals

@dvoytenko
Copy link
Copy Markdown
Contributor

@keithwrightbos Top-level question: is this mostly the same code as we had https://github.com/ampproject/amphtml/blob/master/src/document-click.js ? I can't find where we removed it and why...

'CLICK_Y': () => {
return e.pageY;
},
'POSTMESSAGE': (origin, varName) => {
Copy link
Copy Markdown
Contributor

@lannka lannka Jan 4, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we going to make this genetic to any postmessage?

  • If we are, then there are other security issues to be considered.
  • If not, I suggest making it BG_SIGNAL(type), which is more specific and reduces chance of mis-use.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not really "generic" to any postmessage given the above code restricts populating the postmessage data by messages coming from an iframe within an amp-signal-collection-frame element. I could rename but was just trying to match the i2i #3665 . Thoughts?

*/
export const MESSAGE_INTERVAL_MS = 100;

export class AmpSignalCollectionFrame extends AMP.BaseElement {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have we considered having the logic as a part of the doubleclick A4A extension? an extra extension means extra latency.

of course, probably I should ask this first: is it possible for doubleclick A4A to load a creative with AmpSignalCollectionFrame of other type?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be shared across AdSense and Doubleclick and potentially other networks that could return a creative from either of those networks so having it in A4A abstract element makes the most sense IMO. Let's discuss offline.

Copy link
Copy Markdown
Contributor

@jonkeller jonkeller Jan 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC the resolution of this was to keep separate, but may be thinking of a distinct/similar issue. Confirm please?

}
// Only allow messages from child iframe whose parent is an
// amp-signal-collection-frame element.
const frames = iframeWin.document
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's cache the results for the sake of performance

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason not to cache is that attachment of the iframe within amp-signal-collection-frame is done within layoutCallback of that element. Given it will have a lower priority (2) it is likely it will be appended AFTER registerExpandUrlParams_ executes. I also want to allow for the possibility that multiple amp-signal-collection-frame elements exist within the AMP creative so its not sufficient to use the result of querySelectorAll after the first post message. I suppose I could verify the count of iframes = count of total amp signal collection frames and cache at that time... just not sure its worth it. Thoughts?

// Place listener on outer document and use capture to ensure data is
// always collected.
this.unlisteners_.push(
listen(this.element.ownerDocument.documentElement, name, e => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we allow multiple SignalCollectionFrames in a single creative? If so, should we reuse the listeners for the sake of performance?

}

sendPostMessage(win, message) {
win./*REVIEW*/postMessage(message, '*');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for safety, we can use explicit target origin here.

JSON.stringify({'collection-events': events}));
events = {};
}
}, MESSAGE_INTERVAL_MS);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you think of using rate-limit.js ?

@lannka lannka self-assigned this Jan 4, 2017
/** @type {Object<string, Object<string, string>>} */
const messages = {};
// Listen for messages to creative friendly frame.
this.unlisteners_.push(listen(iframeWin, 'message', evt => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we're eventually having a new extension, i'd prefer all the logic go there.

@keithwrightbos
Copy link
Copy Markdown
Contributor Author

@dvoytenko responding to Top-level question: is this mostly the same code as we had https://github.com/ampproject/amphtml/blob/master/src/document-click.js?

It was removed due to concerns that anchors not within AMP creatives could be modified. @cramforce isolated it to only the AMP creative iframe within amp-a4a.js and I have an outstanding issue #6385 assigned to @lannka to ensure it is ported to AMP-inabox runtime.

Copy link
Copy Markdown
Member

@cramforce cramforce left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the current design allow for a single signal collection iframe per page instead of one per ad?

};
// For now we only allow to replace the vars defined items and nothing else.
// NOTE: Addition to this whitelist requires additional review.
const whitelist = {};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

map({})

try {
if (frame.contentWindow == evt.source) {
const originUrl = parseUrl(evt.origin);
messages[originUrl.hostname.toUpperCase()] =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets use the .origin

/** @type {Object<string, Object<string, string>>} */
const messages = {};
// Listen for messages to creative friendly frame.
this.unlisteners_.push(listen(iframeWin, 'message', evt => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets factor this into a specific method.

Copy link
Copy Markdown

@tdrl tdrl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still reviewing, but have gotten preempted by other things today.

this.xOriginIframeHandler_ = null;
}
this.layoutMeasureExecuted_ = false;
this.unlisteners_.forEach(unlistener => unlistener);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if this is JS-naive, but... Not clear what this is doing. Shouldn't it be .foreach(unlistener => unlistener())?

@lannka
Copy link
Copy Markdown
Contributor

lannka commented May 11, 2017

design changed. closing this for now.

@lannka lannka closed this May 11, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants