Issues with sending and receiving messages

Viktor
2013-12-19
2014-01-02
  • Viktor
    Viktor
    2013-12-19

    We've encountered a few problems when exchanging messages between host page and frame page.

    1. When a frame page sends several messages (ext.js, message()-function) to the host page in quick succession, only the first message gets through. Would it make sense to implement some sort of message queue in ext.js to handle the messaging, ensuring that all messages are sent, one at a time? Now we do the queueing on the frame page, which isn't very tidy.

    2. The host page doesn't send any response to the frame page in case of custom messages (host.js, _handle_msg_evt()-function), thus blocking the communication at least until the frame page resets its internal variables after MAX_MSG_WAIT_TIME (ext.js, _send_msg()-function). We've hacked around this by calling _send_response() in case: "msg" in_handle_msg_evt(). Should the confirmation be done in onPosMsg() instead? If so, what function call should be used as _send_response() is marked as private?

     
  • Sean Snider
    Sean Snider
    2013-12-19

    No we don't really que them nor do we want to. .

    The reason why it's designed this way is that each message in theory could change state, and there by a subsequent message / call may want to know the new state. Even for custom messages it should work that way. . . It's also done this way b/c we want to avoid spamming of cross-domain messages for performance reasons as well since it can get expensive quickly.

    Note that custom messages are not actually part of the standard (yet). The "message" function you speak of is not part of the standard, it just happens to be there for our own prototyping until we get to the point (in the next rev) where custom messaging is actually supported.

    But yes ideally what would happen for any message is a send-to-host->receive-by-host->confirm-receipt methodology. The reference imp code at the moment is probably not doing that, but we can fix that moving forward (and already do this at Yahoo!).
    But that's not done in "onPosMsg". onPosMsg is the event/callback for the outside page/host. You do fire an onPosMsg out to the page/host, but that's separate from sending back a confirmation to the originating SafeFrame. Both should be done. . .

    Some messages can be cancelled by virtue of returning true from onBeforePosMsg (like say expansion/collapse, etc), but some cannot (like "geom-update").

    A SafeFrame should not be trying to send "several messages", as there is no reason to do that. You cannot do animation in this way b/c it will never perform fast enough (and we are going to tackle that a different way in the next rev).

     
    • Viktor
      Viktor
      2013-12-20

      Thanks for your swift reply.

      We have a couple of cases where we need to send some messages from the frame to the host page.

      1. The host page doesn't know the final size of the served ad before it has loaded. For example, a "leaderboard" banner might be 980x120, 980x400 or even 728x90. To deal with this, we create the SafeFrame as 10x10, and the frame then sends its final dimensions to the host page when the ad has loaded so the host page can resize the frame accordingly. We currently use the expand functionality for this, though it's obviously meant for expandable ads.
      2. Some ads may determine whether another ad spot should be rendered or not. If an ad affects the rendering of other ads ("I'm a big ad and I need all space I can get, so don't show my sibling ads"), it communicates this to the host page so the host page knows to leave the other ones unrendered. We currently use the message function with a custom data parameter for this.
      3. Some ads may need some special treatment from the host page ("I'm a very important ad, so make my container sticky when the user scrolls the page"). Again, we use the message function to let the ad communicate this to the host page.

      In other words, at least for now it's mostly about the ad using a few messages to communicate its properties to the host page when the ad is rendering or has finished rendering, not sending dozens or hundreds of messages. In the cases above, it's naturally crucial that the messages are delivered to the host page, or the ads won't work as expected.

       
  • Chris Cole
    Chris Cole
    2013-12-20

    These are great feature use cases. I think we should include these as supported use cases for our v.Next enhancements to make sure what we do supports real world needs.

    In terms of what might be built into the reference implementation without breaking adherence to the spec, I like the queuing approach. We should probably do a short queue in ext and have it delay for maybe 50-100 ms to see if we can send a burst of multiple messages at once. With regards to there not being a response message, I think this is an oversight we should fix. We should also put an absolute cap in place - maybe 10 messages from an ad - publisher configurable.

     
  • Sean Snider
    Sean Snider
    2013-12-26

    @Victor

    Let me go through your list above, and comment based on what I know is coming up next:

    1.) Yes this is a pretty common use case, and in the next rev we plan to address this.
    There are a few ways that this will get addressed:
    a.) A "resize" method in the API, separate from expansion. Resizing basically redefines the collapsed/initial size dynamically on the fly, and of course resizes the SafeFrame to whatever size you specify. Resizing is like push expansion in that the host page has to explicitly say they "support" that feature, and of course they have to set up their page appropriately to allow for changing the size. There will likely be some rules around this as well such as, not being able to do a resize if you are in an expanded state or what not.
    b.) An automatic resize at startup could be something that we have as a setting on the host side as well. For example the SafeFrame is loaded with a bunch of content that obviously needs to be bigger than the configuration. Instead of requiring an explicit API call, assuming the configuration is on, once a SafeFrame is loaded we'd just automatically do a resize. This would be a one time thing, at load of the SafeFrame, so async content loaded into a SafeFrame or content added dynamically after the load would still require a call to the API (to avoid scenarios where someone abusively adds content to keep growing the container).
    c.) In both cases we may also allow the host to define mins/maxs for width/height that apply to resizing (and perhaps to expansion as well).

    2.) I'd like to understand this use case better / how you are wanting to do this. . .
    The host page obviously is in control of all the "positions" on a given page which are defined inline. We have no plans at the moment for a given inline SafeFrame to be able to create another inline SafeFrame somewhere else on the page, mainly b/c that would require a complex scenario where a page would have to somehow define empty positions that could be used or something. Now note also that I said "inline" b/c. .
    a.) We do also plan (again in the next rev) to have a "layer API". A layer is essentially a floating SafeFrame that can be created by any given inline SafeFrame. Said layer can be positioned anywhere in the page relative to the document, and can be setup to scroll with the window, or be in fixed position (staying when user scrolls). A layer is an overlay so it sits on top of page content (which may have just whitespace, or may not).
    The idea behind this API is to allow takeover/interstials, or panels, or a survey/dialog, etc.
    b.) We also have (defined for the next rev), an API for "close" of a given inline SafeFrame (as opposed to collapse which only applies to expansion). Again subject to host permissions, a given SafeFrame could close itself, no longer taking up room on the page (and note that a resize cmd of 0,0 would implictly do the same thing). There would probably be an "open" method as well, to restore you to the original state. (So you'd have expand/collapse, open/close).
    c.) Custom messaging is also targeted for the next release, so assuming that items 2a and 2b would cover your needs along with custom messaging, I think that will address things, although I'm curious to know more about your use-cases.
    3.) No inline SafeFrame can define itself as "fixed" position on it's own. You could as the host page set that up yourself I suppose, but that's not something we could allow a creative inside a SafeFrame to do mainly b/c it would be complicated since the SafeFrame is inline in the page at start. That said, I think 2a (layer API) again should cover this use-case.

    @All

    In terms of these new additions to the API, there will likely be additional rules (as I noted), and also some changes in the API to prevent abuse, since changing inline dimensions of a given SafeFrame or drawing a layer over content could have very bad effects if abused.

    In terms of queuing messages. . . we have to be very very careful with that. . .

    The reason is that most messaging is changing some sort of state which is effecting the host, and the given SafeFrame in terms of dimensions / geometry. For example, from inside a given SafeFrame, you do an expand, but you also want to send a message to another SafeFrame (containing say a sister/companion ad) to close/hide itself.

    So the API user does:

    $sf.ext.expand({l:500,b:200});
    $sf.ext.msg("[some custom msg]", "[channel/other SafeFrame ID]")

    Now the $sf.ext.expand call sets up internal state inside the SafeFrame saying "i'm sending an expansion command/request to the host". The $sf.ext.msg call is "qued" b/c there is already a pending command.

    The expansion request gets routed to the top/host, but then the host page, rejects this expansion for some reason. Now you get a failure, which gets routed back to the original SafeFrame.

    At this point, with simple queuing, the command/request for $sf.ext.msg would get sent to the host and then on to the other SafeFrame. But it won't necessarily know that the expansion failed, rending the custom message call moot.

    Situations like this can get complicated really quickly. . . so as far as I'm concerned, the SafeFrame API itself should NOT keep a que. Rather it deals with one command at a time. If the content inside the SafeFrame wants to have a que, that of course is fine, but it still forces them to listen to notifications about status changes, and prevents abuse / misuse.

    The above situation would be much more correct/stable by just doing:
    var notifying_others_to_close = null;

    function my_sf_handler(cmd, info)
    {
    if (cmd == "expanded") {
    if (notifying_others_to_close === false) {
    notifying_others_to_close = true;
    $sf.ext.msg("[some custom msg]", "[channel/other SafeFrame ID]");
    }
    } else if (cmd == "msg" ) {
    if (notifying_others_to_close === true) {
    if ("custom msg succes val") {
    / YAY /
    } else {
    / boo /
    }
    }
    notifying_others_to_close = null;
    }

    $sf.ext.register(300,250,my_sf_handler);

    //. . . inside some other function where
    //where will will perform a special expansion + changing of state of other sibling
    //SafeFrames

    notifying_others_to_close = false;
    $sf.ext.expand({l:500,b:200})

    /
    * Do our expansion, and wait for return/status update.
    * If expansion succeeds, close our sibling ads
    /

     
    • Viktor
      Viktor
      2014-01-02

      2.0 is indeed going to be a major version bump if all these (among other) changes are implemented. Looking forward to it!

      1. The automatic resize functionality that you outlined in 1b matches our use case exactly. A general resize command sounds useful as well.

      2. One of our scenarios is a sidebar with 3 skyscraper ad spots. The skyscraper ads can be either sticky (ad stays on screen when user scrolls) or normal. The first ad spot can contain either a sticky ad or a normal ad, while the two other spots can contain only normal ads. If the first spot has a normal ad, the two other spots are rendered normally. But if the first spot has a sticky ad, the two other spots are left unrendered. The host page obviously has to coordinate this, delaying the rendering of the second and third spots until the ad in the first spot has finished rendering and told the host page what kind of an ad it is. Thus, the layer API with floating SafeFrames probably doesn't cover this scenario, but we can handle this with custom messages combined with render logic on the host page.

      3. The example above applies to this use case as well.

      As for the message queueing, I understand your point with one message depending on the success of another. But how would one make it possible to send two (or more) independent messages without queueing them somehow? Let's say we have four ads that trigger the following messages:

      Ad 1:
      $sf.ext.msg("[custom message A]");
      $sf.ext.msg("[custom message B]");

      Ad 2:
      $sf.ext.msg("[custom message B]");
      $sf.ext.msg("[custom message A]");

      Ad 3:
      $sf.ext.msg("[custom message A]");

      Ad 4:
      $sf.ext.msg("[custom message B]");

      As far as I can see, rewriting these as one call + message handler can easily get messy too.

      Message queueing combined with something like the success/error callback syntax in jQuery.get() would probably allow for both dependent and independent messages.