diff --git a/3p/integration.js b/3p/integration.js index 8ca7ab19badf..37560ea25cc6 100644 --- a/3p/integration.js +++ b/3p/integration.js @@ -84,11 +84,10 @@ function masterSelection(type) { } /** - * Draws an optionally synchronously to the DOM. + * Draws an embed, optionally synchronously, to the DOM. */ window.draw3p = function() { - const fragment = location.hash; - const data = fragment ? JSON.parse(fragment.substr(1)) : {}; + const data = parseFragment(location.hash); window.context = data._context; window.context.location = parseUrl(data._context.location.href); validateParentOrigin(window, window.context.location); @@ -120,6 +119,9 @@ function triggerDimensions(width, height) { } function nonSensitiveDataPostMessage(type, opt_object) { + if (window.parent == window) { + return; // Nothing to do. + } const object = opt_object || {}; object.type = type; object.sentinel = 'amp-3p'; @@ -173,3 +175,20 @@ export function validateParentOrigin(window, parentLocation) { ancestors[0], parentLocation.origin); parentLocation.originValidated = true; } + +/** + * Expects the fragment to contain JSON. + * @param {string} fragment Value of location.fragment + * @return {!JSONObject} + * @visibleForTesting + */ +export function parseFragment(fragment) { + let json = fragment.substr(1); + // Some browser, notably Firefox produce an encoded version of the fragment + // while most don't. Since we know how the string should start, this is easy + // to detect. + if (json.indexOf('{%22') == 0) { + json = decodeURIComponent(json); + } + return json ? JSON.parse(json) : {}; +} diff --git a/test/functional/test-integration.js b/test/functional/test-integration.js index 24841744d10d..b2c34573d2f8 100644 --- a/test/functional/test-integration.js +++ b/test/functional/test-integration.js @@ -17,7 +17,7 @@ // Tests integration.js // Most coverage through test-3p-frame -import {validateParentOrigin} from '../../3p/integration'; +import {validateParentOrigin, parseFragment} from '../../3p/integration'; import {registrations} from '../../src/3p'; describe('3p integration.js', () => { @@ -73,4 +73,40 @@ describe('3p integration.js', () => { }, parent); }).to.throw(/Parent origin mismatch/); }); + + it('should parse JSON from fragment unencoded (most browsers)', () => { + const unencoded = '#{"tweetid":"638793490521001985","width":390,' + + '"height":50,"initialWindowWidth":1290,"initialWindowHeight":165,' + + '"type":"twitter","_context":{"referrer":"http://localhost:8000/' + + 'examples.build/","canonicalUrl":"http://localhost:8000/' + + 'examples.build/amps.html","location":{"href":"http://' + + 'localhost:8000/examples.build/twitter.amp.max.html"},' + + '"mode":{"localDev":true,"development":false,"minified":false}}}'; + const data = parseFragment(unencoded); + expect(data).to.be.object; + expect(data.tweetid).to.equal('638793490521001985'); + expect(data._context.location.href).to.equal( + 'http://localhost:8000/examples.build/twitter.amp.max.html'); + }); + + it('should parse JSON from fragment encoded (Firefox)', () => { + const encoded = '#{%22tweetid%22:%22638793490521001985%22,%22width' + + '%22:390,%22height%22:50,%22initialWindowWidth%22:1290,%22initial' + + 'WindowHeight%22:165,%22type%22:%22twitter%22,%22_context%22:{%22' + + 'referrer%22:%22http://localhost:8000/examples.build/%22,%22canoni' + + 'calUrl%22:%22http://localhost:8000/examples.build/amps.html%22,%22' + + 'location%22:{%22href%22:%22http://localhost:8000/examples.build/t' + + 'witter.amp.max.html%22},%22mode%22:{%22localDev%22:true,%22develop' + + 'ment%22:false,%22minified%22:false}}}'; + const data = parseFragment(encoded); + expect(data).to.be.object; + expect(data.tweetid).to.equal('638793490521001985'); + expect(data._context.location.href).to.equal( + 'http://localhost:8000/examples.build/twitter.amp.max.html'); + }); + + it('should be ok with empty fragment', () => { + expect(parseFragment('')).to.be.empty; + expect(parseFragment('#')).to.be.empty; + }); });