Skip to content

Commit

Permalink
Move responsability of collapse frame box updating to host
Browse files Browse the repository at this point in the history
  • Loading branch information
alanorozco committed Jun 23, 2017
1 parent 225c29c commit 8ebba43
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 228 deletions.
99 changes: 99 additions & 0 deletions ads/inabox/frame-overlay-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* Copyright 2016 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 {expandFrame, collapseFrame} from './frame-overlay-helper';


/**
* Inabox host manager for full overlay frames.
*/
export class FrameOverlayManager {

/**
* @param {!Window} win
*/
constructor(win) {
/** @private @const {!Window} */
this.win_ = win;

/** @private {boolean} */
this.isExpanded_ = false;

/** @private {boolean} */
this.viewportChangedSinceExpand_ = false;

// TODO(alanorozco): type
/** @private {?} */
this.collapsedRect_ = null;

this.listenToViewportChanges_();
}

/** @private */
listenToViewportChanges_() {
this.win_.addEventListener('resize', () => this.onWindowResize());
}

/** @visibleForTesting */
onWindowResize() {
if (this.isExpanded_) {
this.viewportChangedSinceExpand_ = true;
}
}

/**
* Expands an iframe to full overlay.
* @param {!HTMLIFrameElement} iframe
* @param {!Function} callback Gets executed when expanded with the new box
* rect.
*/
expandFrame(iframe, callback) {
expandFrame(this.win_, iframe, (collapsedRect, expandedRect) => {
this.isExpanded_ = true;
this.viewportChangedSinceExpand_ = false;
this.collapsedRect_ = collapsedRect;
callback(expandedRect);
});
}

/**
* Collapses an iframe back from full overlay.
* @param {!HTMLIFrameElement} iframe
* @param {!Function} callback Gets executed when collapsed with the new box
* rect.
*/
collapseFrame(iframe, callback) {
// There is a delay of one animation frame between collapsing and measuring
// the box rect. collapseFrame() takes a callback for each event.
//
// We know what the collapsed box was. If the viewport has not changed while
// expanded, we can immediately notify the consumer of the collapsed
// box rect since it should be the same. Otherwise, we wait for remeasure.
collapseFrame(this.win_, iframe, () => {
this.isExpanded_ = false;

if (!this.viewportChangedSinceExpand_) {
callback(this.collapsedRect_);
}
}, collapsedRect => {
this.collapsedRect_ = collapsedRect;

if (this.viewportChangedSinceExpand_) {
callback(this.collapsedRect_);
}
});
}
}
38 changes: 13 additions & 25 deletions ads/inabox/inabox-messaging-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import {FrameOverlayManager} from './frame-overlay-manager';
import {PositionObserver} from './position-observer';
import {
serializeMessage,
Expand All @@ -22,7 +23,6 @@ import {
} from '../../src/3p-frame-messaging';
import {dev} from '../../src/log';
import {dict} from '../../src/utils/object';
import {expandFrame, collapseFrame} from './frame-overlay-helper';

/** @const */
const TAG = 'InaboxMessagingHost';
Expand Down Expand Up @@ -71,6 +71,7 @@ export class InaboxMessagingHost {
this.registeredIframeSentinels_ = Object.create(null);
this.positionObserver_ = new PositionObserver(win);
this.msgObservable_ = new NamedObservable();
this.frameOverlayManager_ = new FrameOverlayManager(win);

this.msgObservable_.listen(
MessageType.SEND_POSITIONS, this.handleSendPositions_);
Expand Down Expand Up @@ -149,19 +150,14 @@ export class InaboxMessagingHost {
// 1. Reject request if frame is out of focus
// 2. Disable zoom and scroll on parent doc
handleEnterFullOverlay_(iframe, request, source, origin) {
expandFrame(this.win_, iframe, (collapsedRect, expandedRect) => {
// Inform client of dimensions before and after expand.
// The rect value before expand is propagated so that the client has the
// option to update its own dimensions after collapse without having to
// wait for remeasure.
this.frameOverlayManager_.expandFrame(iframe, boxRect => {
source./*OK*/postMessage(
serializeMessage(
MessageType.FULL_OVERLAY_FRAME_RESPONSE,
request.sentinel,
dict({
'success': true,
'collapsedRect': collapsedRect,
'expandedRect': expandedRect,
'boxRect': boxRect,
})),
origin);
});
Expand All @@ -177,24 +173,16 @@ export class InaboxMessagingHost {
* @return {boolean}
*/
handleCancelFullOverlay_(iframe, request, source, origin) {
collapseFrame(this.win_, iframe, () => {
// Inform client that the iframe has been collapsed.
// We send two separate messages for different events (collapsed/measure)
// as measure is asynchronous and we want the client to be able to update
// itself without waiting when possible.
this.frameOverlayManager_.collapseFrame(iframe, boxRect => {
source./*OK*/postMessage(
serializeMessage(
MessageType.CANCEL_FULL_OVERLAY_FRAME_RESPONSE,
request.sentinel,
dict({'success': true})),
origin);
}, collapsedRect => {
source./*OK*/postMessage(
serializeMessage(
MessageType.CANCEL_FULL_OVERLAY_FRAME_REMEASURE,
request.sentinel,
dict({'collapsedRect': collapsedRect})),
origin);
serializeMessage(
MessageType.CANCEL_FULL_OVERLAY_FRAME_RESPONSE,
request.sentinel,
dict({
'success': true,
'boxRect': boxRect,
})),
origin);
});

return true;
Expand Down
1 change: 0 additions & 1 deletion src/3p-frame-messaging.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export const MessageType = {
FULL_OVERLAY_FRAME_RESPONSE: 'full-overlay-frame-response',
CANCEL_FULL_OVERLAY_FRAME: 'cancel-full-overlay-frame',
CANCEL_FULL_OVERLAY_FRAME_RESPONSE: 'cancel-full-overlay-frame-response',
CANCEL_FULL_OVERLAY_FRAME_REMEASURE: 'cancel-full-overlay-frame-remeasure',

// For amp-inabox
SEND_POSITIONS: 'send-positions',
Expand Down
127 changes: 3 additions & 124 deletions src/inabox/inabox-viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,71 +97,6 @@ export function resetFixedContainer(win, fixedContainer) {
}


/**
* An observable that triggers with a cached value on demand, as long as the
* cahced value is valid.
* @template T
*/
class CachedValueObservable {

/**
* @param {T=} opt_initialValue
*/
constructor(opt_initialValue) {
/** @type {!Observable<T>} */
this.observable_ = new Observable();

/** @type {?T} */
this.value_ = opt_initialValue || null;
}

/**
* @param {function(T)} handler
* @return {!UnlistenDef}
*/
add(handler) {
return this.observable_.add(handler);
}

/**
* Invalidates cached value.
*/
invalidateValue() {
this.value_ = null;
}

/**
* Sets cached value.
* @param {T} value
*/
setValue(value) {
this.value_ = value;
}

/**
* @return {?T}
*/
getValueOptional() {
return this.value_;
}

/**
* @param {T=} opt_value
*/
fire(opt_value) {
const shouldExecute = !this.value_ !== !opt_value;

if (opt_value && !this.value_) {
this.setValue(dev().assert(opt_value));
}

if (shouldExecute) {
this.observable_.fire(dev().assert(this.value_));
}
}
}


/**
* Implementation of ViewportBindingDef that works inside an non-scrollable
* iframe box by listening to host doc for position and resize updates.
Expand Down Expand Up @@ -207,33 +142,15 @@ export class ViewportBindingInabox {
*/
this.boxRect_ = layoutRectLtwh(0, boxHeight + 1, boxWidth, boxHeight);

/**
* Keeps track of the iframe box rect when collapsed (full overlay mode) and
* fires observers if the rect is not stale.
* @private @const {!CachedValueObservable<!../layout-rect.LayoutRectDef>}
*/
this.collapsedRectObservable_ = new CachedValueObservable(this.boxRect_);

/**
* @type {boolean}
* @visibleForTesting
*/
this.inFullOverlayMode = false;

/** @private @const {!../../3p/iframe-messaging-client.IframeMessagingClient} */
this.iframeClient_ = iframeMessagingClientFor(win);

this.collapsedRectObservable_.add(collapsedBoxRect => {
this.updateBoxRect_(collapsedBoxRect);
});

dev().fine(TAG, 'initialized inabox viewport');
}

/** @override */
connect() {
this.listenForPosition_();
this.listenForHostRemeasure_();
}

/** @private */
Expand All @@ -253,16 +170,6 @@ export class ViewportBindingInabox {

this.updateBoxRect_(data.target);

// When the viewport changes it's possible that our cached rect for
// the collapsed frame box is stale, so we invalidate our cached
// value.
// TODO(alanorozco): Invalidate when using native InOb
if (this.inFullOverlayMode &&
isChanged(this.viewportRect_, oldViewportRect)) {

this.collapsedRectObservable_.invalidateValue();
}

if (isResized(this.viewportRect_, oldViewportRect)) {
this.resizeObservable_.fire();
}
Expand All @@ -272,22 +179,6 @@ export class ViewportBindingInabox {
});
}

/** @private */
listenForHostRemeasure_() {
this.iframeClient_.registerCallback(
MessageType.CANCEL_FULL_OVERLAY_FRAME_REMEASURE,
data => this.handleCollapseOverlayRemeasure(data.collapsedRect));
}

/**
* @param {!../layout-rect.LayoutRectDef} rect
* @visibleForTesting
*/
handleCollapseOverlayRemeasure(rect) {
// Host has remeasured box after collapse
this.collapsedRectObservable_.fire(rect);
}

/** @override */
getLayoutRect(el) {
const b = el./*OK*/getBoundingClientRect();
Expand Down Expand Up @@ -429,13 +320,8 @@ export class ViewportBindingInabox {
MessageType.FULL_OVERLAY_FRAME_RESPONSE,
response => {
unlisten();

this.inFullOverlayMode = true;

if (response.success) {
this.collapsedRectObservable_.setValue(response.collapsedRect);
this.updateBoxRect_(response.expandedRect);

this.updateBoxRect_(response.boxRect);
resolve();
} else {
reject('Request to open lightbox rejected by host document');
Expand All @@ -453,16 +339,9 @@ export class ViewportBindingInabox {
const unlisten = this.iframeClient_.makeRequest(
MessageType.CANCEL_FULL_OVERLAY_FRAME,
MessageType.CANCEL_FULL_OVERLAY_FRAME_RESPONSE,
() => {
response => {
unlisten();

this.inFullOverlayMode = false;

// Fire cached collapsed rect observable. If our viewport changed
// in the meantime, our value will be stale and the observable
// won't fire. In this case, we'll wait for remeasure.
// (See message handler for CANCEL_FULL_OVERLAY_FRAME_REMEASURE)
this.collapsedRectObservable_.fire();
this.updateBoxRect_(response.boxRect);
resolve();
});
});
Expand Down

0 comments on commit 8ebba43

Please sign in to comment.