Skip to content

Commit

Permalink
InOb-based load scheduler
Browse files Browse the repository at this point in the history
  • Loading branch information
Dima Voytenko committed Jan 13, 2021
1 parent 1ef4890 commit a33b707
Show file tree
Hide file tree
Showing 9 changed files with 680 additions and 36 deletions.
6 changes: 6 additions & 0 deletions builtins/amp-img.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {Layout, isLayoutSizeDefined} from '../src/layout';
import {Services} from '../src/services';
import {dev} from '../src/log';
import {guaranteeSrcForSrcsetUnsupportedBrowsers} from '../src/utils/img';
import {isExperimentOn} from '../src/experiments';
import {listen} from '../src/event-helper';
import {propagateObjectFitStyles, setImportantStyles} from '../src/style';
import {registerElement} from '../src/service/custom-element-registry';
Expand Down Expand Up @@ -261,6 +262,11 @@ export class AmpImg extends BaseElement {
return false;
}

/** @override */
isLoadableV2() {
return isExperimentOn(this.win, 'amp-img-loader-v2');
}

/** @override */
layoutCallback() {
this.initialize_();
Expand Down
49 changes: 49 additions & 0 deletions examples/loader.amp.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!doctype html>
<html >
<head>
<meta charset="utf-8">
<title>Lorem Ipsum | PublisherName</title>
<link rel="canonical" href="https://medium.com/p/cb7f223fad86" >
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<script async src="https://cdn.ampproject.org/v0.js"></script>

<style amp-custom>
body {
margin: 0;
font-family: 'Georgia', Serif;
background: white;
}

</style>
<link href='https://fonts.googleapis.com/css?family=Georgia|Open+Sans|Roboto' rel='stylesheet' type='text/css'>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
</head>
<body>
<div style="height: 0vh">scroll</div>

<amp-img id="hero-img"
src="img/[email protected]"
srcset="img/[email protected] 1x, img/[email protected] 2x"
layout="responsive" width="360"
height="216">
</amp-img>

<amp-img id="ad-img"
src="img/[email protected]"
layout="responsive" width="360"
height="216"
xpri="2"
noprerender>
</amp-img>

<div style="height: 30vh">scroll</div>

<amp-img id="offscreen-img"
src="img/clock.jpg"
layout="responsive" width="360"
height="216">
</amp-img>

<div style="height: 2000vh">scroll</div>
</body>
</html>
29 changes: 29 additions & 0 deletions src/base-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,11 +415,39 @@ export class BaseElement {
* {@link layoutCallback} calls. Note that this method is not consulted for
* the first layout given that each element must be laid out at least once.
* @return {boolean}
* TODO(#31915): rename to isReloadNeeded
*/
isRelayoutNeeded() {
return false;
}

/**
* Whether this element supports loaderV2 scheduler.
* @return {boolean}
* TODO(#31915): rename to just isLoadable
*/
isLoadableV2() {
return false;
}

/**
* Called when the element should perform loading. At this point the element
* should load/reload resources associated with it. This method is called
* by the runtime and cannot be called manually. Returns promise that will
* complete when loading is considered to be complete.
*
* The first layout call is always called. If the subclass is interested in
* receiving additional callbacks, it has to opt in to do so using
* {@link isRelayoutNeeded} method.
*
* This method is only called if {@link isLoadableV2} returns `true`.
*
* @return {!Promise|undefind}
*/
loadCallback() {
return this.layoutCallback();
}

/**
* Called when the element should perform layout. At this point the element
* should load/reload resources associated with it. This method is called
Expand All @@ -431,6 +459,7 @@ export class BaseElement {
* {@link isRelayoutNeeded} method.
*
* @return {!Promise}
* TODO(#31915): deprecate and remove when loaderV2 is fully launched.
*/
layoutCallback() {
return Promise.resolve();
Expand Down
65 changes: 49 additions & 16 deletions src/custom-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ const UpgradeState = {
UPGRADE_IN_PROGRESS: 4,
};

const EMPTY_FUNC = () => {};

/**
* Caches whether the template tag is supported to avoid memory allocations.
* @type {boolean|undefined}
Expand Down Expand Up @@ -340,6 +342,17 @@ function createBaseCustomElementClass(win) {
return this.upgradeDelayMs_;
}

/**
* Whether this element supports loaderV2 scheduler.
*
* @return {boolean}
* @final
* TODO(#31915): rename to just isLoadable
*/
isLoadableV2() {
return this.implementation_.isLoadableV2();
}

/**
* Completes the upgrade of the element with the provided implementation.
* @param {!./base-element.BaseElement} newImpl
Expand Down Expand Up @@ -396,6 +409,15 @@ function createBaseCustomElementClass(win) {
return this.signals_.whenSignal(CommonSignals.BUILT);
}

/**
* Returns the promise that's rewsolved when the element has been loaded.
* If the loading fails, the resulting promise is rejected.
* @return {!Promise}
*/
whenLoaded() {
return this.signals_.whenSignal(CommonSignals.LOAD_END).then(EMPTY_FUNC);
}

/**
* Get the priority to load the element.
* @return {number}
Expand Down Expand Up @@ -497,6 +519,14 @@ function createBaseCustomElementClass(win) {
));
}

/**
* Forces immediate load of this element.
* @return {!Promise}
*/
load() {
return this.build().then(() => this.getResources().forceLoad(this));
}

/**
* Called to instruct the element to preconnect to hosts it uses during
* layout.
Expand Down Expand Up @@ -534,12 +564,10 @@ function createBaseCustomElementClass(win) {
/**
* Updates the layout box of the element.
* Should only be called by Resources.
* @param {!./layout-rect.LayoutRectDef} layoutBox
* @param {boolean} sizeChanged
*/
updateLayoutBox(layoutBox, sizeChanged = false) {
updateLayoutBox() {
if (this.isBuilt()) {
this.onMeasure(sizeChanged);
this.onMeasure();
}
}

Expand Down Expand Up @@ -877,6 +905,9 @@ function createBaseCustomElementClass(win) {
/** @private */
connected_() {
if (this.built_) {
if (this.isLoadableV2()) {
this.getResources().scheduleLoad(this);
}
this.implementation_.attachedCallback();
}
}
Expand Down Expand Up @@ -1112,11 +1143,8 @@ function createBaseCustomElementClass(win) {
}

this.dispatchCustomEventForTesting(AmpEvents.LOAD_START);
const isLoadEvent = this.layoutCount_ == 0; // First layout is "load".
this.signals_.reset(CommonSignals.UNLOAD);
if (isLoadEvent) {
this.signals_.signal(CommonSignals.LOAD_START);
}
this.signals_.signal(CommonSignals.LOAD_START);

// Potentially start the loading indicator.
this.toggleLoading(true);
Expand All @@ -1130,9 +1158,10 @@ function createBaseCustomElementClass(win) {
if ((!getMode().test || signal) && signal.aborted) {
throw cancellation();
}
if (isLoadEvent) {
this.signals_.signal(CommonSignals.LOAD_END);
if (this.isLoadableV2()) {
this.getResources().unscheduleLoad(this);
}
this.signals_.signal(CommonSignals.LOAD_END);
this.readyState = 'complete';
this.layoutCount_++;
this.toggleLoading(false);
Expand All @@ -1148,13 +1177,14 @@ function createBaseCustomElementClass(win) {
if ((!getMode().test || signal) && signal.aborted) {
throw cancellation();
}
// add layoutCount_ by 1 despite load fails or not
if (isLoadEvent) {
this.signals_.rejectSignal(
CommonSignals.LOAD_END,
/** @type {!Error} */ (reason)
);
if (this.isLoadableV2()) {
this.getResources().unscheduleLoad(this);
}
// add layoutCount_ by 1 despite load fails or not
this.signals_.rejectSignal(
CommonSignals.LOAD_END,
/** @type {!Error} */ (reason)
);
this.layoutCount_++;
this.toggleLoading(false);
throw reason;
Expand Down Expand Up @@ -1226,6 +1256,9 @@ function createBaseCustomElementClass(win) {
const isReLayoutNeeded = this.implementation_.unlayoutCallback();
if (isReLayoutNeeded) {
this.reset_();
if (this.isLoadableV2()) {
this.getResources().scheduleLoad(this);
}
}
this.dispatchCustomEventForTesting(AmpEvents.UNLOAD);
return isReLayoutNeeded;
Expand Down
8 changes: 7 additions & 1 deletion src/preact/base-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,13 @@ export class PreactBaseElement extends AMP.BaseElement {
}

/** @override */
layoutCallback() {
isLoadableV2() {
const Ctor = this.constructor;
return Ctor['loadable'];
}

/** @override */
loadCallback() {
const Ctor = this.constructor;
if (!Ctor['loadable']) {
return super.layoutCallback();
Expand Down
Loading

0 comments on commit a33b707

Please sign in to comment.