Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog/pr-2331.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix: Grant media permissions to prevent call crashes in Teams - by @IsmaelMartinez (#2331)
18 changes: 18 additions & 0 deletions app/browser/preload.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,24 @@ globalThis.electronAPI = {
sessionType: process.env.XDG_SESSION_TYPE || "x11",
};

// Override navigator.permissions.query() so media permissions always report
// "granted". Electron's setPermissionCheckHandler controls the native layer,
// but the Permissions API in the renderer may still return "prompt" or
// "denied" depending on the Chromium version. Teams' calling component checks
// this during _getInitialState and crashes if the state isn't "granted" (#2221).
if (navigator.permissions?.query) {
const originalPermissionsQuery = navigator.permissions.query.bind(navigator.permissions);
const grantedPermissions = new Set(['camera', 'microphone', 'display-capture', 'notifications']);

navigator.permissions.query = async function(descriptor) {
if (grantedPermissions.has(descriptor?.name)) {
// Return a minimal PermissionStatus-like object with 'granted' state
return { state: 'granted', onchange: null, addEventListener: () => {}, removeEventListener: () => {} };
}
return originalPermissionsQuery(descriptor);
};
}

// Fetch config and override Notification immediately (matching v2.2.1 pattern)
// Config is fetched asynchronously but notification function references it via closure
let notificationConfig = null;
Expand Down
33 changes: 33 additions & 0 deletions app/mainAppWindow/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,39 @@ exports.onAppReady = async function onAppReady(configGroup, customBackground, sh
}
);

// Grant media permission requests (camera, microphone, display-capture) so
// that Teams' calling component can access devices without a prompt dialog.
// Without this, Electron's default behaviour may block or prompt for
// permissions, causing the calling module to fail during initialisation (#2221).
const allowedPermissions = new Set([
'media', 'mediaKeySystem', 'display-capture', 'notifications',
'clipboard-read', 'clipboard-sanitized-write',
]);
window.webContents.session.setPermissionRequestHandler(
(_webContents, permission, callback) => {
callback(allowedPermissions.has(permission));
}
);

// Grant permission checks for trusted Teams origins so that
// navigator.permissions.query() reports "granted" for camera/microphone.
// Without this, Teams' calling module may fail to initialise because
// Electron's default permissionCheck can return an unexpected state (#2221).
const trustedOrigins = new Set([
'https://teams.microsoft.com',
'https://teams.live.com',
'https://teams.cloud.microsoft',
]);
window.webContents.session.setPermissionCheckHandler(
(_webContents, _permission, requestingOrigin) => {
try {
return trustedOrigins.has(new URL(requestingOrigin).origin);
} catch {
return false;
}
}
);

// Initialize connection manager
connectionManager = new ConnectionManager();

Expand Down
Loading