blob: 167ef8e21243ebb2d335c22274b046976f856c1a [file] [log] [blame]
// Copyright 2021 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Pass the query string params from the trusted URL.
const mainUrl = 'chrome-untrusted://eche-app/untrusted_index.html';
// The window.location.hash starts with # and window.location.search starts
// with ?, uses substring(1) to remove them.
let urlParams = window.location.hash ?
new URLSearchParams(window.location.hash.substring(1)) :
new URLSearchParams(window.location.search.substring(1));
urlParams = urlParams.toString();
let iframeUrl = mainUrl;
if (urlParams) {
iframeUrl = mainUrl + '?' + urlParams;
}
document.getElementsByTagName('iframe')[0].src = iframeUrl;
// Returns a remote for SignalingMessageExchanger interface which sends messages
// to the browser.
const signalMessageExchanger =
ash.echeApp.mojom.SignalingMessageExchanger.getRemote();
// An object which receives request messages for the SignalingMessageObserver
// mojom interface and dispatches them as callbacks.
const signalingMessageObserverRouter =
new ash.echeApp.mojom.SignalingMessageObserverCallbackRouter();
// Set up a message pipe to talk to the browser process.
signalMessageExchanger.setSignalingMessageObserver(
signalingMessageObserverRouter.$.bindNewPipeAndPassRemote());
// Returns a remote for SystemInfoProvider interface which gets system info
// from the browser.
const systemInfo = ash.echeApp.mojom.SystemInfoProvider.getRemote();
// Returns a remote for UidGenerator interface which gets an uid from the
// browser.
const uidGenerator = ash.echeApp.mojom.UidGenerator.getRemote();
// An object which receives request messages for the SystemInfoObserver
// mojom interface and dispatches them as callbacks.
const systemInfoObserverRouter =
new ash.echeApp.mojom.SystemInfoObserverCallbackRouter();
// Set up a message pipe to the browser process to monitor screen state.
systemInfo.setSystemInfoObserver(
systemInfoObserverRouter.$.bindNewPipeAndPassRemote());
const notificationGenerator =
ash.echeApp.mojom.NotificationGenerator.getRemote();
const displayStreamHandler = ash.echeApp.mojom.DisplayStreamHandler.getRemote();
const streamActionObserverRouter =
new ash.echeApp.mojom.StreamActionObserverCallbackRouter();
// Set up a message pipe to the browser process to monitor stream action.
displayStreamHandler.setStreamActionObserver(
streamActionObserverRouter.$.bindNewPipeAndPassRemote());
/**
* A pipe through which we can send messages to the guest frame.
* Use an undefined `target` to find the <iframe> automatically.
* Do not rethrow errors, since handlers installed here are expected to
* throw exceptions that are handled on the other side of the pipe. And
* nothing `awaits` async callHandlerForMessageType_(), so they will always
* be reported as `unhandledrejection` and trigger a crash report.
*/
const guestMessagePipe =
new MessagePipe('chrome-untrusted://eche-app',
/*target=*/ undefined,
/*rethrow_errors=*/ false);
// Register bi-directional SEND_SIGNAL pipes.
guestMessagePipe.registerHandler(Message.SEND_SIGNAL, async (signal) => {
console.log('echeapi browser_proxy.js sendSignalingMessage');
signalMessageExchanger.sendSignalingMessage(signal);
});
signalingMessageObserverRouter.onReceivedSignalingMessage.addListener(
(signal) => {
console.log('echeapi browser_proxy.js onReceivedSignalingMessage');
guestMessagePipe.sendMessage(
Message.SEND_SIGNAL, {/** @type {Uint8Array} */ signal});
});
// Register TEAR_DOWN_SIGNAL pipes.
guestMessagePipe.registerHandler(Message.TEAR_DOWN_SIGNAL, async () => {
console.log('echeapi browser_proxy.js tearDownSignaling');
signalMessageExchanger.tearDownSignaling();
});
// window.close() doesn't work from the iframe.
guestMessagePipe.registerHandler(Message.CLOSE_WINDOW, async () => {
const info = /** @type {!SystemInfo} */ (await systemInfo.getSystemInfo());
const systemInfoJson = JSON.parse(JSON.stringify(info));
console.log('echeapi browser_proxy.js window.close');
displayStreamHandler.onStreamStatusChanged(
ash.echeApp.mojom.StreamStatus.kStreamStatusStopped);
});
// Register GET_SYSTEM_INFO pipes for wrapping getSystemInfo async api call.
guestMessagePipe.registerHandler(Message.GET_SYSTEM_INFO, async () => {
console.log('echeapi browser_proxy.js getSystemInfo');
return /** @type {!SystemInfo} */ (await systemInfo.getSystemInfo());
});
// Register GET_UID pipes for wrapping getUid async api call.
guestMessagePipe.registerHandler(Message.GET_UID, async () => {
console.log('echeapi browser_proxy.js getUid');
return /** @type {!UidInfo} */ (await uidGenerator.getUid());
});
// Add Screen Backlight state listener and send state via pipes.
systemInfoObserverRouter.onScreenBacklightStateChanged.addListener((state) => {
console.log('echeapi browser_proxy.js onScreenBacklightStateChanged');
guestMessagePipe.sendMessage(
Message.SCREEN_BACKLIGHT_STATE, {/** @type {number} */ state});
});
// Add tablet mode listener and send result via pipes.
systemInfoObserverRouter.onReceivedTabletModeChanged.addListener(
(isTabletMode) => {
console.log('echeapi browser_proxy.js onReceivedTabletModeChanged');
guestMessagePipe.sendMessage(
Message.TABLET_MODE, {/** @type {boolean} */ isTabletMode});
});
// Add stream action listener and send result via pipes.
streamActionObserverRouter.onStreamAction.addListener((action) => {
console.log(`echeapi browser_proxy.js OnStreamAction ${action}`);
guestMessagePipe.sendMessage(
Message.STREAM_ACTION, {/** @type {number} */ action});
});
guestMessagePipe.registerHandler(
Message.SHOW_NOTIFICATION, async (message) => {
// The C++ layer uses std::u16string, which use 16 bit characters. JS
// strings support either 8 or 16 bit characters, and must be converted
// to an array of 16 bit character codes that match std::u16string.
const titleArray = {
data: Array.from(message.title, c => c.charCodeAt()),
};
const messageArray = {
data: Array.from(message.message, c => c.charCodeAt()),
};
console.log('echeapi browser_proxy.js showNotification');
notificationGenerator.showNotification(
titleArray, messageArray, message.notificationType);
});
guestMessagePipe.registerHandler(Message.SHOW_TOAST, async (message) => {
// The C++ layer uses std::u16string, which use 16 bit characters. JS
// strings support either 8 or 16 bit characters, and must be converted
// to an array of 16 bit character codes that match std::u16string.
const textArray = {data: Array.from(message.text, c => c.charCodeAt())};
console.log('echeapi browser_proxy.js showToast');
notificationGenerator.showToast(textArray);
});
guestMessagePipe.registerHandler(
Message.TIME_HISTOGRAM_MESSAGE, async (message) => {
console.log('echeapi browser_proxy.js recordTime');
const histogramData = /** @type {TimeHistogram} */ (message);
chrome.metricsPrivate.recordTime(
histogramData.histogram, histogramData.value);
});
guestMessagePipe.registerHandler(
Message.ENUM_HISTOGRAM_MESSAGE, async (message) => {
console.log('echeapi browser_proxy.js recordEnumerationValue');
const histogramData = /** @type {EnumHistogram} */ (message);
chrome.metricsPrivate.recordEnumerationValue(
histogramData.histogram, histogramData.value,
histogramData.maxValue);
});
// Register START_STREAMING pipes.
guestMessagePipe.registerHandler(Message.START_STREAMING, async () => {
console.log('echeapi browser_proxy.js startStreaming');
displayStreamHandler.onStreamStatusChanged(
ash.echeApp.mojom.StreamStatus.kStreamStatusStarted);
});
// We can't access hash change event inside iframe so parse the notification
// info from the anchor part of the url when hash is changed and send them to
// untrusted section via message pipes.
function locationHashChanged() {
const urlParams = window.location.hash ?
new URLSearchParams(window.location.hash.substring(1)) :
new URLSearchParams(window.location.search.substring(1));
const notificationId = urlParams.get('notification_id');
const packageName = urlParams.get('package_name');
const timestamp = urlParams.get('timestamp');
const userId = urlParams.get('user_id');
const notificationInfo = /** @type {!NotificationInfo} */ (
{notificationId, packageName, timestamp, userId});
guestMessagePipe.sendMessage(Message.NOTIFICATION_INFO, notificationInfo);
}
window.onhashchange = locationHashChanged;
if ('virtualKeyboard' in navigator) {
navigator['virtualKeyboard'].overlaysContent = true;
navigator['virtualKeyboard'].addEventListener('geometrychange', (event) => {
const {x, y, width, height} = event.target['boundingRect'];
console.log('Virtual keyboard geometry:', x, y, width, height);
const isVirtualKeyboardEnabled = width > 0 && height > 0;
guestMessagePipe.sendMessage(
Message.IS_VIRTUAL_KEYBOARD_ENABLED,
{/** @type {boolean} */ isVirtualKeyboardEnabled});
});
} else {
console.log('virtual keyboard is not supported!');
}