| <!DOCTYPE html> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script type="module"> |
| import {Counter, CounterObserverReceiver, CounterRemote} from '/gen/content/test/data/mojo_bindings_web_test.test-mojom.m.js'; |
| |
| class ObserverImpl { |
| constructor() { |
| this.count_ = null; |
| this.disconnectResolvers_ = []; |
| } |
| |
| get count() { |
| return this.count_; |
| } |
| |
| async nextCloneDisconnect() { |
| return new Promise(r => this.disconnectResolvers_.push(r)); |
| } |
| |
| onCountChanged(count) { |
| this.count_ = count; |
| } |
| |
| onCloneDisconnected() { |
| let resolvers = []; |
| [resolvers, this.disconnectResolvers_] = |
| [this.disconnectResolvers_, resolvers]; |
| resolvers.forEach(r => r()); |
| } |
| } |
| |
| function getCounterRemote() { |
| const {handle0, handle1} = Mojo.createMessagePipe(); |
| const remote = new CounterRemote(handle1); |
| Mojo.bindInterface(Counter.$interfaceName, handle0, 'process'); |
| return remote; |
| } |
| |
| async function waitForDisconnect(receiver) { |
| return new Promise(r => receiver.onConnectionError.addListener(r)); |
| } |
| |
| promise_test(async () => { |
| const counter = getCounterRemote(); |
| |
| counter.increment(); |
| counter.increment(); |
| const {count} = await counter.increment(); |
| assert_equals(count, 3); |
| }, 'basic validity check for browser-side support of these tests'); |
| |
| promise_test(async () => { |
| const counter = getCounterRemote(); |
| const observerA = new ObserverImpl; |
| const receiverA = new CounterObserverReceiver(observerA); |
| const observerB = new ObserverImpl; |
| const receiverB = new CounterObserverReceiver(observerB); |
| |
| counter.addObserver(receiverA.$.associateAndPassRemote()); |
| counter.addObserver(receiverB.$.associateAndPassRemote()); |
| |
| counter.increment(); |
| const {count} = await counter.increment(); |
| assert_equals(count, 2); |
| |
| // The observers should always observe changes before the caller of increment |
| // gets a reply, so the above await should guarantee that the observers' count |
| // values are up-to-date. |
| assert_equals(observerA.count, 2); |
| assert_equals(observerB.count, 2); |
| }, 'associated remotes can be created and passed'); |
| |
| promise_test(async () => { |
| const counter = getCounterRemote(); |
| const observerA = new ObserverImpl; |
| const receiverA = new CounterObserverReceiver(observerA); |
| const observerB = new ObserverImpl; |
| const receiverB = new CounterObserverReceiver(observerB); |
| |
| receiverA.$.bindHandle((await counter.addNewObserver()).receiver.handle); |
| receiverB.$.bindHandle((await counter.addNewObserver()).receiver.handle); |
| |
| counter.increment(); |
| const {count} = await counter.increment(); |
| assert_equals(count, 2); |
| |
| assert_equals(observerA.count, 2); |
| assert_equals(observerB.count, 2); |
| }, 'associated receivers can be deserialized and receive messages'); |
| |
| promise_test(async () => { |
| const counterA = getCounterRemote(); |
| const counterB = new CounterRemote; |
| const counterC = new CounterRemote; |
| const counterD = new CounterRemote; |
| counterA.clone(counterB.$.associateAndPassReceiver()); |
| counterA.clone(counterC.$.associateAndPassReceiver()); |
| counterB.clone(counterD.$.associateAndPassReceiver()); |
| |
| // Increment operations among the three interfaces should be strictly ordered. |
| const increments = [ |
| counterA.increment(), |
| counterB.increment(), |
| counterC.increment(), |
| counterD.increment(), |
| counterA.increment(), |
| counterB.increment(), |
| counterC.increment(), |
| counterD.increment(), |
| ]; |
| const replies = await Promise.all(increments); |
| const results = replies.map(reply => reply.count); |
| assert_array_equals([1, 2, 3, 4, 5, 6, 7, 8], results); |
| }, 'associated receivers can be created and passed, and message ordering is preserved among endpoints'); |
| |
| promise_test(async () => { |
| const counterA = getCounterRemote(); |
| const {remote: counterB} = await counterA.cloneToNewRemote(); |
| const {remote: counterC} = await counterA.cloneToNewRemote(); |
| const {remote: counterD} = await counterC.cloneToNewRemote(); |
| |
| const increments = [ |
| counterA.increment(), |
| counterB.increment(), |
| counterC.increment(), |
| counterD.increment(), |
| counterA.increment(), |
| counterB.increment(), |
| counterC.increment(), |
| counterD.increment(), |
| ]; |
| const replies = await Promise.all(increments); |
| const results = replies.map(reply => reply.count); |
| assert_array_equals([1, 2, 3, 4, 5, 6, 7, 8], results); |
| }, 'associated remotes can be deserialized and used to send messages, and message ordering is preserved among endpoints'); |
| |
| promise_test(async () => { |
| const counter = getCounterRemote(); |
| const observer = new ObserverImpl; |
| const receiver = new CounterObserverReceiver(observer); |
| counter.addObserver(receiver.$.associateAndPassRemote()); |
| counter.increment(); |
| counter.increment(); |
| counter.increment(); |
| await counter.$.flushForTesting(); |
| assert_equals(observer.count, 3); |
| }, 'associated endpoints can use flushForTesting'); |
| |
| promise_test(async () => { |
| const counter = getCounterRemote(); |
| const {remote: clone} = await counter.cloneToNewRemote(); |
| const observer = new ObserverImpl; |
| const receiver = new CounterObserverReceiver(observer); |
| counter.addObserver(receiver.$.associateAndPassRemote()); |
| clone.$.close(); |
| observer.nextCloneDisconnect(); |
| }, 'closing an associated endpoint from JavaScript will signal its peer'); |
| |
| promise_test(async () => { |
| const counter = getCounterRemote(); |
| const observer = new ObserverImpl; |
| const receiver = new CounterObserverReceiver(observer); |
| counter.addObserver(receiver.$.associateAndPassRemote()); |
| counter.removeAllObservers(); |
| await waitForDisconnect(receiver); |
| }, 'JavaScript associated endpoints are notified when their peers close'); |
| </script> |