blob: cc7524e02c50210877a279819d56ef0c0d35a8b6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/**
* @fileOverview
* The polyfill of BroadcastChannel API.
* This api can be used to achieve inter-instance communications.
*
* https://html.spec.whatwg.org/multipage/comms.html#broadcasting-to-other-browsing-contexts
*/
import { MessageEvent } from './message-event'
const channels = {}
const instances = {}
/**
* An empty constructor for BroadcastChannel polyfill.
* The real constructor will be defined when a Weex instance created because
* we need to track the channel by Weex instance id.
*/
function BroadcastChannel () {}
/**
* Sends the given message to other BroadcastChannel objects set up for this channel.
* @param {any} message
*/
BroadcastChannel.prototype.postMessage = function (message) {
if (this._closed) {
throw new Error(`BroadcastChannel "${this.name}" is closed.`)
}
const subscribers = channels[this.name]
if (subscribers && subscribers.length) {
for (let i = 0; i < subscribers.length; ++i) {
const member = subscribers[i]
if (member._closed || member === this) continue
if (typeof member.onmessage === 'function') {
member.onmessage(new MessageEvent('message', { data: message }))
}
}
}
}
/**
* Closes the BroadcastChannel object, opening it up to garbage collection.
*/
BroadcastChannel.prototype.close = function () {
if (this._closed) {
return
}
this._closed = true
// remove itself from channels.
if (channels[this.name]) {
const subscribers = channels[this.name].filter(x => x !== this)
if (subscribers.length) {
channels[this.name] = subscribers
}
else {
delete channels[this.name]
}
}
}
export default {
create: (id, env, config) => {
instances[id] = []
if (typeof global.BroadcastChannel === 'function') {
return {}
}
const serviceObject = {
/**
* Returns a new BroadcastChannel object via which messages for the given
* channel name can be sent and received.
* @param {string} name
*/
BroadcastChannel: function (name) {
// the name property is readonly
Object.defineProperty(this, 'name', {
configurable: false,
enumerable: true,
writable: false,
value: String(name)
})
this._closed = false
this.onmessage = null
if (!channels[this.name]) {
channels[this.name] = []
}
channels[this.name].push(this)
instances[id].push(this)
}
}
serviceObject.BroadcastChannel.prototype = BroadcastChannel.prototype
return {
instance: serviceObject
}
},
destroy: (id, env) => {
instances[id].forEach(channel => channel.close())
delete instances[id]
}
}