Skip to content

Commit

Permalink
Extension installation for FIE ampdocs (ampproject#23153)
Browse files Browse the repository at this point in the history
* Extension installation for FIE ampdocs

* cleanup

* type fixes
  • Loading branch information
Dima Voytenko authored Jul 9, 2019
1 parent bd7be86 commit 0f91887
Show file tree
Hide file tree
Showing 18 changed files with 539 additions and 34 deletions.
8 changes: 4 additions & 4 deletions extensions/amp-a4a/0.1/a4a-variable-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ const WHITELISTED_VARIABLES = [
/** Provides A4A specific variable substitution. */
export class A4AVariableSource extends VariableSource {
/**
* @param {!../../../src/service/ampdoc-impl.AmpDoc} ampdoc
* @param {!../../../src/service/ampdoc-impl.AmpDoc} parentAmpdoc
* @param {!Window} embedWin
*/
constructor(ampdoc, embedWin) {
super(ampdoc);
constructor(parentAmpdoc, embedWin) {
super(parentAmpdoc);

// Use parent URL replacements service for fallback.
const headNode = ampdoc.getHeadNode();
const headNode = parentAmpdoc.getHeadNode();
const urlReplacements = Services.urlReplacementsForDoc(headNode);

/** @private {VariableSource} global variable source for fallback. */
Expand Down
8 changes: 5 additions & 3 deletions extensions/amp-a4a/0.1/amp-a4a.js
Original file line number Diff line number Diff line change
Expand Up @@ -1502,11 +1502,13 @@ export class AmpA4A extends AMP.BaseElement {
extensionIds: creativeMetaData.customElementExtensions || [],
fonts: fontsArray,
},
embedWin => {
(embedWin, ampdoc) => {
const parentAmpdoc = this.getAmpDoc();
installUrlReplacementsForEmbed(
this.getAmpDoc(),
// TODO(#22733): Cleanup `parentAmpdoc` once ampdoc-fie is launched.
ampdoc || parentAmpdoc,
embedWin,
new A4AVariableSource(this.getAmpDoc(), embedWin)
new A4AVariableSource(parentAmpdoc, embedWin)
);
}
).then(friendlyIframeEmbed => {
Expand Down
8 changes: 5 additions & 3 deletions extensions/amp-a4a/0.1/friendly-frame-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,13 @@ export function renderCreativeIntoFriendlyFrame(
extensionIds: creativeMetadata.customElementExtensions || [],
fonts: fontsArray,
},
embedWin => {
(embedWin, ampdoc) => {
const parentAmpdoc = element.getAmpDoc();
installUrlReplacementsForEmbed(
element.getAmpDoc(),
// TODO(#22733): Cleanup `parentAmpdoc` once ampdoc-fie is launched.
ampdoc || parentAmpdoc,
embedWin,
new A4AVariableSource(element.getAmpDoc(), embedWin)
new A4AVariableSource(parentAmpdoc, embedWin)
);
}
).then(friendlyIframeEmbed => {
Expand Down
40 changes: 39 additions & 1 deletion extensions/amp-a4a/0.1/test/test-amp-a4a.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,14 @@ import {
incrementLoadingAds,
is3pThrottled,
} from '../../../amp-ad/0.1/concurrent-load';
import {installDocService} from '../../../../src/service/ampdoc-impl';
import {
installDocService,
updateFieModeForTesting,
} from '../../../../src/service/ampdoc-impl';
import {layoutRectLtwh} from '../../../../src/layout-rect';
import {resetScheduledElementForTesting} from '../../../../src/service/custom-element-registry';
import {data as testFragments} from './testdata/test_fragments';
import {toggleExperiment} from '../../../../src/experiments';
import {data as validCSSAmp} from './testdata/valid_css_at_rules_amp.reserialized';

describe('amp-a4a', () => {
Expand Down Expand Up @@ -2173,6 +2177,37 @@ describe('amp-a4a', () => {
});
});
it('should render correctly', () => {
// TODO(#22733): remove this test once ampdoc-fie is launched.
const parentWin = a4aElement.ownerDocument.defaultView;
return a4a.renderAmpCreative_(metaData).then(() => {
// Verify iframe presence.
expect(a4aElement.children.length).to.equal(1);
const friendlyIframe = a4aElement.children[0];
expect(friendlyIframe.tagName).to.equal('IFRAME');
expect(friendlyIframe.src).to.not.be.ok;
expect(friendlyIframe.srcdoc).to.be.ok;
const frameDoc = friendlyIframe.contentDocument;
const styles = frameDoc.querySelectorAll('style[amp-custom]');
expect(
Array.prototype.some.call(styles, s => {
return s.innerHTML == 'p { background: green }';
}),
'Some style is "background: green"'
).to.be.true;
expect(frameDoc.body.innerHTML.trim()).to.equal('<p>some text</p>');
expect(
Services.urlReplacementsForDoc(frameDoc.documentElement)
).to.not.equal(Services.urlReplacementsForDoc(a4aElement));
expect(
Services.urlReplacementsForDoc(frameDoc.documentElement).ampdoc.win
).to.equal(parentWin);
});
});
it('should render correctly in ampdoc-fie mode', () => {
const parentWin = a4aElement.ownerDocument.defaultView;
const ampdocService = Services.ampdocServiceFor(parentWin);
toggleExperiment(parentWin, 'ampdoc-fie', true);
updateFieModeForTesting(ampdocService, true);
return a4a.renderAmpCreative_(metaData).then(() => {
// Verify iframe presence.
expect(a4aElement.children.length).to.equal(1);
Expand All @@ -2192,6 +2227,9 @@ describe('amp-a4a', () => {
expect(
Services.urlReplacementsForDoc(frameDoc.documentElement)
).to.not.equal(Services.urlReplacementsForDoc(a4aElement));
expect(
Services.urlReplacementsForDoc(frameDoc.documentElement).ampdoc.win
).to.equal(frameDoc.defaultView);
});
});
});
Expand Down
2 changes: 2 additions & 0 deletions extensions/amp-bind/0.1/bind-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ export class Bind {
* @param {!Window=} opt_win
*/
constructor(ampdoc, opt_win) {
// TODO(#22733): remove opt_win subroooting once ampdoc-fie is launched.

/** @const {!../../../src/service/ampdoc-impl.AmpDoc} */
this.ampdoc = ampdoc;

Expand Down
2 changes: 2 additions & 0 deletions extensions/amp-gwd-animation/0.1/amp-gwd-animation-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ export class AmpGwdRuntimeService {
* the service.
*/
constructor(ampdoc, opt_win) {
// TODO(#22733): remove opt_win subroooting once ampdoc-fie is launched.

/** @const @protected {!../../../src/service/ampdoc-impl.AmpDoc} */
this.ampdoc_ = ampdoc;

Expand Down
35 changes: 33 additions & 2 deletions src/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import {Deferred} from './utils/promise';
import {dev, devAssert} from './log';
import {isExperimentOn} from './experiments';
import {toWin} from './types';

/**
Expand Down Expand Up @@ -78,11 +79,14 @@ export class EmbeddableService {
* @return {?Object}
*/
export function getExistingServiceForDocInEmbedScope(element, id) {
// TODO(#22733): completely remove this method once ampdoc-fie launches.
const document = element.ownerDocument;
const win = toWin(document.defaultView);
const topWin = getTopWindow(win);
// First, try to resolve via local embed window (if applicable).
const isEmbed = win != getTopWindow(win);
if (isEmbed) {
const isEmbed = win != topWin;
const ampdocFieExperimentOn = isExperimentOn(topWin, 'ampdoc-fie');
if (isEmbed && !ampdocFieExperimentOn) {
if (isServiceRegistered(win, id)) {
return getServiceInternal(win, id);
}
Expand Down Expand Up @@ -114,6 +118,16 @@ export function installServiceInEmbedScope(embedWin, id, service) {
);
registerServiceInternal(embedWin, embedWin, id, () => service);
getServiceInternal(embedWin, id); // Force service to build.
const ampdocFieExperimentOn = isExperimentOn(topWin, 'ampdoc-fie');
if (ampdocFieExperimentOn) {
const ampdoc = getAmpdoc(embedWin.document);
registerServiceInternal(
getAmpdocServiceHolder(ampdoc),
ampdoc,
id,
() => service
);
}
}

/**
Expand Down Expand Up @@ -600,6 +614,23 @@ export function installServiceInEmbedIfEmbeddable(embedWin, serviceClass) {
return true;
}

/**
* @param {!./service/ampdoc-impl.AmpDoc} ampdoc
* @param {string} id
*/
export function adoptServiceForEmbedDoc(ampdoc, id) {
const service = getServiceInternal(
getAmpdocServiceHolder(devAssert(ampdoc.getParent())),
id
);
registerServiceInternal(
getAmpdocServiceHolder(ampdoc),
ampdoc,
id,
() => service
);
}

/**
* Resets a single service, so it gets recreated on next getService invocation.
* @param {!Object} holder
Expand Down
2 changes: 2 additions & 0 deletions src/service/action-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ export class ActionService {
* @param {(!Document|!ShadowRoot)=} opt_root
*/
constructor(ampdoc, opt_root) {
// TODO(#22733): remove subroooting once ampdoc-fie is launched.

/** @const {!./ampdoc-impl.AmpDoc} */
this.ampdoc = ampdoc;

Expand Down
16 changes: 12 additions & 4 deletions src/service/ampdoc-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class AmpDocService {
win.document[AMPDOC_PROP] = this.singleDoc_;
}

/** @private @const */
/** @private {boolean} */
this.ampdocFieExperimentOn_ = isExperimentOn(win, 'ampdoc-fie');
}

Expand Down Expand Up @@ -215,9 +215,7 @@ export class AmpDocService {
installFieDoc(url, childWin) {
const doc = childWin.document;
devAssert(!doc[AMPDOC_PROP], 'The fie already contains ampdoc');
const frameElement = /** @type {!Node} */ (devAssert(
getParentWindowFrameElement(doc, this.win)
));
const frameElement = devAssert(childWin.frameElement);
const ampdoc = new AmpDocFie(childWin, url, this.getAmpDoc(frameElement));
doc[AMPDOC_PROP] = ampdoc;
return ampdoc;
Expand Down Expand Up @@ -689,3 +687,13 @@ export function installDocService(win, isSingleDoc) {
return new AmpDocService(win, isSingleDoc);
});
}

/**
* @param {AmpDocService} ampdocService
* @param {boolean} value
* @visibleForTesting
*/
export function updateFieModeForTesting(ampdocService, value) {
// TODO(#22733): remove this method once ampdoc-fie is launched.
ampdocService.ampdocFieExperimentOn_ = value;
}
35 changes: 27 additions & 8 deletions src/service/core-services.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import {adoptServiceForEmbedDoc} from '../service';
import {installActionServiceForDoc} from './action-impl';
import {installBatchedXhrService} from './batched-xhr-impl';
import {installCidService} from './cid-impl';
Expand Down Expand Up @@ -77,24 +78,42 @@ export function installRuntimeServices(global) {
* @restricted
*/
export function installAmpdocServices(ampdoc, opt_initParams, opt_inabox) {
const isEmbedded = !!ampdoc.getParent();

// Order is important!
installUrlForDoc(ampdoc);
installDocumentInfoServiceForDoc(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'documentInfo')
: installDocumentInfoServiceForDoc(ampdoc);
if (!opt_inabox) {
// those services are installed in amp-inabox.js
installCidService(ampdoc);
installViewerServiceForDoc(ampdoc, opt_initParams);
installViewportServiceForDoc(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'cid')
: installCidService(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'viewer')
: installViewerServiceForDoc(ampdoc, opt_initParams);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'viewport')
: installViewportServiceForDoc(ampdoc);
}
installHiddenObserverForDoc(ampdoc);
installHistoryServiceForDoc(ampdoc);
installResourcesServiceForDoc(ampdoc);
installUrlReplacementsServiceForDoc(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'history')
: installHistoryServiceForDoc(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'resources')
: installResourcesServiceForDoc(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'url-replace')
: installUrlReplacementsServiceForDoc(ampdoc);
installActionServiceForDoc(ampdoc);
installStandardActionsForDoc(ampdoc);
if (!opt_inabox) {
// For security, Storage is not supported in inabox.
installStorageServiceForDoc(ampdoc);
isEmbedded
? adoptServiceForEmbedDoc(ampdoc, 'storage')
: installStorageServiceForDoc(ampdoc);
}
installGlobalNavigationHandlerForDoc(ampdoc);
installGlobalSubmitListenerForDoc(ampdoc);
Expand Down
51 changes: 50 additions & 1 deletion src/service/extensions-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
setParentWindow,
} from '../service';
import {getMode} from '../mode';
import {installAmpdocServices} from './core-services';
import {install as installCustomElements} from '../polyfills/custom-elements';
import {install as installDOMTokenListToggle} from '../polyfills/domtokenlist-toggle';
import {install as installDocContains} from '../polyfills/document-contains';
Expand Down Expand Up @@ -440,10 +441,47 @@ export class Extensions {
* @restricted
*/
installExtensionsInFie(ampdoc, extensionIds, opt_preinstallCallback) {
// TODO(#22734): Implement.
const childWin = ampdoc.win;
const topWin = this.win;
const parentWin = toWin(childWin.frameElement.ownerDocument.defaultView);
setParentWindow(childWin, parentWin);

// Install necessary polyfills.
installPolyfillsInChildWindow(parentWin, childWin);

// Install runtime styles.
installStylesForDoc(
ampdoc,
isExperimentOn(this.win, 'fie-css-cleanup')
? ampSharedCss
: ampDocCss + ampSharedCss,
/* callback */ null,
/* opt_isRuntimeCss */ true,
/* opt_ext */ 'amp-runtime'
);

// Run pre-install callback.
if (opt_preinstallCallback) {
opt_preinstallCallback(ampdoc.win, ampdoc);
}

// Install embeddable standard services.
installStandardServicesInEmbeddedDoc(ampdoc);

// Install built-ins and legacy elements.
copyBuiltinElementsToChildWindow(topWin, childWin);
stubLegacyElements(childWin);

return Promise.all(
extensionIds.map(extensionId => {
// This will extend automatic upgrade of custom elements from top
// window to the child window.
if (!LEGACY_ELEMENTS.includes(extensionId)) {
stubElementIfNotKnown(childWin, extensionId);
}
return this.installExtensionInDoc_(ampdoc, extensionId);
})
);
}

/**
Expand Down Expand Up @@ -735,6 +773,7 @@ function installPolyfillsInChildWindow(parentWin, childWin) {
* @visibleForTesting
*/
export function installStandardServicesInEmbed(childWin) {
// TODO(#22733): remove when ampdoc-fie is launched.
const frameElement = dev().assertElement(
childWin.frameElement,
'frameElement not found for embed'
Expand All @@ -754,6 +793,16 @@ export function installStandardServicesInEmbed(childWin) {
installTimerInEmbedWindow(childWin);
}

/**
* Adopt predefined core services for the embedded ampdoc (friendly iframe).
* @param {!./ampdoc-impl.AmpDoc} ampdoc
* @visibleForTesting
*/
export function installStandardServicesInEmbeddedDoc(ampdoc) {
installAmpdocServices(ampdoc);
installTimerInEmbedWindow(ampdoc.win);
}

/**
* @return {!Object}
*/
Expand Down
2 changes: 2 additions & 0 deletions src/service/hidden-observer-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ export class HiddenObserver {
* @param {(!Document|!ShadowRoot)=} opt_root
*/
constructor(ampdoc, opt_root) {
// TODO(#22733): remove subroooting once ampdoc-fie is launched.

/** @const {!Document|!ShadowRoot} */
this.root_ = opt_root || ampdoc.getRootNode();
const doc = this.root_.ownerDocument || this.root_;
Expand Down
Loading

0 comments on commit 0f91887

Please sign in to comment.