341 lines
15 KiB
JavaScript
341 lines
15 KiB
JavaScript
/*TRANSPILED*//*
|
|
|
|
Copyright The Closure Library Authors.
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
'use strict';
|
|
goog.provide("goog.net.xpc.CrossPageChannel");
|
|
goog.require("goog.Uri");
|
|
goog.require("goog.async.Deferred");
|
|
goog.require("goog.async.Delay");
|
|
goog.require("goog.dispose");
|
|
goog.require("goog.dom");
|
|
goog.require("goog.dom.TagName");
|
|
goog.require("goog.dom.safe");
|
|
goog.require("goog.events");
|
|
goog.require("goog.events.EventHandler");
|
|
goog.require("goog.events.EventType");
|
|
goog.require("goog.functions");
|
|
goog.require("goog.html.legacyconversions");
|
|
goog.require("goog.json");
|
|
goog.require("goog.log");
|
|
goog.require("goog.messaging.AbstractChannel");
|
|
goog.require("goog.net.xpc");
|
|
goog.require("goog.net.xpc.CfgFields");
|
|
goog.require("goog.net.xpc.ChannelStates");
|
|
goog.require("goog.net.xpc.CrossPageChannelRole");
|
|
goog.require("goog.net.xpc.NativeMessagingTransport");
|
|
goog.require("goog.net.xpc.TransportTypes");
|
|
goog.require("goog.net.xpc.UriCfgFields");
|
|
goog.require("goog.string");
|
|
goog.require("goog.uri.utils");
|
|
goog.require("goog.userAgent");
|
|
goog.requireType("goog.net.xpc.Transport");
|
|
goog.net.xpc.CrossPageChannel = function(cfg, opt_domHelper) {
|
|
goog.net.xpc.CrossPageChannel.base(this, "constructor");
|
|
for (let i = 0, uriField; uriField = goog.net.xpc.UriCfgFields[i]; i++) {
|
|
if (uriField in cfg && !/^https?:\/\//.test(cfg[uriField])) {
|
|
throw new Error("URI " + cfg[uriField] + " is invalid for field " + uriField);
|
|
}
|
|
}
|
|
this.cfg_ = cfg;
|
|
this.name = this.cfg_[goog.net.xpc.CfgFields.CHANNEL_NAME] || goog.net.xpc.getRandomString(10);
|
|
this.domHelper_ = opt_domHelper || goog.dom.getDomHelper();
|
|
this.deferredDeliveries_ = [];
|
|
this.peerLoadHandler_ = new goog.events.EventHandler(this);
|
|
cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] = cfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] || goog.uri.utils.getHost(this.domHelper_.getWindow().location.href) + "/robots.txt";
|
|
cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] = cfg[goog.net.xpc.CfgFields.PEER_POLL_URI] || goog.uri.utils.getHost(cfg[goog.net.xpc.CfgFields.PEER_URI] || "") + "/robots.txt";
|
|
goog.net.xpc.CrossPageChannel.channels[this.name] = this;
|
|
if (!goog.events.getListener(window, goog.events.EventType.UNLOAD, goog.net.xpc.CrossPageChannel.disposeAll_)) {
|
|
goog.events.listenOnce(window, goog.events.EventType.UNLOAD, goog.net.xpc.CrossPageChannel.disposeAll_);
|
|
}
|
|
goog.log.info(goog.net.xpc.logger, "CrossPageChannel created: " + this.name);
|
|
};
|
|
goog.inherits(goog.net.xpc.CrossPageChannel, goog.messaging.AbstractChannel);
|
|
goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_ESCAPE_RE_ = new RegExp("^%*" + goog.net.xpc.TRANSPORT_SERVICE + "$");
|
|
goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_UNESCAPE_RE_ = new RegExp("^%+" + goog.net.xpc.TRANSPORT_SERVICE + "$");
|
|
goog.net.xpc.CrossPageChannel.prototype.connectionDelay_ = null;
|
|
goog.net.xpc.CrossPageChannel.prototype.peerWindowDeferred_ = null;
|
|
goog.net.xpc.CrossPageChannel.prototype.transport_ = null;
|
|
goog.net.xpc.CrossPageChannel.prototype.state_ = goog.net.xpc.ChannelStates.NOT_CONNECTED;
|
|
goog.net.xpc.CrossPageChannel.prototype.isConnected = function() {
|
|
return this.state_ == goog.net.xpc.ChannelStates.CONNECTED;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.peerWindowObject_ = null;
|
|
goog.net.xpc.CrossPageChannel.prototype.iframeElement_ = null;
|
|
goog.net.xpc.CrossPageChannel.prototype.getConfig = function() {
|
|
return this.cfg_;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getIframeElement = function() {
|
|
return this.iframeElement_;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.setPeerWindowObject = function(peerWindowObject) {
|
|
this.peerWindowObject_ = peerWindowObject;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getPeerWindowObject = function() {
|
|
return this.peerWindowObject_;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.isPeerAvailable = function() {
|
|
try {
|
|
return !!this.peerWindowObject_ && !this.peerWindowObject_.closed;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.determineTransportType_ = function() {
|
|
let transportType;
|
|
if (typeof document.postMessage === "function" || typeof window.postMessage === "function" || goog.userAgent.IE && window.postMessage) {
|
|
transportType = goog.net.xpc.TransportTypes.NATIVE_MESSAGING;
|
|
} else {
|
|
transportType = goog.net.xpc.TransportTypes.UNDEFINED;
|
|
}
|
|
return transportType;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.createTransport_ = function() {
|
|
if (this.transport_) {
|
|
return;
|
|
}
|
|
const CfgFields = goog.net.xpc.CfgFields;
|
|
if (!this.cfg_[CfgFields.TRANSPORT]) {
|
|
this.cfg_[CfgFields.TRANSPORT] = this.determineTransportType_();
|
|
}
|
|
if (typeof this.cfg_[CfgFields.TRANSPORT] === "function") {
|
|
this.transport_ = new this.cfg_[CfgFields.TRANSPORT](this, this.domHelper_);
|
|
} else {
|
|
switch(this.cfg_[CfgFields.TRANSPORT]) {
|
|
case goog.net.xpc.TransportTypes.NATIVE_MESSAGING:
|
|
const protocolVersion = this.cfg_[CfgFields.NATIVE_TRANSPORT_PROTOCOL_VERSION] || 2;
|
|
this.transport_ = new goog.net.xpc.NativeMessagingTransport(this, this.cfg_[CfgFields.PEER_HOSTNAME], this.domHelper_, !!this.cfg_[CfgFields.ONE_SIDED_HANDSHAKE], protocolVersion);
|
|
break;
|
|
}
|
|
}
|
|
if (this.transport_) {
|
|
goog.log.info(goog.net.xpc.logger, "Transport created: " + this.transport_.getName());
|
|
} else {
|
|
throw new Error("CrossPageChannel: No suitable transport found! You may " + "try injecting a Transport constructor directly via the channel " + "config object.");
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getTransportType = function() {
|
|
return this.transport_.getType();
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getTransportName = function() {
|
|
return this.transport_.getName();
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getPeerConfiguration = function() {
|
|
const peerCfg = {};
|
|
peerCfg[goog.net.xpc.CfgFields.CHANNEL_NAME] = this.name;
|
|
peerCfg[goog.net.xpc.CfgFields.TRANSPORT] = this.cfg_[goog.net.xpc.CfgFields.TRANSPORT];
|
|
peerCfg[goog.net.xpc.CfgFields.ONE_SIDED_HANDSHAKE] = this.cfg_[goog.net.xpc.CfgFields.ONE_SIDED_HANDSHAKE];
|
|
if (this.cfg_[goog.net.xpc.CfgFields.LOCAL_RELAY_URI]) {
|
|
peerCfg[goog.net.xpc.CfgFields.PEER_RELAY_URI] = this.cfg_[goog.net.xpc.CfgFields.LOCAL_RELAY_URI];
|
|
}
|
|
if (this.cfg_[goog.net.xpc.CfgFields.LOCAL_POLL_URI]) {
|
|
peerCfg[goog.net.xpc.CfgFields.PEER_POLL_URI] = this.cfg_[goog.net.xpc.CfgFields.LOCAL_POLL_URI];
|
|
}
|
|
if (this.cfg_[goog.net.xpc.CfgFields.PEER_POLL_URI]) {
|
|
peerCfg[goog.net.xpc.CfgFields.LOCAL_POLL_URI] = this.cfg_[goog.net.xpc.CfgFields.PEER_POLL_URI];
|
|
}
|
|
const role = this.cfg_[goog.net.xpc.CfgFields.ROLE];
|
|
if (role) {
|
|
peerCfg[goog.net.xpc.CfgFields.ROLE] = role == goog.net.xpc.CrossPageChannelRole.INNER ? goog.net.xpc.CrossPageChannelRole.OUTER : goog.net.xpc.CrossPageChannelRole.INNER;
|
|
}
|
|
return peerCfg;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.createPeerIframe = function(parentElm, opt_configureIframeCb, opt_addCfgParam) {
|
|
goog.log.info(goog.net.xpc.logger, "createPeerIframe()");
|
|
let iframeId = this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID];
|
|
if (!iframeId) {
|
|
iframeId = this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID] = "xpcpeer" + goog.net.xpc.getRandomString(4);
|
|
}
|
|
const iframeElm = goog.dom.getDomHelper(parentElm).createElement(goog.dom.TagName.IFRAME);
|
|
iframeElm.id = iframeElm.name = iframeId;
|
|
if (opt_configureIframeCb) {
|
|
opt_configureIframeCb(iframeElm);
|
|
} else {
|
|
iframeElm.style.width = iframeElm.style.height = "100%";
|
|
}
|
|
this.cleanUpIncompleteConnection_();
|
|
this.peerWindowDeferred_ = new goog.async.Deferred(undefined, this);
|
|
const peerUri = this.getPeerUri(opt_addCfgParam);
|
|
this.peerLoadHandler_.listenOnceWithScope(iframeElm, "load", this.peerWindowDeferred_.callback, false, this.peerWindowDeferred_);
|
|
if (goog.userAgent.GECKO || goog.userAgent.WEBKIT) {
|
|
window.setTimeout(goog.bind(function() {
|
|
parentElm.appendChild(iframeElm);
|
|
goog.dom.safe.setIframeSrc(iframeElm, goog.html.legacyconversions.trustedResourceUrlFromString(peerUri.toString()));
|
|
goog.log.info(goog.net.xpc.logger, "peer iframe created (" + iframeId + ")");
|
|
}, this), 1);
|
|
} else {
|
|
goog.dom.safe.setIframeSrc(iframeElm, goog.html.legacyconversions.trustedResourceUrlFromString(peerUri.toString()));
|
|
parentElm.appendChild(iframeElm);
|
|
goog.log.info(goog.net.xpc.logger, "peer iframe created (" + iframeId + ")");
|
|
}
|
|
return iframeElm;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.cleanUpIncompleteConnection_ = function() {
|
|
if (this.peerWindowDeferred_) {
|
|
this.peerWindowDeferred_.cancel();
|
|
this.peerWindowDeferred_ = null;
|
|
}
|
|
this.deferredDeliveries_.length = 0;
|
|
this.peerLoadHandler_.removeAll();
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getPeerUri = function(opt_addCfgParam) {
|
|
let peerUri = this.cfg_[goog.net.xpc.CfgFields.PEER_URI];
|
|
if (typeof peerUri === "string") {
|
|
peerUri = this.cfg_[goog.net.xpc.CfgFields.PEER_URI] = new goog.Uri(peerUri);
|
|
}
|
|
if (opt_addCfgParam !== false) {
|
|
peerUri.setParameterValue("xpc", goog.json.serialize(this.getPeerConfiguration()));
|
|
}
|
|
return peerUri;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.connect = function(opt_connectCb) {
|
|
this.connectCb_ = opt_connectCb || goog.functions.UNDEFINED;
|
|
if (this.state_ == goog.net.xpc.ChannelStates.CLOSED) {
|
|
this.state_ = goog.net.xpc.ChannelStates.NOT_CONNECTED;
|
|
}
|
|
if (this.peerWindowDeferred_) {
|
|
this.peerWindowDeferred_.addCallback(this.continueConnection_);
|
|
} else {
|
|
this.continueConnection_();
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.continueConnection_ = function() {
|
|
goog.log.info(goog.net.xpc.logger, "continueConnection_()");
|
|
this.peerWindowDeferred_ = null;
|
|
if (this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID]) {
|
|
this.iframeElement_ = this.domHelper_.getElement(this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID]);
|
|
}
|
|
if (this.iframeElement_) {
|
|
let winObj = this.iframeElement_.contentWindow;
|
|
if (!winObj) {
|
|
winObj = window.frames[this.cfg_[goog.net.xpc.CfgFields.IFRAME_ID]];
|
|
}
|
|
this.setPeerWindowObject(winObj);
|
|
}
|
|
if (!this.peerWindowObject_) {
|
|
if (window == window.top) {
|
|
throw new Error("CrossPageChannel: Can't connect, peer window-object not set.");
|
|
} else {
|
|
this.setPeerWindowObject(window.parent);
|
|
}
|
|
}
|
|
this.createTransport_();
|
|
this.transport_.connect();
|
|
while (this.deferredDeliveries_.length > 0) {
|
|
this.deferredDeliveries_.shift()();
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.close = function() {
|
|
this.cleanUpIncompleteConnection_();
|
|
this.state_ = goog.net.xpc.ChannelStates.CLOSED;
|
|
goog.dispose(this.transport_);
|
|
this.transport_ = null;
|
|
this.connectCb_ = null;
|
|
goog.dispose(this.connectionDelay_);
|
|
this.connectionDelay_ = null;
|
|
goog.log.info(goog.net.xpc.logger, 'Channel "' + this.name + '" closed');
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.notifyConnected = function(opt_delay) {
|
|
if (this.isConnected() || this.connectionDelay_ && this.connectionDelay_.isActive()) {
|
|
return;
|
|
}
|
|
this.state_ = goog.net.xpc.ChannelStates.CONNECTED;
|
|
goog.log.info(goog.net.xpc.logger, 'Channel "' + this.name + '" connected');
|
|
goog.dispose(this.connectionDelay_);
|
|
if (opt_delay !== undefined) {
|
|
this.connectionDelay_ = new goog.async.Delay(this.connectCb_, opt_delay);
|
|
this.connectionDelay_.start();
|
|
} else {
|
|
this.connectionDelay_ = null;
|
|
this.connectCb_();
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.notifyTransportError = function() {
|
|
goog.log.info(goog.net.xpc.logger, "Transport Error");
|
|
this.close();
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.send = function(serviceName, payload) {
|
|
if (!this.isConnected()) {
|
|
goog.log.error(goog.net.xpc.logger, "Can't send. Channel not connected.");
|
|
return;
|
|
}
|
|
if (!this.isPeerAvailable()) {
|
|
goog.log.error(goog.net.xpc.logger, "Peer has disappeared.");
|
|
this.close();
|
|
return;
|
|
}
|
|
if (goog.isObject(payload)) {
|
|
payload = goog.json.serialize(payload);
|
|
}
|
|
this.transport_.send(this.escapeServiceName_(serviceName), payload);
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.xpcDeliver = function(serviceName, payload, opt_origin) {
|
|
if (this.peerWindowDeferred_) {
|
|
this.deferredDeliveries_.push(goog.bind(this.xpcDeliver, this, serviceName, payload, opt_origin));
|
|
return;
|
|
}
|
|
if (!this.isMessageOriginAcceptable(opt_origin)) {
|
|
goog.log.warning(goog.net.xpc.logger, 'Message received from unapproved origin "' + opt_origin + '" - rejected.');
|
|
return;
|
|
}
|
|
if (this.isDisposed() || this.state_ == goog.net.xpc.ChannelStates.CLOSED) {
|
|
goog.log.warning(goog.net.xpc.logger, "CrossPageChannel::xpcDeliver(): Channel closed.");
|
|
} else if (!serviceName || serviceName == goog.net.xpc.TRANSPORT_SERVICE) {
|
|
this.transport_.transportServiceHandler(payload);
|
|
} else {
|
|
if (this.isConnected()) {
|
|
this.deliver(this.unescapeServiceName_(serviceName), payload);
|
|
} else {
|
|
goog.log.info(goog.net.xpc.logger, "CrossPageChannel::xpcDeliver(): Not connected.");
|
|
}
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.escapeServiceName_ = function(name) {
|
|
if (goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_ESCAPE_RE_.test(name)) {
|
|
name = "%" + name;
|
|
}
|
|
return name.replace(/[%:|]/g, encodeURIComponent);
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.unescapeServiceName_ = function(name) {
|
|
name = name.replace(/%[0-9a-f]{2}/gi, decodeURIComponent);
|
|
if (goog.net.xpc.CrossPageChannel.TRANSPORT_SERVICE_UNESCAPE_RE_.test(name)) {
|
|
return name.substring(1);
|
|
} else {
|
|
return name;
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.getRole = function() {
|
|
const role = this.cfg_[goog.net.xpc.CfgFields.ROLE];
|
|
if (typeof role === "number") {
|
|
return role;
|
|
} else {
|
|
return window.parent == this.peerWindowObject_ ? goog.net.xpc.CrossPageChannelRole.INNER : goog.net.xpc.CrossPageChannelRole.OUTER;
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.updateChannelNameAndCatalog = function(name) {
|
|
goog.log.fine(goog.net.xpc.logger, "changing channel name to " + name);
|
|
delete goog.net.xpc.CrossPageChannel.channels[this.name];
|
|
this.name = name;
|
|
goog.net.xpc.CrossPageChannel.channels[name] = this;
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.isMessageOriginAcceptable = function(opt_origin) {
|
|
const peerHostname = this.cfg_[goog.net.xpc.CfgFields.PEER_HOSTNAME];
|
|
return goog.string.isEmptyOrWhitespace(goog.string.makeSafe(opt_origin)) || goog.string.isEmptyOrWhitespace(goog.string.makeSafe(peerHostname)) || opt_origin == this.cfg_[goog.net.xpc.CfgFields.PEER_HOSTNAME];
|
|
};
|
|
goog.net.xpc.CrossPageChannel.prototype.disposeInternal = function() {
|
|
this.close();
|
|
this.peerWindowObject_ = null;
|
|
this.iframeElement_ = null;
|
|
delete goog.net.xpc.CrossPageChannel.channels[this.name];
|
|
goog.dispose(this.peerLoadHandler_);
|
|
delete this.peerLoadHandler_;
|
|
goog.net.xpc.CrossPageChannel.base(this, "disposeInternal");
|
|
};
|
|
goog.net.xpc.CrossPageChannel.disposeAll_ = function() {
|
|
for (let name in goog.net.xpc.CrossPageChannel.channels) {
|
|
goog.dispose(goog.net.xpc.CrossPageChannel.channels[name]);
|
|
}
|
|
};
|
|
goog.net.xpc.CrossPageChannel.channels = {};
|