Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions 3p/3p.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,18 @@
});
}

/**
* Preferred alternative to computeInMasterFrame.
* @param {!Window} global
* @param {string} taskId
* @param {function(*)} work
* @param {function(*)} cb
* @return {undefined}
*/
export function computeInCoordinatingFrame(global, taskId, work, cb) {
return computeInMasterFrame(global, taskId, work, cb);

Check warning on line 207 in 3p/3p.js

View check run for this annotation

Codecov / codecov/patch

3p/3p.js#L207

Added line #L207 was not covered by tests
}

/**
* Validates given data. Throws an exception if the data does not
* contains a mandatory field. If called with the optional param
Expand Down
23 changes: 22 additions & 1 deletion 3p/ampcontext-integration.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {dev, user, userAssert} from '#utils/log';

import {computeInMasterFrame} from './3p';
import {computeInCoordinatingFrame, computeInMasterFrame} from './3p';
import {AbstractAmpContext} from './ampcontext';

/**
Expand Down Expand Up @@ -67,6 +67,11 @@
return masterSelection(this.win_, dev().assertString(this.embedType_));
}

/** @return {!Window} */
get coordinator() {
return this.master_();

Check warning on line 72 in 3p/ampcontext-integration.js

View check run for this annotation

Codecov / codecov/patch

3p/ampcontext-integration.js#L72

Added line #L72 was not covered by tests
}

/** @return {boolean} */
get isMaster() {
return this.isMaster_();
Expand All @@ -77,6 +82,11 @@
return this.master == this.win_;
}

/** @return {boolean} */
get isCoordinator() {
return this.isMaster_();

Check warning on line 87 in 3p/ampcontext-integration.js

View check run for this annotation

Codecov / codecov/patch

3p/ampcontext-integration.js#L87

Added line #L87 was not covered by tests
}

/**
* @param {number} width
* @param {number} height
Expand Down Expand Up @@ -133,4 +143,15 @@
computeInMasterFrame(global, taskId, work, cb) {
computeInMasterFrame(global, taskId, work, cb);
}

/**
* Preferred alternative to computeInMasterFrame.
* @param {!Window} global
* @param {string} taskId
* @param {function(*)} work
* @param {function(*)} cb
*/
computeInCoordinatingFrame(global, taskId, work, cb) {
computeInCoordinatingFrame(global, taskId, work, cb);

Check warning on line 155 in 3p/ampcontext-integration.js

View check run for this annotation

Codecov / codecov/patch

3p/ampcontext-integration.js#L155

Added line #L155 was not covered by tests
}
}
8 changes: 6 additions & 2 deletions ads/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,13 @@ documentation with the new behaviors on user's consent choices. You can refer to

#### JS reuse across iframes

To allow ads to bundle HTTP requests across multiple ad units on the same page the object `window.context.master` will contain the window object of the iframe being elected master iframe for the current page. The `window.context.isMaster` property is `true` when the current frame is the master frame.
To allow ads to bundle HTTP requests across multiple ad units on the same page, use `window.context.coordinator` to access the coordinating iframe window object, and `window.context.isCoordinator` to check if the current frame is the coordinating frame.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@powerivq do you know if 3rd party code would need to make changes for this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They will. The best we can do is to offer an alias therefore.


The `computeInMasterFrame` function is designed to make it easy to perform a task only in the master frame and provide the result to all frames. It is also available to custom ad iframes as `window.context.computeInMasterFrame`. See [3p.js](https://github.com/ampproject/amphtml/blob/main/3p/3p.js) for function signature.
_Note: The legacy properties `window.context.master` and `window.context.isMaster` are still available for backward compatibility._

The `computeInCoordinatingFrame` function is designed to make it easy to perform a task only in the coordinating frame and provide the result to all frames. It is available to custom ad iframes as `window.context.computeInCoordinatingFrame`. See [3p.js](https://github.com/ampproject/amphtml/blob/main/3p/3p.js) for the function signature.

_Note: The legacy function `window.context.computeInMasterFrame` is still available for backward compatibility._

#### Preconnect and prefetch

Expand Down
2 changes: 2 additions & 0 deletions build-system/externs/amp.extern.js
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ window.context.sourceUrl;
window.context.experimentToggles;
window.context.master;
window.context.isMaster;
window.context.coordinator;
window.context.isCoordinator;
window.context.ampcontextVersion;
window.context.ampcontextFilepath;
window.context.canary;
Expand Down
55 changes: 54 additions & 1 deletion test/unit/3p/test-ampcontext-integration.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,61 @@
import {masterSelection} from '#3p/ampcontext-integration';
import {
IntegrationAmpContext,
masterSelection,
} from '#3p/ampcontext-integration';

describes.fakeWin('#masterSelect', {}, (env) => {
it('should allow sharing between configured networks', () =>
expect(masterSelection(env.win, 'fake_network').name).to.equal(
'frame_fake_network_master'
));
});

describes.sandboxed('IntegrationAmpContext aliases', {}, (env) => {
let context;

beforeEach(() => {
context = Object.create(IntegrationAmpContext.prototype);
context.master_ = env.sandbox.stub().returns('test-master-window');
context.isMaster_ = env.sandbox.stub().returns(true);
context.computeInMasterFrame = env.sandbox.stub();
});

it('should delegate coordinator to master', () => {
const result = context.coordinator;

expect(context.master_).to.have.been.calledOnce;
expect(result).to.equal('test-master-window');
});

it('should delegate isCoordinator to isMaster', () => {
const result = context.isCoordinator;

expect(context.isMaster_).to.have.been.calledOnce;
expect(result).to.equal(true);
});

it('should execute computeInCoordinatingFrame method', () => {
const masterWindow = {__ampMasterTasks: {}};
const global = {
context: {
master: masterWindow,
isMaster: true,
},
};
const taskId = 'test-task';
let workCalled = false;
const work = (done) => {
workCalled = true;
done('result');
};
let callbackResult;
const cb = (result) => {
callbackResult = result;
};

context.computeInCoordinatingFrame(global, taskId, work, cb);

expect(workCalled).to.be.true;
expect(callbackResult).to.equal('result');
});
});
52 changes: 52 additions & 0 deletions test/unit/test-3p.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
computeInCoordinatingFrame,
computeInMasterFrame,
loadScript,
nextTick,
Expand Down Expand Up @@ -265,6 +266,57 @@ describes.sandboxed('3p', {}, (env) => {
expect(workCalls).to.equal(1);
});

it('should do work only in coordinator (coordinatingFrame alias)', () => {
const taskId = 'exampleId';
const coordinator = {
context: {
isMaster: true,
},
};
coordinator.context.master = coordinator;
const client0 = {
context: {
isMaster: false,
master: coordinator,
},
};
const client1 = {
context: {
isMaster: false,
master: coordinator,
},
};
const client2 = {
context: {
isMaster: false,
master: coordinator,
},
};
let done;
let workCalls = 0;
const work = (d) => {
workCalls++;
done = d;
};
let progress = '';
const frame = (id) => {
return (result) => {
progress += result + id;
};
};
computeInCoordinatingFrame(client0, taskId, work, frame('client0'));
expect(workCalls).to.equal(0);
computeInCoordinatingFrame(coordinator, taskId, work, frame('coordinator'));
expect(workCalls).to.equal(1);
computeInCoordinatingFrame(client1, taskId, work, frame('client1'));
expect(progress).to.equal('');
done(';');
expect(progress).to.equal(';client0;coordinator;client1');
computeInCoordinatingFrame(client2, taskId, work, frame('client2'));
expect(progress).to.equal(';client0;coordinator;client1;client2');
expect(workCalls).to.equal(1);
});

describe('loadScript', () => {
it('should add <script /> with url to the body', () => {
const url = 'http://test.com/example.js';
Expand Down