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

Refactor AmpContext dependencies for building #8258

Merged
merged 11 commits into from
Mar 23, 2017
10 changes: 8 additions & 2 deletions 3p/ampcontext-lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import './polyfills';
import {AmpContext} from './ampcontext.js';
import {initLogConstructor, setReportError} from '../src/log';
import {reportError} from '../src/error';


initLogConstructor();
setReportError(reportError);


// TODO(alanorozco): Refactor src/error.reportError so it does not contain big
// transitive dependencies and can be included here.
setReportError(() => {});


/**
Expand Down
52 changes: 37 additions & 15 deletions 3p/ampcontext.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import './polyfills';
import {dev} from '../src/log';
import {IframeMessagingClient} from './iframe-messaging-client';
import {MessageType} from '../src/3p-frame';
import {MessageType} from '../src/3p-frame-messaging';
import {tryParseJson} from '../src/json';

export class AmpContext {

/**
* @param {Window} win The window that the instance is built inside.
* @param {!Window} win The window that the instance is built inside.
*/
constructor(win) {
/** @private {!Window} */
this.win_ = win;

/** @type {?string} */
this.location = null;

/** @type {?string} */
this.canonicalUrl = null;

/** @type {?string} */
this.pageViewId = null;

/** @type {?string} */
this.sentinel = null;

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

// TODO(alanorozco): confirm type
/** @type {?string} */
this.referrer = null;

this.findAndSetMetadata_();
this.client_ = new IframeMessagingClient(win);
this.client_.setHostWindow(this.getHostWindow_());
this.client_.setSentinel(this.sentinel);
this.client_.setSentinel(dev().assertString(this.sentinel));
}

/**
Expand Down Expand Up @@ -64,8 +84,8 @@ export class AmpContext {
/**
* Send message to runtime requesting to resize ad to height and width.
* This is not guaranteed to succeed. All this does is make the request.
* @param {int} width The new width for the ad we are requesting.
* @param {int} height The new height for the ad we are requesting.
* @param {number} width The new width for the ad we are requesting.
* @param {number} height The new height for the ad we are requesting.
*/
requestResize(width, height) {
this.client_.sendMessage(MessageType.EMBED_SIZE, {width, height});
Expand All @@ -75,8 +95,8 @@ export class AmpContext {
* Allows a creative to set the callback function for when the resize
* request returns a success. The callback should be set before resizeAd
* is ever called.
* @param {function(requestedHeight, requestedWidth)} callback Function
* to call if the resize request succeeds.
* @param {function(number, number)} callback Function to call if the resize
* request succeeds.
*/
onResizeSuccess(callback) {
this.client_.registerCallback(MessageType.EMBED_SIZE_CHANGED, obj => {
Expand All @@ -87,8 +107,8 @@ export class AmpContext {
* Allows a creative to set the callback function for when the resize
* request is denied. The callback should be set before resizeAd
* is ever called.
* @param {function(requestedHeight, requestedWidth)} callback Function
* to call if the resize request is denied.
* @param {function(number, number)} callback Function to call if the resize
* request is denied.
*/
onResizeDenied(callback) {
this.client_.registerCallback(MessageType.EMBED_SIZE_DENIED, obj => {
Expand All @@ -99,7 +119,7 @@ export class AmpContext {
/**
* Takes the current name on the window, and attaches it to
* the name of the iframe.
* @param {HTMLIframeElement} iframe The iframe we are adding the context to.
* @param {HTMLIFrameElement} iframe The iframe we are adding the context to.
*/
addContextToIframe(iframe) {
iframe.name = this.win_.name;
Expand All @@ -108,15 +128,15 @@ export class AmpContext {
/**
* Parse the metadata attributes from the name and add them to
* the class instance.
* @param {!Object|string} contextData
* @param {!Object|string} data
* @private
*/
setupMetadata_(data) {
data = tryParseJson(data);
if (!data) {
const dataObject = typeof data === 'string' ? tryParseJson(data) : data;
if (!dataObject) {
throw new Error('Could not setup metadata.');
}
const context = data._context;
const context = dataObject._context;
this.location = context.location;
this.canonicalUrl = context.canonicalUrl;
this.pageViewId = context.pageViewId;
Expand Down Expand Up @@ -149,6 +169,8 @@ export class AmpContext {
findAndSetMetadata_() {
// If the context data is set on window, that means we don't need
// to check the name attribute as it has been bypassed.
// TODO(alanorozco): why the heck could AMP_CONTEXT_DATA be two different
// types? FIX THIS.
if (!this.win_.AMP_CONTEXT_DATA) {
this.setupMetadata_(this.win_.name);
} else if (typeof this.win_.AMP_CONTEXT_DATA == 'string') {
Expand Down
7 changes: 5 additions & 2 deletions 3p/iframe-messaging-client.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {listen} from '../src/event-helper';
import {map} from '../src/utils/object';
import {serializeMessage, deserializeMessage} from '../src/3p-frame';
import {
listen,
serializeMessage,
deserializeMessage,
} from '../src/3p-frame-messaging';
import {getMode} from '../src/mode';
import {dev} from '../src/log';

Expand Down
109 changes: 68 additions & 41 deletions 3p/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
*/

import './polyfills';
import {AmpContext} from './ampcontext';
import {installEmbedStateListener, manageWin} from './environment';
import {isExperimentOn} from '../src/experiments';
import {nonSensitiveDataPostMessage, listenParent} from './messaging';
import {
computeInMasterFrame,
Expand Down Expand Up @@ -151,6 +153,11 @@ import {zedo} from '../ads/zedo';
import {zergnet} from '../ads/zergnet';
import {zucks} from '../ads/zucks';


/** @const {string} */
const AMP_CONTEXT_EXPERIMENT = '3p-use-ampcontext';


/**
* Whether the embed type may be used with amp-embed tag.
* @const {!Object<string, boolean>}
Expand Down Expand Up @@ -396,57 +403,23 @@ function isMaster() {
window.draw3p = function(opt_configCallback, opt_allowed3pTypes,
opt_allowedEmbeddingOrigins) {
try {
const location = parseUrl(data._context.location.href);

ensureFramed(window);
window.context.location = parseUrl(data._context.location.href);
validateParentOrigin(window, window.context.location);
validateParentOrigin(window, location);
validateAllowedTypes(window, data.type, opt_allowed3pTypes);
if (opt_allowedEmbeddingOrigins) {
validateAllowedEmbeddingOrigins(window, opt_allowedEmbeddingOrigins);
}
// Define master related properties to be lazily read.
Object.defineProperties(window.context, {
master: {
get: () => masterSelection(data.type),
},
isMaster: {
get: isMaster,
},
});
window.context.data = data;
window.context.noContentAvailable = triggerNoContentAvailable;
window.context.requestResize = triggerResizeRequest;
window.context.renderStart = triggerRenderStart;

if (data.type === 'facebook' || data.type === 'twitter') {
// Only make this available to selected embeds until the
// generic solution is available.
window.context.updateDimensions = triggerDimensions;
}

// This only actually works for ads.
const initialIntersection = window.context.initialIntersection;
window.context.observeIntersection = cb => {
const unlisten = observeIntersection(cb);
// Call the callback with the value that was transmitted when the
// iframe was drawn. Called in nextTick, so that callers don't
// have to specially handle the sync case.
nextTick(window, () => cb([initialIntersection]));
return unlisten;
};
window.context.onResizeSuccess = onResizeSuccess;
window.context.onResizeDenied = onResizeDenied;
window.context.reportRenderedEntityIdentifier =
reportRenderedEntityIdentifier;
window.context.computeInMasterFrame = computeInMasterFrame;
window.context.addContextToIframe = iframe => {
iframe.name = iframeName;
};
window.context.getHtml = getHtml;
installContext(window);
delete data._context;
manageWin(window);
installEmbedStateListener();
draw3p(window, data, opt_configCallback);
updateVisibilityState(window);

// TODO(alanorozco): use IframeMessagingClient for updates when experiment
// flag is on.
// Subscribe to page visibility updates.
nonSensitiveDataPostMessage('send-embed-state');
nonSensitiveDataPostMessage('bootstrap-loaded');
Expand All @@ -459,6 +432,60 @@ window.draw3p = function(opt_configCallback, opt_allowed3pTypes,
}
};

function installContext(win) {
if (isExperimentOn(win, AMP_CONTEXT_EXPERIMENT)) {
// TODO(alanorozco): Enhance AmpContext to match standard implementation.
win.context = new AmpContext(win);
return;
}

installContextUsingStandardImpl(win);
}

function installContextUsingStandardImpl(win) {
// Define master related properties to be lazily read.
Object.defineProperties(win.context, {
master: {
get: () => masterSelection(data.type),
},
isMaster: {
get: isMaster,
},
});

win.context.data = data;
win.context.location = parseUrl(data._context.location.href);
win.context.noContentAvailable = triggerNoContentAvailable;
win.context.requestResize = triggerResizeRequest;
win.context.renderStart = triggerRenderStart;

if (data.type === 'facebook' || data.type === 'twitter') {
// Only make this available to selected embeds until the
// generic solution is available.
win.context.updateDimensions = triggerDimensions;
}

// This only actually works for ads.
const initialIntersection = win.context.initialIntersection;
win.context.observeIntersection = cb => {
const unlisten = observeIntersection(cb);
// Call the callback with the value that was transmitted when the
// iframe was drawn. Called in nextTick, so that callers don't
// have to specially handle the sync case.
nextTick(win, () => cb([initialIntersection]));
return unlisten;
};
win.context.onResizeSuccess = onResizeSuccess;
win.context.onResizeDenied = onResizeDenied;
win.context.reportRenderedEntityIdentifier =
reportRenderedEntityIdentifier;
win.context.computeInMasterFrame = computeInMasterFrame;
win.context.addContextToIframe = iframe => {
iframe.name = iframeName;
};
win.context.getHtml = getHtml;
}

function triggerNoContentAvailable() {
nonSensitiveDataPostMessage('no-content');
}
Expand Down
2 changes: 1 addition & 1 deletion ads/inabox/inabox-messaging-host.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
serializeMessage,
deserializeMessage,
MessageType,
} from '../../src/3p-frame';
} from '../../src/3p-frame-messaging';
import {dev} from '../../src/log';

/** @const */
Expand Down
2 changes: 2 additions & 0 deletions build-system/amp.extern.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ window.AMP_CONFIG.thirdPartyFrameRegex;
window.AMP_CONFIG.cdnUrl;
window.AMP_CONFIG.errorReportingUrl;

window.AMP_CONTEXT_DATA;

// amp-viz-vega related externs.
/**
* @typedef {{spec: function(!JSONType, function())}}
Expand Down
9 changes: 9 additions & 0 deletions build-system/tasks/presubmit-checks.js
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,15 @@ var forbiddenTerms = {
'src/inabox/inabox-viewer.js',
],
},
'internalListenImplementation': {
message: 'Use `listen()` in either `event-helper` or `3p-frame-messaging`' +
', depending on your use case.',
whitelist: [
'src/3p-frame-messaging.js',
'src/event-helper.js',
'src/event-helper-listen.js',
],
},
'setTimeout.*throw': {
message: 'Use dev.error or user.error instead.',
whitelist: [
Expand Down
7 changes: 7 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,13 @@ function checkTypes() {
includePolyfills: true,
checkTypes: true,
}),
closureCompile(['./3p/ampcontext-lib.js'], './dist',
'ampcontext-check-types.js', {
externs: ['ads/ads.extern.js'],
include3pDirectories: true,
includePolyfills: true,
checkTypes: true,
}),
]);
}

Expand Down
Loading