Skip to content
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

Calling __cmp from iframes do not work #174

Closed
Fumler opened this issue Jul 19, 2018 · 2 comments
Closed

Calling __cmp from iframes do not work #174

Fumler opened this issue Jul 19, 2018 · 2 comments

Comments

@Fumler
Copy link
Contributor

Fumler commented Jul 19, 2018

Hello.

Thank you for making this project, it's great!

One of our advertisers noticed that calling __cmp from an iframe does not work. They are following the specc: https://github.com/InteractiveAdvertisingBureau/GDPR-Transparency-and-Consent-Framework/blob/master/CMP%20JS%20API%20v1.1%20Final.md#how-can-vendors-that-use-iframes-call-the-cmp-api-from-an-iframe-

Reproduction:
index.html

<!DOCTYPE html>
<html>
    <head>
        <script id="oil-configuration" type="application/configuration">
        {
            "publicPath": "https://oil.axelspringer.com/release/1.2.1"
        }
        </script>
        <script>
        /*! 1.2.1-RELEASE */!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:r})},n.r=function(e){Object.defineProperty(e,"__esModule",{value:!0})},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="//oil.axelspringer.com/release/1.2.1/",n(n.s=115)}({115:function(e,t,n){"use strict";!function(e,t){e.__cmp||(e.__cmp=function(){function n(){return!(!e.AS_OIL||!e.AS_OIL.commandCollectionExecutor)}(e.attachEvent||e.addEventListener)("message",function(t){e.__cmp.receiveMessage(t)},!1),function e(){if(!(t.getElementsByName("__cmpLocator").length>0))if(t.body){var n=t.createElement("iframe");n.style.display="none",n.name="__cmpLocator",t.body.appendChild(n)}else setTimeout(e,5)}();var r=[],o=function(o,a,c){if("ping"===o)!function(e){if(e){var r=!0,o=t.querySelector('script[type="application/configuration"]#oil-configuration');if(null!==o&&o.text)try{var a=JSON.parse(o.text);a&&a.hasOwnProperty("gdpr_applies_globally")&&(r=a.gdpr_applies_globally)}catch(e){}e({gdprAppliesGlobally:r,cmpLoaded:n()},!0)}}(c);else{var l={command:o,parameter:a,callback:c};r.push(l),n()&&e.AS_OIL.commandCollectionExecutor(l)}};return o.commandCollection=r,o.receiveMessage=function(e){var t=e&&e.data&&e.data.__cmpCall;t&&r.push({callId:t.callId,command:t.command,parameter:t.parameter,event:e})},o}())}(window,document)}});
        </script>
        <script type="text/javascript" src="https://oil.axelspringer.com/release/1.2.1/oil.1.2.1-RELEASE.min.js"></script>
        
    </head>
    <body>
        <iframe src="http://localhost:8081/iframe.html">
        </iframe>
        <script type="text/javascript">
            // Workaround we use to try and fix below
            
            // function cmpMsgHandler(event) {
            //     var msgIsString = typeof event.data === "string";
            //     var json = msgIsString ? JSON.parse(event.data) : event.data;
            //     if (json.__cmpCall) {
            //         var i = json.__cmpCall;
            //         window.__cmp(i.command, i.parameter, function(retValue, success) {
            //             var returnMsg = {"__cmpReturn": {
            //             "returnValue": retValue,
            //             "success": success,
            //                 "callId": i.callId
            //             }};
            //             event.source.postMessage(msgIsString ?
            //             JSON.stringify(returnMsg) : returnMsg, '*');
            //         });
            //     }

            // }
            // var result = null
            // window.__cmp('ping', null, function(res) { console.log(res); result = JSON.stringify(res) })
            // document.querySelector('.test').innerHTML = result

            //window.addEventListener('message', cmpMsgHandler)
        </script>
    </body>
</html>

iframe.html

<!DOCTYPE html>
<html>
<head>
</head>
<body>
    This is an iframe
    <script type="text/javascript">
        // find the CMP frame
        var f = window;
        var cmpFrame;
        while (!cmpFrame) {
            try {
                if (f.frames["__cmpLocator"]) cmpFrame = f;
            } catch (e) {
                console.log("Catched: ", e)
             }
            if (f === window.top) break;
            f = f.parent;
        }
        var cmpCallbacks = {}
        /* Set up a __cmp function to do the postMessage and
           stash the callback.
           This function behaves (from the caller's perspective)
           identically to the in-frame __cmp call */
        window.__cmp = function (cmd, arg, callback) {
            if (!cmpFrame) {
                console.log("no cmpFrame")
                callback({ msg: "CMP not found" }, false);
                return;
            }
            console.log('have cmpFrame')
            var callId = Math.random() + "";
            var msg = {
                __cmpCall: {
                    command: cmd,
                    parameter: arg,
                    callId: callId
                }
            };
            cmpCallbacks[callId] = callback;
            cmpFrame.postMessage(msg, '*');
        }

        /* when we get the return message, call the stashed callback */
        window.addEventListener("message", function (event) {
            console.log('message')
            var json = typeof event.data === "string" ? JSON.parse(event.data) : event.data;
            if (json.__cmpReturn) {
                var i = json.__cmpReturn;
                cmpCallbacks[i.callId](i.returnValue, i.success);
                delete cmpCallbacks[i.callId];
            }
        }, false);

        /* example call of the above __cmp wrapper function */
        __cmp("ping", null, function (val, success) {
            console.log("val=", val, " success=", success)
        });
    </script>
</body>
</html>

And serve these files on localhost:8081

With that code you will not get the val= success= logs, but if you uncomment the commented code, you will.

@ltparis2018
Copy link
Contributor

Hello Fredrik,
thank you for your report and the provided workaround. You're right, it's a bug in the event handler.
The handling of CMP commands is a little bit complicated. The very small OIL stub that is loaded at first (to provide the CMP API as soon as possible) only registers CMP command invocations. When the complete layer is loaded at a later time, all registered commands are performed. When a further command is invoked now it must be registered and invoked immediately.
Because of the bug the message event handler for iframe messages currently only registers the invoked CMP command but does not perform it immediately if complete opt-in layer is already loaded.
We will fix it in the next release.
Greetings,
Alex.

@ltparis2018
Copy link
Contributor

Fix provided with pull request #176.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants