forked from ampproject/amphtml
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce iframe transport to amp-analytics - 3p side (ampproject#10596)
* Adds 3p iframe to amp analytics. Uses Subscription API. Event message queue much simpler than previous PR. Response functionality is included, but extraData is currently commented out - will add again before PR * Fix SubscriptionAPI * Brings in previous chanages to anchor-click-interceptor.js * Adds unit tests * Updates queue unit tests * Updates custom types and unit tests * Adds misc small fixes/nits from self-review * Changes 2 URLs in example from absolute to relative * Comments only * Fixes TODO re: unlayoutCallback destroying 3p iframe * Fixes an 80-char line length violation introduced in previous PR * Implements review feedback * Implements review feedback * Addresses review feedback * Reverts change to URL calculation, pending additional discussion * Fixes unit test * Fixes indentation of 2 lines * Removes extraData, new creative message * Fixes transport unit tests * Clarifies transport ID * Fixes whitespace * Improves example triggers. Fixes issue of delivery of creative response * Makes indentation consistent in this section * Addresses review feedback * Fixes a null check * Keys off of type rather than URL. Fixes issue of null origin, without relying on omitting sandbox allow-same-origin * Minor type annotation corrections * Makes use of new param to SubscriptionAPI c'tor * Removes allow-same-origin * Addresses review feedback, including removing response * Fixes unit test * Adds a forgotten semicolon :( * Addresses review feedback * Splits crossDomainIframe functionality out from transport.js into iframe-transport.js (and likewise for unit tests) * Removes a couple blank lines * Fixes merge conflict * Mostly changes comments, removes a small amount of redundant unit test code * Changes enum to const since all but one enum value no longer used. Removes changes to iframe-helper, makes xframe have allow-same-origin. Moves iframe-transport teardown from amp-analytics.unlayoutCallback to detachedCallback * Renames things using 3P in name, changes wire format from map to array * Adds 3p iframe to amp analytics. Uses Subscription API. Event message queue much simpler than previous PR. Response functionality is included, but extraData is currently commented out - will add again before PR * Amp 3p analytics using SubscriptionAPI, 3p side * Fixes ampanalytics-lib tests * Fixes linter issues * Implements review feedback * Removes extraData, new creative message * Removes extraData, new creative message * Stash * Changes creative ID to transport ID * Includes transport ID in example logging, response * Renamed files/classes to get rid of '3p' * Makes example ad click URL match parent branch * Addresses review feedback * Addresses further review feedback * extensions/amp-analytics/0.1/amp-analytics.js * Corrects rebase issue * Add XSS check to example HTML * Changes XSS message from log to warning * Addresses review nits * Addresses review feedback including removing response temporarily * Clean up unit tests, rename event datatype * Resolves rebase issue * Intentionally adding trivial change, will remove in a minute * Removing trival change added a minute ago. This is because Git UI is being strange. * Changes an enum to a const, since all but one enum value have been removed * Renames things using 3P in name, changes wire format from map to array * Fixes unit test * Renamed files/classes to get rid of '3p' * Fixes some merge issues * Found typo in method name, which led to renaming that and a few other things * Adds missing space in error msg * Addresses review feedback re: renaming files/class, and object field access. Have not yet addressed the comments about IframeMessagingClient nor simplifying to eliminate CreativeEventRouter. * Switch to IframeMessagingClient, add clarifying comment * Now uses IframeMessagingClient * Greatly simplified iframe-transport-client * Updates unit tests * Changes whitespace only * Fixes a broken unit test * Changes comment only * Makes lib architecture more similar to ampcontext * Adds 3p/iframe-transport-client-lib.js * Differentiates message name, fixes possible null exception * Trivial change to re-run Percy, which was flakey last time * Splits request/response message types better * Moves message type declarations into 3p-frame-messaging.js * Addresses a few nits
- Loading branch information
1 parent
6aa61ab
commit 53925ed
Showing
12 changed files
with
319 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* 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 './polyfills'; | ||
import {IframeTransportClient} from './iframe-transport-client.js'; | ||
import {initLogConstructor, setReportError} from '../src/log'; | ||
|
||
initLogConstructor(); | ||
// TODO(alanorozco): Refactor src/error.reportError so it does not contain big | ||
// transitive dependencies and can be included here. | ||
setReportError(() => {}); | ||
|
||
/** | ||
* If window.iframeTransportClient does not exist, we must instantiate and | ||
* assign it to window.iframeTransportClient, to provide the creative with | ||
* all the required functionality. | ||
*/ | ||
try { | ||
const iframeTransportClientCreated = | ||
new Event('amp-iframeTransportClientCreated'); | ||
window.iframeTransportClient = new IframeTransportClient(window); | ||
window.dispatchEvent(iframeTransportClientCreated); | ||
} catch (err) { | ||
// do nothing with error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/** | ||
* 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 {tryParseJson} from '../src/json'; | ||
import {dev, user} from '../src/log'; | ||
import {MessageType} from '../src/3p-frame-messaging'; | ||
import {IframeMessagingClient} from './iframe-messaging-client'; | ||
|
||
/** @private @const {string} */ | ||
const TAG_ = 'iframe-transport-client'; | ||
|
||
/** | ||
* Receives event messages bound for this cross-domain iframe, from all | ||
* creatives | ||
*/ | ||
export class IframeTransportClient { | ||
|
||
/** @param {!Window} win */ | ||
constructor(win) { | ||
/** @private {!Window} */ | ||
this.win_ = win; | ||
|
||
/** @private {?function(string,string)} */ | ||
this.listener_ = null; | ||
|
||
/** @protected {!IframeMessagingClient} */ | ||
this.client_ = new IframeMessagingClient(win); | ||
this.client_.setHostWindow(this.win_.parent); | ||
this.client_.setSentinel(user().assertString( | ||
tryParseJson(this.win_.name)['sentinel'], | ||
'Invalid/missing sentinel on iframe name attribute' + this.win_.name)); | ||
this.client_.makeRequest( | ||
MessageType.SEND_IFRAME_TRANSPORT_EVENTS, | ||
MessageType.IFRAME_TRANSPORT_EVENTS, | ||
eventData => { | ||
const events = | ||
/** | ||
* @type | ||
* {!Array<../src/3p-frame-messaging.IframeTransportEvent>} | ||
*/ | ||
(eventData['events']); | ||
user().assert(events, | ||
'Received malformed events list in ' + this.win_.location.href); | ||
dev().assert(events.length, | ||
'Received empty events list in ' + this.win_.location.href); | ||
user().assert(this.listener_, | ||
'Must call onAnalyticsEvent in ' + this.win_.location.href); | ||
events.forEach(event => { | ||
try { | ||
this.listener_ && | ||
this.listener_(event.message, event.transportId); | ||
} catch (e) { | ||
user().error(TAG_, | ||
'Exception in callback passed to onAnalyticsEvent: ' + | ||
e.message); | ||
} | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* Registers a callback function to be called when an AMP analytics event | ||
* is received. | ||
* Note that calling this a second time will result in the first listener | ||
* being removed - the events will not be sent to both callbacks. | ||
* @param {function(string,string)} callback | ||
*/ | ||
onAnalyticsEvent(callback) { | ||
this.listener_ = callback; | ||
} | ||
|
||
/** | ||
* Gets the IframeMessagingClient | ||
* @returns {!IframeMessagingClient} | ||
* @VisibleForTesting | ||
*/ | ||
getClient() { | ||
return this.client_; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8"> | ||
<title>Requests Frame</title> | ||
<script> | ||
window.addEventListener('amp-iframeTransportClientCreated', () => { | ||
/** | ||
* To receive AMP analytics events in a third-party frame, you must | ||
* pass a callback function to this method. The callback will be called | ||
* when an event is received, and will be passed two parameters: | ||
* @param {string} event A string of the format specified in the | ||
* requests block of the amp-analytics JSON config | ||
* @param {string} transportId An ID uniquely identifying which creative | ||
* generated the event | ||
*/ | ||
window.iframeTransportClient.onAnalyticsEvent( | ||
(event, transportId) => { | ||
// Now, do something meaningful with the AMP Analytics event | ||
console.log('The page at: ' + window.location.href + | ||
' has received an event: ' + event + | ||
' from the creative with transport ID: ' + transportId); | ||
}); | ||
}); | ||
|
||
// Load the script specified in the iframe’s name attribute: | ||
const url = JSON.parse(window.name).scriptSrc; | ||
if (url && url.startsWith('https://3p.ampproject.net/')) { | ||
script = document.createElement('script'); | ||
script.src = url; | ||
document.head.appendChild(script); | ||
// The script will be loaded, and will call onNewAmpAnalyticsInstance() | ||
} else { | ||
console.warn('Received invalid URL - risk of XSS! ' + url); | ||
} | ||
</script> | ||
</head> | ||
<!-- The frame will not be visible, so there is no need for a body tag. --> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
101 changes: 101 additions & 0 deletions
101
extensions/amp-analytics/0.1/test/test-iframe-transport-client.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* 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 {MessageType} from '../../../../src/3p-frame-messaging'; | ||
import { | ||
IframeTransportClient, | ||
} from '../../../../3p/iframe-transport-client'; | ||
import {dev, user} from '../../../../src/log'; | ||
import {adopt} from '../../../../src/runtime'; | ||
import * as sinon from 'sinon'; | ||
|
||
adopt(window); | ||
|
||
let nextId = 5000; | ||
function createUniqueId() { | ||
return String(++(nextId)); | ||
} | ||
|
||
describe('iframe-transport-client', () => { | ||
let sandbox; | ||
let badAssertsCounterStub; | ||
let iframeTransportClient; | ||
let sentinel; | ||
|
||
beforeEach(() => { | ||
sandbox = sinon.sandbox.create(); | ||
badAssertsCounterStub = sandbox.stub(); | ||
sentinel = createUniqueId(); | ||
window.name = '{"sentinel": "' + sentinel + '"}'; | ||
iframeTransportClient = new IframeTransportClient(window); | ||
sandbox.stub(dev(), 'assert', (condition, msg) => { | ||
if (!condition) { | ||
badAssertsCounterStub(msg); | ||
} | ||
}); | ||
sandbox.stub(user(), 'assert', (condition, msg) => { | ||
if (!condition) { | ||
badAssertsCounterStub(msg); | ||
} | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
sandbox.restore(); | ||
}); | ||
|
||
/** | ||
* Sends a message from the current window to itself | ||
* @param {string} type Type of the message. | ||
* @param {!JsonObject} object Message payload. | ||
*/ | ||
function send(type, data) { | ||
const object = {}; | ||
object['type'] = type; | ||
object['sentinel'] = sentinel; | ||
if (data['events']) { | ||
object['events'] = data['events']; | ||
} else { | ||
object['data'] = data; | ||
} | ||
const payload = 'amp-' + JSON.stringify(object); | ||
window./*OK*/postMessage(payload, '*'); | ||
} | ||
|
||
it('fails to create iframeTransportClient if no window.name ', () => { | ||
const oldWindowName = window.name; | ||
expect(() => { | ||
window.name = ''; | ||
new IframeTransportClient(window); | ||
}).to.throw(/Cannot read property 'sentinel' of undefined/); | ||
window.name = oldWindowName; | ||
}); | ||
|
||
it('sets sentinel from window.name.sentinel ', () => { | ||
expect(iframeTransportClient.getClient().sentinel_).to.equal(sentinel); | ||
}); | ||
|
||
it('receives an event message ', () => { | ||
window.processAmpAnalyticsEvent = (event, transportId) => { | ||
expect(transportId).to.equal('101'); | ||
expect(event).to.equal('hello, world!'); | ||
}; | ||
send(MessageType.IFRAME_TRANSPORT_EVENTS, /** @type {!JsonObject} */ ({ | ||
events: [ | ||
{transportId: '101', message: 'hello, world!'}, | ||
]})); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.