mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-11-23 02:10:12 +01:00
22f931e6b2
* Prevent automatic rejection of confirmations Confirmations are now only automatically rejected if a user explicitly closes the notification window. If we close the window programmatically because there are no notifications left to show, nothing gets rejected. This partially avoids a race condition where a confirmation gets rejected automatically without the user having seen the confirmation first. This could happen if the confirmation was processed just as the notification window was being closed. It's still possible for a confirmation that the user has never seen to get rejected as a result of the user closing the window. But at least now it's no longer possible for a confirmation to get rejected in this manner after the user resolves the last confirmation in the queue. * Fix bug that prevented automatic closure detection All windows were being detected as explicit window closures, essentially just as they were previously, because this variable was cleared too soon. * Re-open popup when necessary After the window is automatically closed, a confirmation may have been queued up while the window was closing. If so, the popup is now re- opened.
121 lines
3.7 KiB
JavaScript
121 lines
3.7 KiB
JavaScript
import EventEmitter from 'safe-event-emitter';
|
|
import ExtensionPlatform from '../platforms/extension';
|
|
|
|
const NOTIFICATION_HEIGHT = 620;
|
|
const NOTIFICATION_WIDTH = 360;
|
|
|
|
export const NOTIFICATION_MANAGER_EVENTS = {
|
|
POPUP_CLOSED: 'onPopupClosed',
|
|
};
|
|
|
|
export default class NotificationManager extends EventEmitter {
|
|
/**
|
|
* A collection of methods for controlling the showing and hiding of the notification popup.
|
|
*
|
|
* @typedef {Object} NotificationManager
|
|
*
|
|
*/
|
|
|
|
constructor() {
|
|
super();
|
|
this.platform = new ExtensionPlatform();
|
|
this.platform.addOnRemovedListener(this._onWindowClosed.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Mark the notification popup as having been automatically closed.
|
|
*
|
|
* This lets us differentiate between the cases where we close the
|
|
* notification popup v.s. when the user closes the popup window directly.
|
|
*/
|
|
markAsAutomaticallyClosed() {
|
|
this._popupAutomaticallyClosed = true;
|
|
}
|
|
|
|
/**
|
|
* Either brings an existing MetaMask notification window into focus, or creates a new notification window. New
|
|
* notification windows are given a 'popup' type.
|
|
*
|
|
*/
|
|
async showPopup() {
|
|
const popup = await this._getPopup();
|
|
|
|
// Bring focus to chrome popup
|
|
if (popup) {
|
|
// bring focus to existing chrome popup
|
|
await this.platform.focusWindow(popup.id);
|
|
} else {
|
|
let left = 0;
|
|
let top = 0;
|
|
try {
|
|
const lastFocused = await this.platform.getLastFocusedWindow();
|
|
// Position window in top right corner of lastFocused window.
|
|
top = lastFocused.top;
|
|
left = lastFocused.left + (lastFocused.width - NOTIFICATION_WIDTH);
|
|
} catch (_) {
|
|
// The following properties are more than likely 0, due to being
|
|
// opened from the background chrome process for the extension that
|
|
// has no physical dimensions
|
|
const { screenX, screenY, outerWidth } = window;
|
|
top = Math.max(screenY, 0);
|
|
left = Math.max(screenX + (outerWidth - NOTIFICATION_WIDTH), 0);
|
|
}
|
|
|
|
// create new notification popup
|
|
const popupWindow = await this.platform.openWindow({
|
|
url: 'notification.html',
|
|
type: 'popup',
|
|
width: NOTIFICATION_WIDTH,
|
|
height: NOTIFICATION_HEIGHT,
|
|
left,
|
|
top,
|
|
});
|
|
|
|
// Firefox currently ignores left/top for create, but it works for update
|
|
if (popupWindow.left !== left && popupWindow.state !== 'fullscreen') {
|
|
await this.platform.updateWindowPosition(popupWindow.id, left, top);
|
|
}
|
|
this._popupId = popupWindow.id;
|
|
}
|
|
}
|
|
|
|
_onWindowClosed(windowId) {
|
|
if (windowId === this._popupId) {
|
|
this._popupId = undefined;
|
|
this.emit(NOTIFICATION_MANAGER_EVENTS.POPUP_CLOSED, {
|
|
automaticallyClosed: this._popupAutomaticallyClosed,
|
|
});
|
|
this._popupAutomaticallyClosed = undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks all open MetaMask windows, and returns the first one it finds that is a notification window (i.e. has the
|
|
* type 'popup')
|
|
*
|
|
* @private
|
|
* @param {Function} cb - A node style callback that to which the found notification window will be passed.
|
|
*
|
|
*/
|
|
async _getPopup() {
|
|
const windows = await this.platform.getAllWindows();
|
|
return this._getPopupIn(windows);
|
|
}
|
|
|
|
/**
|
|
* Given an array of windows, returns the 'popup' that has been opened by MetaMask, or null if no such window exists.
|
|
*
|
|
* @private
|
|
* @param {Array} windows - An array of objects containing data about the open MetaMask extension windows.
|
|
*
|
|
*/
|
|
_getPopupIn(windows) {
|
|
return windows
|
|
? windows.find((win) => {
|
|
// Returns notification popup
|
|
return win && win.type === 'popup' && win.id === this._popupId;
|
|
})
|
|
: null;
|
|
}
|
|
}
|