Mensagens Iframe CORS com acesso DOM e retorno de chamada

Às vezes, você não pode usar iframes, mas ainda quer seu próprio design e fazer com que o iframe execute seus comandos, mas fique invisível. Fiz um pequeno utilitário que usa o postmessage para enviar e receber dados DOM analisados ​​por json.

Ainda não configurei tudo isso para ser 100% seguro, suas chamadas de origem são curingas, o que na produção não seria aconselhável. De qualquer forma, funciona e você pode enviar fluxo através do iframe e detectar erros verificando em qual página ele está ou o que o dom está fazendo. Não é bonito, mas os iframes não são bonitos.

Coloque-o no lado do iframe para permitir alguma manipulação de dom por meio de pacotes postmessage

#   DOM Manipulation via PostMessage
#
do ( dom = window ) ->

#
# Abstract API for doing small DOM manipulations
#
class DomManipulator
parseHTML
: ( strHtml ) ->
elConverted
= document.implementation.createHTMLDocument()
elConverted
.body.innerHTML = strHTML
elConverted
.body.children
stringifyHTML
: ( elTarget ) ->
if typeof elTarget is 'object' then elTarget.outerHTML else false
getElement
: ( strElement ) ->
elTarget
= document.body.querySelector( strElement )
elTarget

getText
: ( strElement ) ->
elTarget
= @getElement strElement
elTarget
.textContent
getProperty
: ( strElement, strProperty ) ->
elTarget
= @getElement strElement
if elTarget then elTarget.getAttribute( strProperty ) else false
setProperty
: ( strElement, strProperty, strValue ) ->
elTarget
= @getElement strElement
if elTarget then elTarget.setAttribute strProperty, strValue #elTarget[strProperty] = strValue
@getProperty strElement, strProperty
doMouseEvent
: ( strElement, strEvent ) ->
elTarget
= @getElement strElement
if elTarget
evtMouseAny
= document.createEvent('MouseEvents')
evtMouseAny
.initMouseEvent strEvent, true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null
elTarget
.dispatchEvent evtMouseAny
true
false

#
# Extends DOM Manipulator and transforms the manipulations to be compatible with PostMessage
#
class PostMessageDomManipulator extends DomManipulator
constructor: () ->

#
# Public PostMessage DomManipulator API
#
@objApi =
getElement
: (( objOptions, objJsonData ) ->
strElement
= objOptions['strElement']
if strElement then objJsonData.strReturn = @stringifyHTML @getElement(strElement)
).bind @
getText
: (( objOptions, objJsonData ) ->
strElement
= objOptions['strElement']
if strElement then objJsonData.strReturn = @getText strElement
).bind @
getProperty
: (( objOptions, objJsonData ) ->
strElement
= objOptions['strElement']
strProperty
= objOptions['strProperty']
if strElement and strProperty then objJsonData.strReturn = @getProperty strElement, strProperty
).bind @
setProperty
: (( objOptions, objJsonData ) ->
strElement
= objOptions['strElement']
strProperty
= objOptions['strProperty']
strValue
= objOptions['strValue']
if strElement and strProperty and strValue then objJsonData.strReturn = @setProperty strElement, strProperty, strValue
).bind @
doMouseEvent
: (( objOptions, objJsonData ) ->
strElement
= objOptions['strElement']
strEvent
= objOptions['strEvent']
if strElement and strEvent then objJsonData.strReturn = @doMouseEvent strElement, strEvent
).bind @

# Give a debug message that can be read
console
.debug 'Initializing postMessage protocol'

# Bind the postMessage to onReceiveData. That will transform the call for the Public API
dom
.addEventListener 'message', @onReceiveData.bind( @ )
@doSendData.bind(@) boolInitialized: true, strId: 'intitialize'

#
# For receiving messages via PostMessage to DOM Manipulation
#
onReceiveData
: ( e ) ->
strOrigin
= e.origin
objJsonData
= JSON.parse e.data
strLocation
= objJsonData['strLocation']
strAction
= objJsonData['strAction']
objParameters
= objJsonData['objParameters']

# Parent always needs to say what he expects, else we might end up going out of sync
boolExpected
= strLocation is window.location.href or strLocation is '*'
boolOrigin
= true
boolAction
= strAction
boolParameters
= objParameters
boolPassing
= boolExpected and boolOrigin and boolAction and boolParameters

if not boolPassing then return @doSendData strErrorMessage: 'Origin not allowed or you were expecting someone else.'

# Try to execute the request by testing if the action exists and can be called
try
objJsonData
['strReturn'] = false
if @objApi[strAction] then @objApi[strAction]( objParameters, objJsonData )
@doSendData objJsonData
catch
@doSendData { strErrorMessage: 'Could not pass action onto manipulator. Something malformed..' }

#
# For sending messages via PostMessage back to the parent
#
doSendData
: ( objData = {} ) ->
objData
.objLocation = window.location
objData
.strId = objData.strId or 'keep-alive'
strOrgin
= '*' # Needs to be set for safety, only send messages to allowed domain
strJsonData
= JSON.stringify objData
if parent
parent
.postMessage strJsonData, strOrgin
#
# on DOMContentLoaded initialize the PostMessageDomManipulator
#
onLoad
= () ->
dom
.pmdm = new PostMessageDomManipulator()
dom
.addEventListener 'DOMContentLoaded', onLoad

No lugar dos pais algo como

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>

<script>
window
.onload = function() {

var onClickDemo = function() {
console
.info('Trying to post a message to the target');
var objData = {
strId
: '',
strLocation
: '*',
strAction
: 'setProperty',
objParameters
: {
strElement
: '#amount',
strProperty
: 'value',
strValue
: '100'
}
}

document
.getElementsByTagName('iframe')[0].contentWindow.postMessage(JSON.stringify(objData), '*');
},
onClickTriggerDemo
= function() {
console
.info('Trying to trigger the mouse event');

var objData = {
strId
: 'ontoNextPage',
strLocation
: '*',
strAction
: 'doMouseEvent',
objParameters
: {
strElement
: '#continueButton',
strEvent
: 'click'
}
}
document
.getElementsByTagName('iframe')[0].contentWindow.postMessage(JSON.stringify(objData), '*');
},
onGetErrorMessage
= function() {
console
.info('Trying to get the error');

var objData = {
strLocation
: '*',
strAction
: 'getElement',
objParameters
: {
strElement
: '#textWithdrawalDeclined'
}
}
document
.getElementsByTagName('iframe')[0].contentWindow.postMessage(JSON.stringify(objData), '*');
}
onMessageReceived
= function( e ) {
console
.debug( 'data;', JSON.parse(e.data));
}
elIframe
= document.createElement('iframe');
elIframe
.width = '100%';
elIframe
.height = '800';

document
.body.querySelectorAll( '.try' )[0].addEventListener('click', onClickDemo);
document
.body.querySelectorAll( '.do' )[0].addEventListener('click', onClickTriggerDemo);

window
.addEventListener( 'message', onMessageReceived )
document
.body.appendChild(elIframe);
}
</script>

<input type="submit" value="Try a postMessage" class="try">
<input type="submit" value="Try a trigger" class="do">
</body>
</html>

Basicamente, você joga pacotes sobre a linha JSON stringified:

var objData = {
strId
: '',
strLocation
: '*',
strAction
: 'setProperty',
objParameters
: {
strElement
: '#amount',
strProperty
: 'value',
strValue
: '100'
}
}

document
.getElementsByTagName('iframe')[0].contentWindow.postMessage(JSON.stringify(objData), '*');

PostMessage DOM que fiz muito pequeno, você tem o seguinte:

  • getElement
  • getText
  • getProperty
  • setProperty
  • doMouseEvent

Você pode verificá-los e adicioná-los no DomManipulator

Felicidades!