1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
// Copyright 2019 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.
/**
* @fileoverview This file is not auto-generated from cr.js, because it's a
* special case. cr.js holds a lot of functionality that is unnecessary in the
* JS Module world, and auto-generating cr.m.js would require adding a lot of
* logic in js_modulizer.py only to address the cr.js case, which is not worth
* it.
*/
import {assert} from './assert.m.js';
import {PromiseResolver} from './promise_resolver.m.js';
/** @typedef {{eventName: string, uid: number}} */
export let WebUIListener;
/**
* Counter for use with createUid
* @type {number}
*/
let uidCounter = 1;
/**
* @return {number} A new unique ID.
*/
function createUid() {
return uidCounter++;
}
/**
* Dispatches a simple event on an event target.
* @param {!EventTarget} target The event target to dispatch the event on.
* @param {string} type The type of the event.
* @param {boolean=} opt_bubbles Whether the event bubbles or not.
* @param {boolean=} opt_cancelable Whether the default action of the event
* can be prevented. Default is true.
* @return {boolean} If any of the listeners called {@code preventDefault}
* during the dispatch this will return false.
*/
export function dispatchSimpleEvent(target, type, opt_bubbles, opt_cancelable) {
const e = new Event(type, {
bubbles: opt_bubbles,
cancelable: opt_cancelable === undefined || opt_cancelable
});
return target.dispatchEvent(e);
}
/**
* Adds a {@code getInstance} static method that always return the same
* instance object.
* @param {!Function} ctor The constructor for the class to add the static
* method to.
*/
export function addSingletonGetter(ctor) {
ctor.getInstance = function() {
return ctor.instance_ || (ctor.instance_ = new ctor());
};
}
/**
* The mapping used by the sendWithPromise mechanism to tie the Promise
* returned to callers with the corresponding WebUI response. The mapping is
* from ID to the PromiseResolver helper; the ID is generated by
* sendWithPromise and is unique across all invocations of said method.
* @type {!Object<!PromiseResolver>}
*/
const chromeSendResolverMap = {};
/**
* The named method the WebUI handler calls directly in response to a
* chrome.send call that expects a response. The handler requires no knowledge
* of the specific name of this method, as the name is passed to the handler
* as the first argument in the arguments list of chrome.send. The handler
* must pass the ID, also sent via the chrome.send arguments list, as the
* first argument of the JS invocation; additionally, the handler may
* supply any number of other arguments that will be included in the response.
* @param {string} id The unique ID identifying the Promise this response is
* tied to.
* @param {boolean} isSuccess Whether the request was successful.
* @param {*} response The response as sent from C++.
*/
export function webUIResponse(id, isSuccess, response) {
const resolver = chromeSendResolverMap[id];
delete chromeSendResolverMap[id];
if (isSuccess) {
resolver.resolve(response);
} else {
resolver.reject(response);
}
}
/**
* A variation of chrome.send, suitable for messages that expect a single
* response from C++.
* @param {string} methodName The name of the WebUI handler API.
* @param {...*} var_args Variable number of arguments to be forwarded to the
* C++ call.
* @return {!Promise}
*/
export function sendWithPromise(methodName, var_args) {
const args = Array.prototype.slice.call(arguments, 1);
const promiseResolver = new PromiseResolver();
const id = methodName + '_' + createUid();
chromeSendResolverMap[id] = promiseResolver;
chrome.send(methodName, [id].concat(args));
return promiseResolver.promise;
}
/**
* A map of maps associating event names with listeners. The 2nd level map
* associates a listener ID with the callback function, such that individual
* listeners can be removed from an event without affecting other listeners of
* the same event.
* @type {!Object<!Object<!Function>>}
*/
const webUIListenerMap = {};
/**
* The named method the WebUI handler calls directly when an event occurs.
* The WebUI handler must supply the name of the event as the first argument
* of the JS invocation; additionally, the handler may supply any number of
* other arguments that will be forwarded to the listener callbacks.
* @param {string} event The name of the event that has occurred.
* @param {...*} var_args Additional arguments passed from C++.
*/
export function webUIListenerCallback(event, var_args) {
const eventListenersMap = webUIListenerMap[event];
if (!eventListenersMap) {
// C++ event sent for an event that has no listeners.
// TODO(dpapad): Should a warning be displayed here?
return;
}
const args = Array.prototype.slice.call(arguments, 1);
for (const listenerId in eventListenersMap) {
eventListenersMap[listenerId].apply(null, args);
}
}
/**
* Registers a listener for an event fired from WebUI handlers. Any number of
* listeners may register for a single event.
* @param {string} eventName The event to listen to.
* @param {!Function} callback The callback run when the event is fired.
* @return {!WebUIListener} An object to be used for removing a listener via
* cr.removeWebUIListener. Should be treated as read-only.
*/
export function addWebUIListener(eventName, callback) {
webUIListenerMap[eventName] = webUIListenerMap[eventName] || {};
const uid = createUid();
webUIListenerMap[eventName][uid] = callback;
return {eventName: eventName, uid: uid};
}
/**
* Removes a listener. Does nothing if the specified listener is not found.
* @param {!WebUIListener} listener The listener to be removed (as returned by
* addWebUIListener).
* @return {boolean} Whether the given listener was found and actually
* removed.
*/
export function removeWebUIListener(listener) {
const listenerExists = webUIListenerMap[listener.eventName] &&
webUIListenerMap[listener.eventName][listener.uid];
if (listenerExists) {
delete webUIListenerMap[listener.eventName][listener.uid];
return true;
}
return false;
}
// Globally expose functions that must be called from C++.
window.cr = window.cr || {};
assert(!window.cr.webUIResponse);
assert(!window.cr.webUIListenerCallback);
window.cr.webUIResponse = webUIResponse;
window.cr.webUIListenerCallback = webUIListenerCallback;
/** Whether we are using a Mac or not. */
export const isMac = /Mac/.test(navigator.platform);
/** Whether this is on the Windows platform or not. */
export const isWindows = /Win/.test(navigator.platform);
/** Whether this is on chromeOS or not. */
export const isChromeOS = /CrOS/.test(navigator.userAgent);
/** Whether this is on vanilla Linux (not chromeOS). */
export const isLinux = /Linux/.test(navigator.userAgent);
/** Whether this is on Android. */
export const isAndroid = /Android/.test(navigator.userAgent);
/** Whether this is on iOS. */
export const isIOS = /CriOS/.test(navigator.userAgent);
|